blob: 6ebf41f1ad96a4c2762feea368d4018a194df070 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Build resource files from raw assets.
5//
6#include "Main.h"
7#include "AaptAssets.h"
8#include "StringPool.h"
9#include "XMLNode.h"
10#include "ResourceTable.h"
11#include "Images.h"
12
Josiah Gaskin8a39da82011-06-06 17:00:35 -070013#include "CrunchCache.h"
14#include "FileFinder.h"
15#include "CacheUpdater.h"
16
Mathias Agopian1f5762e2013-05-06 20:20:34 -070017#include "WorkQueue.h"
Jeff Brownc0f73662012-03-16 22:17:41 -070018
Andreas Gampe2412f842014-09-30 20:55:57 -070019// STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary.
Raphaelf51125d2011-10-27 17:01:31 -070020#if HAVE_PRINTF_ZD
21# define ZD "%zd"
22# define ZD_TYPE ssize_t
Andreas Gampe2412f842014-09-30 20:55:57 -070023# define STATUST(x) x
Raphaelf51125d2011-10-27 17:01:31 -070024#else
25# define ZD "%ld"
26# define ZD_TYPE long
Andreas Gampe2412f842014-09-30 20:55:57 -070027# define STATUST(x) (status_t)x
Raphaelf51125d2011-10-27 17:01:31 -070028#endif
29
Andreas Gampe2412f842014-09-30 20:55:57 -070030// Set to true for noisy debug output.
31static const bool kIsDebug = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
Jeff Brownc0f73662012-03-16 22:17:41 -070033// Number of threads to use for preprocessing images.
34static const size_t MAX_THREADS = 4;
35
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036// ==========================================================================
37// ==========================================================================
38// ==========================================================================
39
40class PackageInfo
41{
42public:
43 PackageInfo()
44 {
45 }
46 ~PackageInfo()
47 {
48 }
49
50 status_t parsePackage(const sp<AaptGroup>& grp);
51};
52
53// ==========================================================================
54// ==========================================================================
55// ==========================================================================
56
57static String8 parseResourceName(const String8& leaf)
58{
59 const char* firstDot = strchr(leaf.string(), '.');
60 const char* str = leaf.string();
61
62 if (firstDot) {
63 return String8(str, firstDot-str);
64 } else {
65 return String8(str);
66 }
67}
68
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069ResourceTypeSet::ResourceTypeSet()
70 :RefBase(),
71 KeyedVector<String8,sp<AaptGroup> >()
72{
73}
74
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -070075FilePathStore::FilePathStore()
76 :RefBase(),
77 Vector<String8>()
78{
79}
80
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081class ResourceDirIterator
82{
83public:
84 ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
85 : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
86 {
Narayan Kamath788fa412014-01-21 15:32:36 +000087 memset(&mParams, 0, sizeof(ResTable_config));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 }
89
90 inline const sp<AaptGroup>& getGroup() const { return mGroup; }
91 inline const sp<AaptFile>& getFile() const { return mFile; }
92
93 inline const String8& getBaseName() const { return mBaseName; }
94 inline const String8& getLeafName() const { return mLeafName; }
95 inline String8 getPath() const { return mPath; }
96 inline const ResTable_config& getParams() const { return mParams; }
97
98 enum {
99 EOD = 1
100 };
101
102 ssize_t next()
103 {
104 while (true) {
105 sp<AaptGroup> group;
106 sp<AaptFile> file;
107
108 // Try to get next file in this current group.
109 if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) {
110 group = mGroup;
111 file = group->getFiles().valueAt(mGroupPos++);
112
113 // Try to get the next group/file in this directory
114 } else if (mSetPos < mSet->size()) {
115 mGroup = group = mSet->valueAt(mSetPos++);
116 if (group->getFiles().size() < 1) {
117 continue;
118 }
119 file = group->getFiles().valueAt(0);
120 mGroupPos = 1;
121
122 // All done!
123 } else {
124 return EOD;
125 }
126
127 mFile = file;
128
129 String8 leaf(group->getLeaf());
130 mLeafName = String8(leaf);
131 mParams = file->getGroupEntry().toParams();
Andreas Gampe2412f842014-09-30 20:55:57 -0700132 if (kIsDebug) {
133 printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
134 group->getPath().string(), mParams.mcc, mParams.mnc,
135 mParams.language[0] ? mParams.language[0] : '-',
136 mParams.language[1] ? mParams.language[1] : '-',
137 mParams.country[0] ? mParams.country[0] : '-',
138 mParams.country[1] ? mParams.country[1] : '-',
139 mParams.orientation, mParams.uiMode,
140 mParams.density, mParams.touchscreen, mParams.keyboard,
141 mParams.inputFlags, mParams.navigation);
142 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 mPath = "res";
144 mPath.appendPath(file->getGroupEntry().toDirName(mResType));
145 mPath.appendPath(leaf);
146 mBaseName = parseResourceName(leaf);
147 if (mBaseName == "") {
148 fprintf(stderr, "Error: malformed resource filename %s\n",
149 file->getPrintableSource().string());
150 return UNKNOWN_ERROR;
151 }
152
Andreas Gampe2412f842014-09-30 20:55:57 -0700153 if (kIsDebug) {
154 printf("file name=%s\n", mBaseName.string());
155 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156
157 return NO_ERROR;
158 }
159 }
160
161private:
162 String8 mResType;
163
164 const sp<ResourceTypeSet> mSet;
165 size_t mSetPos;
166
167 sp<AaptGroup> mGroup;
168 size_t mGroupPos;
169
170 sp<AaptFile> mFile;
171 String8 mBaseName;
172 String8 mLeafName;
173 String8 mPath;
174 ResTable_config mParams;
175};
176
177// ==========================================================================
178// ==========================================================================
179// ==========================================================================
180
181bool isValidResourceType(const String8& type)
182{
Dianne Hackbornf31161a2011-01-04 21:02:48 -0800183 return type == "anim" || type == "animator" || type == "interpolator"
Chet Haased82c8ac2013-08-26 14:20:16 -0700184 || type == "transition"
Dianne Hackbornf31161a2011-01-04 21:02:48 -0800185 || type == "drawable" || type == "layout"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 || type == "values" || type == "xml" || type == "raw"
Kenny Root7c710232010-11-22 22:28:37 -0800187 || type == "color" || type == "menu" || type == "mipmap";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188}
189
190static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
191{
192 sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
193 sp<AaptFile> file;
194 if (group != NULL) {
195 file = group->getFiles().valueFor(AaptGroupEntry());
196 if (file != NULL) {
197 return file;
198 }
199 }
200
201 if (!makeIfNecessary) {
202 return NULL;
203 }
204 return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
205 NULL, String8());
206}
207
Kenny Rootb5ef7ee2009-12-10 13:52:53 -0800208static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
209 const sp<AaptGroup>& grp)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210{
211 if (grp->getFiles().size() != 1) {
Marco Nelissendd931862009-07-13 13:02:33 -0700212 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 grp->getFiles().valueAt(0)->getPrintableSource().string());
214 }
215
216 sp<AaptFile> file = grp->getFiles().valueAt(0);
217
218 ResXMLTree block;
219 status_t err = parseXMLResource(file, &block);
220 if (err != NO_ERROR) {
221 return err;
222 }
223 //printXMLBlock(&block);
224
225 ResXMLTree::event_code_t code;
226 while ((code=block.next()) != ResXMLTree::START_TAG
227 && code != ResXMLTree::END_DOCUMENT
228 && code != ResXMLTree::BAD_DOCUMENT) {
229 }
230
231 size_t len;
232 if (code != ResXMLTree::START_TAG) {
233 fprintf(stderr, "%s:%d: No start tag found\n",
234 file->getPrintableSource().string(), block.getLineNumber());
235 return UNKNOWN_ERROR;
236 }
237 if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) {
238 fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n",
239 file->getPrintableSource().string(), block.getLineNumber(),
240 String8(block.getElementName(&len)).string());
241 return UNKNOWN_ERROR;
242 }
243
244 ssize_t nameIndex = block.indexOfAttribute(NULL, "package");
245 if (nameIndex < 0) {
246 fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n",
247 file->getPrintableSource().string(), block.getLineNumber());
248 return UNKNOWN_ERROR;
249 }
250
251 assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len)));
252
Kenny Rootb5ef7ee2009-12-10 13:52:53 -0800253 String16 uses_sdk16("uses-sdk");
254 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
255 && code != ResXMLTree::BAD_DOCUMENT) {
256 if (code == ResXMLTree::START_TAG) {
257 if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) {
Kenny Root5a8ec762010-02-24 20:00:03 -0800258 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
Kenny Rootb5ef7ee2009-12-10 13:52:53 -0800259 "minSdkVersion");
260 if (minSdkIndex >= 0) {
Dan Albertf348c152014-09-08 18:28:00 -0700261 const char16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
Kenny Root7ff20e32010-02-24 23:49:59 -0800262 const char* minSdk8 = strdup(String8(minSdk16).string());
Kenny Root1741cd42010-03-18 12:12:11 -0700263 bundle->setManifestMinSdkVersion(minSdk8);
Kenny Rootb5ef7ee2009-12-10 13:52:53 -0800264 }
265 }
266 }
267 }
268
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 return NO_ERROR;
270}
271
272// ==========================================================================
273// ==========================================================================
274// ==========================================================================
275
276static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,
277 ResourceTable* table,
278 const sp<ResourceTypeSet>& set,
279 const char* resType)
280{
281 String8 type8(resType);
282 String16 type16(resType);
283
284 bool hasErrors = false;
285
286 ResourceDirIterator it(set, String8(resType));
287 ssize_t res;
288 while ((res=it.next()) == NO_ERROR) {
289 if (bundle->getVerbose()) {
290 printf(" (new resource id %s from %s)\n",
291 it.getBaseName().string(), it.getFile()->getPrintableSource().string());
292 }
293 String16 baseName(it.getBaseName());
294 const char16_t* str = baseName.string();
295 const char16_t* const end = str + baseName.size();
296 while (str < end) {
297 if (!((*str >= 'a' && *str <= 'z')
298 || (*str >= '0' && *str <= '9')
299 || *str == '_' || *str == '.')) {
300 fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",
301 it.getPath().string());
302 hasErrors = true;
303 }
304 str++;
305 }
306 String8 resPath = it.getPath();
307 resPath.convertToResPath();
308 table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),
309 type16,
310 baseName,
311 String16(resPath),
312 NULL,
313 &it.getParams());
314 assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);
315 }
316
Andreas Gampe2412f842014-09-30 20:55:57 -0700317 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318}
319
Jeff Brownc0f73662012-03-16 22:17:41 -0700320class PreProcessImageWorkUnit : public WorkQueue::WorkUnit {
321public:
322 PreProcessImageWorkUnit(const Bundle* bundle, const sp<AaptAssets>& assets,
323 const sp<AaptFile>& file, volatile bool* hasErrors) :
324 mBundle(bundle), mAssets(assets), mFile(file), mHasErrors(hasErrors) {
325 }
326
327 virtual bool run() {
328 status_t status = preProcessImage(mBundle, mAssets, mFile, NULL);
329 if (status) {
330 *mHasErrors = true;
331 }
332 return true; // continue even if there are errors
333 }
334
335private:
336 const Bundle* mBundle;
337 sp<AaptAssets> mAssets;
338 sp<AaptFile> mFile;
339 volatile bool* mHasErrors;
340};
341
342static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& assets,
Kenny Root7c710232010-11-22 22:28:37 -0800343 const sp<ResourceTypeSet>& set, const char* type)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344{
Jeff Brownc0f73662012-03-16 22:17:41 -0700345 volatile bool hasErrors = false;
Josiah Gaskin8a39da82011-06-06 17:00:35 -0700346 ssize_t res = NO_ERROR;
347 if (bundle->getUseCrunchCache() == false) {
Jeff Brownc0f73662012-03-16 22:17:41 -0700348 WorkQueue wq(MAX_THREADS, false);
Xavier Ducrohet84be06e2011-07-20 17:45:11 -0700349 ResourceDirIterator it(set, String8(type));
Josiah Gaskin8a39da82011-06-06 17:00:35 -0700350 while ((res=it.next()) == NO_ERROR) {
Jeff Brownc0f73662012-03-16 22:17:41 -0700351 PreProcessImageWorkUnit* w = new PreProcessImageWorkUnit(
352 bundle, assets, it.getFile(), &hasErrors);
353 status_t status = wq.schedule(w);
354 if (status) {
355 fprintf(stderr, "preProcessImages failed: schedule() returned %d\n", status);
Josiah Gaskin8a39da82011-06-06 17:00:35 -0700356 hasErrors = true;
Jeff Brownc0f73662012-03-16 22:17:41 -0700357 delete w;
358 break;
Josiah Gaskin8a39da82011-06-06 17:00:35 -0700359 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 }
Jeff Brownc0f73662012-03-16 22:17:41 -0700361 status_t status = wq.finish();
362 if (status) {
363 fprintf(stderr, "preProcessImages failed: finish() returned %d\n", status);
364 hasErrors = true;
365 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 }
Andreas Gampe2412f842014-09-30 20:55:57 -0700367 return (hasErrors || (res < NO_ERROR)) ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368}
369
370status_t postProcessImages(const sp<AaptAssets>& assets,
371 ResourceTable* table,
372 const sp<ResourceTypeSet>& set)
373{
374 ResourceDirIterator it(set, String8("drawable"));
Daniel Sandler3547f852009-08-14 13:47:30 -0700375 bool hasErrors = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 ssize_t res;
377 while ((res=it.next()) == NO_ERROR) {
378 res = postProcessImage(assets, table, it.getFile());
Daniel Sandler3547f852009-08-14 13:47:30 -0700379 if (res < NO_ERROR) {
380 hasErrors = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 }
382 }
383
Andreas Gampe2412f842014-09-30 20:55:57 -0700384 return (hasErrors || (res < NO_ERROR)) ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385}
386
387static void collect_files(const sp<AaptDir>& dir,
388 KeyedVector<String8, sp<ResourceTypeSet> >* resources)
389{
390 const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles();
391 int N = groups.size();
392 for (int i=0; i<N; i++) {
393 String8 leafName = groups.keyAt(i);
394 const sp<AaptGroup>& group = groups.valueAt(i);
395
396 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files
397 = group->getFiles();
398
399 if (files.size() == 0) {
400 continue;
401 }
402
403 String8 resType = files.valueAt(0)->getResourceType();
404
405 ssize_t index = resources->indexOfKey(resType);
406
407 if (index < 0) {
408 sp<ResourceTypeSet> set = new ResourceTypeSet();
Andreas Gampe2412f842014-09-30 20:55:57 -0700409 if (kIsDebug) {
410 printf("Creating new resource type set for leaf %s with group %s (%p)\n",
411 leafName.string(), group->getPath().string(), group.get());
412 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 set->add(leafName, group);
414 resources->add(resType, set);
415 } else {
416 sp<ResourceTypeSet> set = resources->valueAt(index);
417 index = set->indexOfKey(leafName);
418 if (index < 0) {
Andreas Gampe2412f842014-09-30 20:55:57 -0700419 if (kIsDebug) {
420 printf("Adding to resource type set for leaf %s group %s (%p)\n",
421 leafName.string(), group->getPath().string(), group.get());
422 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 set->add(leafName, group);
424 } else {
425 sp<AaptGroup> existingGroup = set->valueAt(index);
Andreas Gampe2412f842014-09-30 20:55:57 -0700426 if (kIsDebug) {
427 printf("Extending to resource type set for leaf %s group %s (%p)\n",
428 leafName.string(), group->getPath().string(), group.get());
429 }
Dianne Hackborne6b68032011-10-13 16:26:02 -0700430 for (size_t j=0; j<files.size(); j++) {
Andreas Gampe2412f842014-09-30 20:55:57 -0700431 if (kIsDebug) {
432 printf("Adding file %s in group %s resType %s\n",
433 files.valueAt(j)->getSourceFile().string(),
434 files.keyAt(j).toDirName(String8()).string(),
435 resType.string());
436 }
437 existingGroup->addFile(files.valueAt(j));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 }
439 }
440 }
441 }
442}
443
444static void collect_files(const sp<AaptAssets>& ass,
445 KeyedVector<String8, sp<ResourceTypeSet> >* resources)
446{
447 const Vector<sp<AaptDir> >& dirs = ass->resDirs();
448 int N = dirs.size();
449
450 for (int i=0; i<N; i++) {
451 sp<AaptDir> d = dirs.itemAt(i);
Andreas Gampe2412f842014-09-30 20:55:57 -0700452 if (kIsDebug) {
453 printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
454 d->getLeaf().string());
455 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 collect_files(d, resources);
457
458 // don't try to include the res dir
Andreas Gampe2412f842014-09-30 20:55:57 -0700459 if (kIsDebug) {
460 printf("Removing dir leaf %s\n", d->getLeaf().string());
461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 ass->removeDir(d->getLeaf());
463 }
464}
465
466enum {
467 ATTR_OKAY = -1,
468 ATTR_NOT_FOUND = -2,
469 ATTR_LEADING_SPACES = -3,
470 ATTR_TRAILING_SPACES = -4
471};
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800472static int validateAttr(const String8& path, const ResTable& table,
473 const ResXMLParser& parser,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 const char* ns, const char* attr, const char* validChars, bool required)
475{
476 size_t len;
477
478 ssize_t index = parser.indexOfAttribute(ns, attr);
Dan Albertf348c152014-09-08 18:28:00 -0700479 const char16_t* str;
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800480 Res_value value;
481 if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) {
482 const ResStringPool* pool = &parser.getStrings();
483 if (value.dataType == Res_value::TYPE_REFERENCE) {
484 uint32_t specFlags = 0;
485 int strIdx;
486 if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) {
487 fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n",
488 path.string(), parser.getLineNumber(),
489 String8(parser.getElementName(&len)).string(), attr,
490 value.data);
491 return ATTR_NOT_FOUND;
492 }
Elliott Hughesc367d482013-10-29 13:12:55 -0700493
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800494 pool = table.getTableStringBlock(strIdx);
495 #if 0
496 if (pool != NULL) {
497 str = pool->stringAt(value.data, &len);
498 }
499 printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr,
500 specFlags, strIdx, str != NULL ? String8(str).string() : "???");
501 #endif
502 if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) {
503 fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n",
504 path.string(), parser.getLineNumber(),
505 String8(parser.getElementName(&len)).string(), attr,
506 specFlags);
507 return ATTR_NOT_FOUND;
508 }
509 }
510 if (value.dataType == Res_value::TYPE_STRING) {
511 if (pool == NULL) {
512 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n",
513 path.string(), parser.getLineNumber(),
514 String8(parser.getElementName(&len)).string(), attr);
515 return ATTR_NOT_FOUND;
516 }
517 if ((str=pool->stringAt(value.data, &len)) == NULL) {
518 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
519 path.string(), parser.getLineNumber(),
520 String8(parser.getElementName(&len)).string(), attr);
521 return ATTR_NOT_FOUND;
522 }
523 } else {
524 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n",
525 path.string(), parser.getLineNumber(),
526 String8(parser.getElementName(&len)).string(), attr,
527 value.dataType);
528 return ATTR_NOT_FOUND;
529 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 if (validChars) {
531 for (size_t i=0; i<len; i++) {
532 uint16_t c = str[i];
533 const char* p = validChars;
534 bool okay = false;
535 while (*p) {
536 if (c == *p) {
537 okay = true;
538 break;
539 }
540 p++;
541 }
542 if (!okay) {
543 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n",
544 path.string(), parser.getLineNumber(),
545 String8(parser.getElementName(&len)).string(), attr, (char)str[i]);
546 return (int)i;
547 }
548 }
549 }
550 if (*str == ' ') {
551 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n",
552 path.string(), parser.getLineNumber(),
553 String8(parser.getElementName(&len)).string(), attr);
554 return ATTR_LEADING_SPACES;
555 }
556 if (str[len-1] == ' ') {
557 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n",
558 path.string(), parser.getLineNumber(),
559 String8(parser.getElementName(&len)).string(), attr);
560 return ATTR_TRAILING_SPACES;
561 }
562 return ATTR_OKAY;
563 }
564 if (required) {
565 fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n",
566 path.string(), parser.getLineNumber(),
567 String8(parser.getElementName(&len)).string(), attr);
568 return ATTR_NOT_FOUND;
569 }
570 return ATTR_OKAY;
571}
572
573static void checkForIds(const String8& path, ResXMLParser& parser)
574{
575 ResXMLTree::event_code_t code;
576 while ((code=parser.next()) != ResXMLTree::END_DOCUMENT
577 && code > ResXMLTree::BAD_DOCUMENT) {
578 if (code == ResXMLTree::START_TAG) {
579 ssize_t index = parser.indexOfAttribute(NULL, "id");
580 if (index >= 0) {
Marco Nelissendd931862009-07-13 13:02:33 -0700581 fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 path.string(), parser.getLineNumber());
583 }
584 }
585 }
586}
587
Robert Greenwalt832528f2009-08-31 14:48:20 -0700588static bool applyFileOverlay(Bundle *bundle,
589 const sp<AaptAssets>& assets,
Xavier Ducrohet83f4c092010-03-04 15:21:59 -0800590 sp<ResourceTypeSet> *baseSet,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 const char *resType)
592{
Robert Greenwalt832528f2009-08-31 14:48:20 -0700593 if (bundle->getVerbose()) {
594 printf("applyFileOverlay for %s\n", resType);
595 }
596
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 // Replace any base level files in this category with any found from the overlay
598 // Also add any found only in the overlay.
599 sp<AaptAssets> overlay = assets->getOverlay();
600 String8 resTypeString(resType);
Robert Greenwaltfa5c7e12009-06-05 18:53:26 -0700601
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 // work through the linked list of overlays
603 while (overlay.get()) {
604 KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources();
605
606 // get the overlay resources of the requested type
607 ssize_t index = overlayRes->indexOfKey(resTypeString);
608 if (index >= 0) {
609 sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index);
610
611 // for each of the resources, check for a match in the previously built
612 // non-overlay "baseset".
613 size_t overlayCount = overlaySet->size();
614 for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
Robert Greenwalt832528f2009-08-31 14:48:20 -0700615 if (bundle->getVerbose()) {
616 printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
617 }
Andreas Gampe2412f842014-09-30 20:55:57 -0700618 ssize_t baseIndex = UNKNOWN_ERROR;
Xavier Ducrohet83f4c092010-03-04 15:21:59 -0800619 if (baseSet->get() != NULL) {
620 baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
621 }
Andreas Gampe2412f842014-09-30 20:55:57 -0700622 if (baseIndex >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 // look for same flavor. For a given file (strings.xml, for example)
624 // there may be a locale specific or other flavors - we want to match
625 // the same flavor.
626 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
Xavier Ducrohet83f4c092010-03-04 15:21:59 -0800627 sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
Robert Greenwalt832528f2009-08-31 14:48:20 -0700628
629 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 overlayGroup->getFiles();
Robert Greenwalt832528f2009-08-31 14:48:20 -0700631 if (bundle->getVerbose()) {
632 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
633 baseGroup->getFiles();
634 for (size_t i=0; i < baseFiles.size(); i++) {
Raphaelf51125d2011-10-27 17:01:31 -0700635 printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i,
Robert Greenwalt832528f2009-08-31 14:48:20 -0700636 baseFiles.keyAt(i).toString().string());
637 }
638 for (size_t i=0; i < overlayFiles.size(); i++) {
Raphaelf51125d2011-10-27 17:01:31 -0700639 printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i,
Robert Greenwalt832528f2009-08-31 14:48:20 -0700640 overlayFiles.keyAt(i).toString().string());
641 }
642 }
643
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 size_t overlayGroupSize = overlayFiles.size();
Robert Greenwalt832528f2009-08-31 14:48:20 -0700645 for (size_t overlayGroupIndex = 0;
646 overlayGroupIndex<overlayGroupSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 overlayGroupIndex++) {
Andreas Gampe2412f842014-09-30 20:55:57 -0700648 ssize_t baseFileIndex =
Robert Greenwalt832528f2009-08-31 14:48:20 -0700649 baseGroup->getFiles().indexOfKey(overlayFiles.
650 keyAt(overlayGroupIndex));
Andreas Gampe2412f842014-09-30 20:55:57 -0700651 if (baseFileIndex >= 0) {
Robert Greenwalt832528f2009-08-31 14:48:20 -0700652 if (bundle->getVerbose()) {
Raphaelf51125d2011-10-27 17:01:31 -0700653 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
654 (ZD_TYPE) baseFileIndex,
Robert Greenwalt832528f2009-08-31 14:48:20 -0700655 overlayGroup->getLeaf().string(),
656 overlayFiles.keyAt(overlayGroupIndex).toString().string());
657 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 baseGroup->removeFile(baseFileIndex);
659 } else {
660 // didn't find a match fall through and add it..
Dianne Hackborne6b68032011-10-13 16:26:02 -0700661 if (true || bundle->getVerbose()) {
662 printf("nothing matches overlay file %s, for flavor %s\n",
663 overlayGroup->getLeaf().string(),
664 overlayFiles.keyAt(overlayGroupIndex).toString().string());
665 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 }
667 baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
Dianne Hackborn64551b22009-08-15 00:00:33 -0700668 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 }
670 } else {
Xavier Ducrohet83f4c092010-03-04 15:21:59 -0800671 if (baseSet->get() == NULL) {
672 *baseSet = new ResourceTypeSet();
673 assets->getResources()->add(String8(resType), *baseSet);
674 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 // this group doesn't exist (a file that's only in the overlay)
Xavier Ducrohet83f4c092010-03-04 15:21:59 -0800676 (*baseSet)->add(overlaySet->keyAt(overlayIndex),
Dianne Hackborn58c27a02009-08-13 13:36:00 -0700677 overlaySet->valueAt(overlayIndex));
Dianne Hackborn64551b22009-08-15 00:00:33 -0700678 // make sure all flavors are defined in the resources.
679 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
Robert Greenwalt832528f2009-08-31 14:48:20 -0700680 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
Dianne Hackborn64551b22009-08-15 00:00:33 -0700681 overlayGroup->getFiles();
682 size_t overlayGroupSize = overlayFiles.size();
Robert Greenwalt832528f2009-08-31 14:48:20 -0700683 for (size_t overlayGroupIndex = 0;
684 overlayGroupIndex<overlayGroupSize;
Dianne Hackborn64551b22009-08-15 00:00:33 -0700685 overlayGroupIndex++) {
686 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
687 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 }
689 }
690 // this overlay didn't have resources for this type
691 }
692 // try next overlay
693 overlay = overlay->getOverlay();
694 }
Robert Greenwaltfa5c7e12009-06-05 18:53:26 -0700695 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696}
697
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700698/*
699 * Inserts an attribute in a given node, only if the attribute does not
700 * exist.
701 * If errorOnFailedInsert is true, and the attribute already exists, returns false.
702 * Returns true otherwise, even if the attribute already exists.
703 */
704bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
705 const char* attr8, const char* value, bool errorOnFailedInsert)
Dianne Hackborn62da8462009-05-13 15:06:13 -0700706{
707 if (value == NULL) {
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700708 return true;
Dianne Hackborn62da8462009-05-13 15:06:13 -0700709 }
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700710
Dianne Hackborn62da8462009-05-13 15:06:13 -0700711 const String16 ns(ns8);
712 const String16 attr(attr8);
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700713
Dianne Hackborn62da8462009-05-13 15:06:13 -0700714 if (node->getAttribute(ns, attr) != NULL) {
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700715 if (errorOnFailedInsert) {
716 fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);"
717 " cannot insert new value %s.\n",
718 String8(attr).string(), String8(ns).string(), value);
719 return false;
720 }
721
Kenny Rooted983092010-03-18 14:14:49 -0700722 fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
723 " using existing value in manifest.\n",
Dianne Hackborn62da8462009-05-13 15:06:13 -0700724 String8(attr).string(), String8(ns).string());
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700725
726 // don't stop the build.
727 return true;
Dianne Hackborn62da8462009-05-13 15:06:13 -0700728 }
Elliott Hughesc367d482013-10-29 13:12:55 -0700729
Dianne Hackborn62da8462009-05-13 15:06:13 -0700730 node->addAttribute(ns, attr, String16(value));
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700731 return true;
Dianne Hackborn62da8462009-05-13 15:06:13 -0700732}
733
Dianne Hackbornef05e072010-03-01 17:43:39 -0800734static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
735 const String16& attrName) {
Jeff Hamilton2fee0ed2010-01-06 15:46:38 -0600736 XMLNode::attribute_entry* attr = node->editAttribute(
Dianne Hackbornef05e072010-03-01 17:43:39 -0800737 String16("http://schemas.android.com/apk/res/android"), attrName);
Jeff Hamilton2fee0ed2010-01-06 15:46:38 -0600738 if (attr != NULL) {
739 String8 name(attr->string);
740
741 // asdf --> package.asdf
742 // .asdf .a.b --> package.asdf package.a.b
743 // asdf.adsf --> asdf.asdf
744 String8 className;
745 const char* p = name.string();
746 const char* q = strchr(p, '.');
747 if (p == q) {
748 className += package;
749 className += name;
750 } else if (q == NULL) {
751 className += package;
752 className += ".";
753 className += name;
754 } else {
755 className += name;
756 }
Andreas Gampe2412f842014-09-30 20:55:57 -0700757 if (kIsDebug) {
758 printf("Qualifying class '%s' to '%s'", name.string(), className.string());
759 }
Jeff Hamilton2fee0ed2010-01-06 15:46:38 -0600760 attr->string.setTo(String16(className));
761 }
762}
763
Dianne Hackborn62da8462009-05-13 15:06:13 -0700764status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
765{
766 root = root->searchElement(String16(), String16("manifest"));
767 if (root == NULL) {
768 fprintf(stderr, "No <manifest> tag.\n");
769 return UNKNOWN_ERROR;
770 }
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700771
772 bool errorOnFailedInsert = bundle->getErrorOnFailedInsert();
773
774 if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
775 bundle->getVersionCode(), errorOnFailedInsert)) {
776 return UNKNOWN_ERROR;
777 }
778 if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
779 bundle->getVersionName(), errorOnFailedInsert)) {
780 return UNKNOWN_ERROR;
781 }
Elliott Hughesc367d482013-10-29 13:12:55 -0700782
Dianne Hackborn62da8462009-05-13 15:06:13 -0700783 if (bundle->getMinSdkVersion() != NULL
784 || bundle->getTargetSdkVersion() != NULL
785 || bundle->getMaxSdkVersion() != NULL) {
786 sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk"));
787 if (vers == NULL) {
788 vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk"));
789 root->insertChildAt(vers, 0);
790 }
Elliott Hughesc367d482013-10-29 13:12:55 -0700791
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700792 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
793 bundle->getMinSdkVersion(), errorOnFailedInsert)) {
794 return UNKNOWN_ERROR;
795 }
796 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
797 bundle->getTargetSdkVersion(), errorOnFailedInsert)) {
798 return UNKNOWN_ERROR;
799 }
800 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
801 bundle->getMaxSdkVersion(), errorOnFailedInsert)) {
802 return UNKNOWN_ERROR;
803 }
Dianne Hackborn62da8462009-05-13 15:06:13 -0700804 }
Jeff Hamilton2fee0ed2010-01-06 15:46:38 -0600805
Xavier Ducrohet6487b092010-08-31 10:45:31 -0700806 if (bundle->getDebugMode()) {
807 sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
808 if (application != NULL) {
Xavier Ducrohet7714a242012-09-05 17:49:21 -0700809 if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true",
810 errorOnFailedInsert)) {
811 return UNKNOWN_ERROR;
812 }
Xavier Ducrohet6487b092010-08-31 10:45:31 -0700813 }
814 }
815
Jeff Hamilton2fee0ed2010-01-06 15:46:38 -0600816 // Deal with manifest package name overrides
817 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
818 if (manifestPackageNameOverride != NULL) {
819 // Update the actual package name
820 XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package"));
821 if (attr == NULL) {
822 fprintf(stderr, "package name is required with --rename-manifest-package.\n");
823 return UNKNOWN_ERROR;
824 }
825 String8 origPackage(attr->string);
826 attr->string.setTo(String16(manifestPackageNameOverride));
Andreas Gampe2412f842014-09-30 20:55:57 -0700827 if (kIsDebug) {
828 printf("Overriding package '%s' to be '%s'\n", origPackage.string(),
829 manifestPackageNameOverride);
830 }
Jeff Hamilton2fee0ed2010-01-06 15:46:38 -0600831
832 // Make class names fully qualified
833 sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
834 if (application != NULL) {
Dianne Hackbornef05e072010-03-01 17:43:39 -0800835 fullyQualifyClassName(origPackage, application, String16("name"));
Dianne Hackbornb0381ef2010-03-03 13:36:35 -0800836 fullyQualifyClassName(origPackage, application, String16("backupAgent"));
Jeff Hamilton2fee0ed2010-01-06 15:46:38 -0600837
838 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
839 for (size_t i = 0; i < children.size(); i++) {
840 sp<XMLNode> child = children.editItemAt(i);
841 String8 tag(child->getElementName());
842 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
Dianne Hackbornef05e072010-03-01 17:43:39 -0800843 fullyQualifyClassName(origPackage, child, String16("name"));
844 } else if (tag == "activity-alias") {
845 fullyQualifyClassName(origPackage, child, String16("name"));
846 fullyQualifyClassName(origPackage, child, String16("targetActivity"));
Jeff Hamilton2fee0ed2010-01-06 15:46:38 -0600847 }
848 }
849 }
850 }
851
Dianne Hackbornef05e072010-03-01 17:43:39 -0800852 // Deal with manifest package name overrides
853 const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride();
854 if (instrumentationPackageNameOverride != NULL) {
855 // Fix up instrumentation targets.
856 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren());
857 for (size_t i = 0; i < children.size(); i++) {
858 sp<XMLNode> child = children.editItemAt(i);
859 String8 tag(child->getElementName());
860 if (tag == "instrumentation") {
861 XMLNode::attribute_entry* attr = child->editAttribute(
862 String16("http://schemas.android.com/apk/res/android"), String16("targetPackage"));
863 if (attr != NULL) {
864 attr->string.setTo(String16(instrumentationPackageNameOverride));
865 }
866 }
867 }
868 }
Elliott Hughesc367d482013-10-29 13:12:55 -0700869
Dianne Hackborn62da8462009-05-13 15:06:13 -0700870 return NO_ERROR;
871}
872
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873#define ASSIGN_IT(n) \
874 do { \
875 ssize_t index = resources->indexOfKey(String8(#n)); \
876 if (index >= 0) { \
877 n ## s = resources->valueAt(index); \
878 } \
879 } while (0)
880
Josiah Gaskin8a39da82011-06-06 17:00:35 -0700881status_t updatePreProcessedCache(Bundle* bundle)
882{
883 #if BENCHMARK
884 fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n");
885 long startPNGTime = clock();
886 #endif /* BENCHMARK */
887
888 String8 source(bundle->getResourceSourceDirs()[0]);
889 String8 dest(bundle->getCrunchedOutputDir());
890
891 FileFinder* ff = new SystemFileFinder();
892 CrunchCache cc(source,dest,ff);
893
894 CacheUpdater* cu = new SystemCacheUpdater(bundle);
895 size_t numFiles = cc.crunch(cu);
896
897 if (bundle->getVerbose())
898 fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles);
899
900 delete ff;
901 delete cu;
902
903 #if BENCHMARK
904 fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n"
905 ,(clock() - startPNGTime)/1000.0);
906 #endif /* BENCHMARK */
907 return 0;
908}
909
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
911{
912 // First, look for a package file to parse. This is required to
913 // be able to generate the resource information.
914 sp<AaptGroup> androidManifestFile =
915 assets->getFiles().valueFor(String8("AndroidManifest.xml"));
916 if (androidManifestFile == NULL) {
917 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
918 return UNKNOWN_ERROR;
919 }
920
Kenny Rootb5ef7ee2009-12-10 13:52:53 -0800921 status_t err = parsePackage(bundle, assets, androidManifestFile);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 if (err != NO_ERROR) {
923 return err;
924 }
925
Andreas Gampe2412f842014-09-30 20:55:57 -0700926 if (kIsDebug) {
927 printf("Creating resources for package %s\n", assets->getPackage().string());
928 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929
930 ResourceTable table(bundle, String16(assets->getPackage()));
931 err = table.addIncludedResources(bundle, assets);
932 if (err != NO_ERROR) {
933 return err;
934 }
935
Andreas Gampe2412f842014-09-30 20:55:57 -0700936 if (kIsDebug) {
937 printf("Found %d included resource packages\n", (int)table.size());
938 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939
Kenny Root19138462009-12-04 09:38:48 -0800940 // Standard flags for compiled XML and optional UTF-8 encoding
941 int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
Kenny Root1741cd42010-03-18 12:12:11 -0700942
943 /* Only enable UTF-8 if the caller of aapt didn't specifically
944 * request UTF-16 encoding and the parameters of this package
945 * allow UTF-8 to be used.
946 */
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800947 if (!bundle->getUTF16StringsOption()) {
Kenny Root19138462009-12-04 09:38:48 -0800948 xmlFlags |= XML_COMPILE_UTF8;
949 }
950
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 // --------------------------------------------------------------
952 // First, gather all resource information.
953 // --------------------------------------------------------------
954
955 // resType -> leafName -> group
Elliott Hughesc367d482013-10-29 13:12:55 -0700956 KeyedVector<String8, sp<ResourceTypeSet> > *resources =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 new KeyedVector<String8, sp<ResourceTypeSet> >;
958 collect_files(assets, resources);
959
960 sp<ResourceTypeSet> drawables;
961 sp<ResourceTypeSet> layouts;
962 sp<ResourceTypeSet> anims;
Dianne Hackbornf31161a2011-01-04 21:02:48 -0800963 sp<ResourceTypeSet> animators;
964 sp<ResourceTypeSet> interpolators;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700965 sp<ResourceTypeSet> transitions;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 sp<ResourceTypeSet> xmls;
967 sp<ResourceTypeSet> raws;
968 sp<ResourceTypeSet> colors;
969 sp<ResourceTypeSet> menus;
Kenny Root7c710232010-11-22 22:28:37 -0800970 sp<ResourceTypeSet> mipmaps;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971
972 ASSIGN_IT(drawable);
973 ASSIGN_IT(layout);
974 ASSIGN_IT(anim);
Dianne Hackbornf31161a2011-01-04 21:02:48 -0800975 ASSIGN_IT(animator);
976 ASSIGN_IT(interpolator);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700977 ASSIGN_IT(transition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 ASSIGN_IT(xml);
979 ASSIGN_IT(raw);
980 ASSIGN_IT(color);
981 ASSIGN_IT(menu);
Kenny Root7c710232010-11-22 22:28:37 -0800982 ASSIGN_IT(mipmap);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983
984 assets->setResources(resources);
985 // now go through any resource overlays and collect their files
986 sp<AaptAssets> current = assets->getOverlay();
987 while(current.get()) {
Elliott Hughesc367d482013-10-29 13:12:55 -0700988 KeyedVector<String8, sp<ResourceTypeSet> > *resources =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 new KeyedVector<String8, sp<ResourceTypeSet> >;
990 current->setResources(resources);
991 collect_files(current, resources);
992 current = current->getOverlay();
993 }
994 // apply the overlay files to the base set
Xavier Ducrohet83f4c092010-03-04 15:21:59 -0800995 if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
996 !applyFileOverlay(bundle, assets, &layouts, "layout") ||
997 !applyFileOverlay(bundle, assets, &anims, "anim") ||
Dianne Hackbornf31161a2011-01-04 21:02:48 -0800998 !applyFileOverlay(bundle, assets, &animators, "animator") ||
999 !applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
Chet Haasefaebd8f2012-05-18 14:17:57 -07001000 !applyFileOverlay(bundle, assets, &transitions, "transition") ||
Xavier Ducrohet83f4c092010-03-04 15:21:59 -08001001 !applyFileOverlay(bundle, assets, &xmls, "xml") ||
1002 !applyFileOverlay(bundle, assets, &raws, "raw") ||
1003 !applyFileOverlay(bundle, assets, &colors, "color") ||
Kenny Root7c710232010-11-22 22:28:37 -08001004 !applyFileOverlay(bundle, assets, &menus, "menu") ||
1005 !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
Robert Greenwaltfa5c7e12009-06-05 18:53:26 -07001006 return UNKNOWN_ERROR;
1007 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008
1009 bool hasErrors = false;
1010
1011 if (drawables != NULL) {
Anthony Newnam578a57f2010-09-01 12:06:04 -05001012 if (bundle->getOutputAPKFile() != NULL) {
Kenny Root7c710232010-11-22 22:28:37 -08001013 err = preProcessImages(bundle, assets, drawables, "drawable");
Anthony Newnam578a57f2010-09-01 12:06:04 -05001014 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 if (err == NO_ERROR) {
1016 err = makeFileResources(bundle, assets, &table, drawables, "drawable");
1017 if (err != NO_ERROR) {
1018 hasErrors = true;
1019 }
1020 } else {
1021 hasErrors = true;
1022 }
1023 }
1024
Kenny Root7c710232010-11-22 22:28:37 -08001025 if (mipmaps != NULL) {
1026 if (bundle->getOutputAPKFile() != NULL) {
1027 err = preProcessImages(bundle, assets, mipmaps, "mipmap");
1028 }
1029 if (err == NO_ERROR) {
1030 err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
1031 if (err != NO_ERROR) {
1032 hasErrors = true;
1033 }
1034 } else {
1035 hasErrors = true;
1036 }
1037 }
1038
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 if (layouts != NULL) {
1040 err = makeFileResources(bundle, assets, &table, layouts, "layout");
1041 if (err != NO_ERROR) {
1042 hasErrors = true;
1043 }
1044 }
1045
1046 if (anims != NULL) {
1047 err = makeFileResources(bundle, assets, &table, anims, "anim");
1048 if (err != NO_ERROR) {
1049 hasErrors = true;
1050 }
1051 }
1052
Dianne Hackbornf31161a2011-01-04 21:02:48 -08001053 if (animators != NULL) {
1054 err = makeFileResources(bundle, assets, &table, animators, "animator");
1055 if (err != NO_ERROR) {
1056 hasErrors = true;
1057 }
1058 }
1059
Chet Haasefaebd8f2012-05-18 14:17:57 -07001060 if (transitions != NULL) {
1061 err = makeFileResources(bundle, assets, &table, transitions, "transition");
1062 if (err != NO_ERROR) {
1063 hasErrors = true;
1064 }
1065 }
1066
Dianne Hackbornf31161a2011-01-04 21:02:48 -08001067 if (interpolators != NULL) {
1068 err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
1069 if (err != NO_ERROR) {
1070 hasErrors = true;
1071 }
1072 }
1073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 if (xmls != NULL) {
1075 err = makeFileResources(bundle, assets, &table, xmls, "xml");
1076 if (err != NO_ERROR) {
1077 hasErrors = true;
1078 }
1079 }
1080
1081 if (raws != NULL) {
1082 err = makeFileResources(bundle, assets, &table, raws, "raw");
1083 if (err != NO_ERROR) {
1084 hasErrors = true;
1085 }
1086 }
1087
1088 // compile resources
1089 current = assets;
1090 while(current.get()) {
Elliott Hughesc367d482013-10-29 13:12:55 -07001091 KeyedVector<String8, sp<ResourceTypeSet> > *resources =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 current->getResources();
1093
1094 ssize_t index = resources->indexOfKey(String8("values"));
1095 if (index >= 0) {
1096 ResourceDirIterator it(resources->valueAt(index), String8("values"));
1097 ssize_t res;
1098 while ((res=it.next()) == NO_ERROR) {
1099 sp<AaptFile> file = it.getFile();
Elliott Hughesc367d482013-10-29 13:12:55 -07001100 res = compileResourceFile(bundle, assets, file, it.getParams(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 (current!=assets), &table);
1102 if (res != NO_ERROR) {
1103 hasErrors = true;
1104 }
1105 }
1106 }
1107 current = current->getOverlay();
1108 }
1109
1110 if (colors != NULL) {
1111 err = makeFileResources(bundle, assets, &table, colors, "color");
1112 if (err != NO_ERROR) {
1113 hasErrors = true;
1114 }
1115 }
1116
1117 if (menus != NULL) {
1118 err = makeFileResources(bundle, assets, &table, menus, "menu");
1119 if (err != NO_ERROR) {
1120 hasErrors = true;
1121 }
1122 }
1123
1124 // --------------------------------------------------------------------
1125 // Assignment of resource IDs and initial generation of resource table.
1126 // --------------------------------------------------------------------
1127
1128 if (table.hasResources()) {
1129 sp<AaptFile> resFile(getResourceFile(assets));
1130 if (resFile == NULL) {
1131 fprintf(stderr, "Error: unable to generate entry for resource data\n");
1132 return UNKNOWN_ERROR;
1133 }
1134
1135 err = table.assignResourceIds();
1136 if (err < NO_ERROR) {
1137 return err;
1138 }
1139 }
1140
1141 // --------------------------------------------------------------
1142 // Finally, we can now we can compile XML files, which may reference
1143 // resources.
1144 // --------------------------------------------------------------
1145
1146 if (layouts != NULL) {
1147 ResourceDirIterator it(layouts, String8("layout"));
1148 while ((err=it.next()) == NO_ERROR) {
1149 String8 src = it.getFile()->getPrintableSource();
Kenny Root19138462009-12-04 09:38:48 -08001150 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 if (err == NO_ERROR) {
1152 ResXMLTree block;
1153 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1154 checkForIds(src, block);
1155 } else {
1156 hasErrors = true;
1157 }
1158 }
1159
1160 if (err < NO_ERROR) {
1161 hasErrors = true;
1162 }
1163 err = NO_ERROR;
1164 }
1165
1166 if (anims != NULL) {
1167 ResourceDirIterator it(anims, String8("anim"));
1168 while ((err=it.next()) == NO_ERROR) {
Kenny Root19138462009-12-04 09:38:48 -08001169 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 if (err != NO_ERROR) {
1171 hasErrors = true;
1172 }
1173 }
1174
1175 if (err < NO_ERROR) {
1176 hasErrors = true;
1177 }
1178 err = NO_ERROR;
1179 }
1180
Dianne Hackbornf31161a2011-01-04 21:02:48 -08001181 if (animators != NULL) {
1182 ResourceDirIterator it(animators, String8("animator"));
1183 while ((err=it.next()) == NO_ERROR) {
1184 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1185 if (err != NO_ERROR) {
1186 hasErrors = true;
1187 }
1188 }
1189
1190 if (err < NO_ERROR) {
1191 hasErrors = true;
1192 }
1193 err = NO_ERROR;
1194 }
1195
1196 if (interpolators != NULL) {
1197 ResourceDirIterator it(interpolators, String8("interpolator"));
1198 while ((err=it.next()) == NO_ERROR) {
1199 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1200 if (err != NO_ERROR) {
1201 hasErrors = true;
1202 }
1203 }
1204
1205 if (err < NO_ERROR) {
1206 hasErrors = true;
1207 }
1208 err = NO_ERROR;
1209 }
1210
Chet Haasefaebd8f2012-05-18 14:17:57 -07001211 if (transitions != NULL) {
1212 ResourceDirIterator it(transitions, String8("transition"));
1213 while ((err=it.next()) == NO_ERROR) {
1214 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1215 if (err != NO_ERROR) {
1216 hasErrors = true;
1217 }
1218 }
1219
1220 if (err < NO_ERROR) {
1221 hasErrors = true;
1222 }
1223 err = NO_ERROR;
1224 }
1225
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 if (xmls != NULL) {
1227 ResourceDirIterator it(xmls, String8("xml"));
1228 while ((err=it.next()) == NO_ERROR) {
Kenny Root19138462009-12-04 09:38:48 -08001229 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 if (err != NO_ERROR) {
1231 hasErrors = true;
1232 }
1233 }
1234
1235 if (err < NO_ERROR) {
1236 hasErrors = true;
1237 }
1238 err = NO_ERROR;
1239 }
1240
1241 if (drawables != NULL) {
1242 err = postProcessImages(assets, &table, drawables);
1243 if (err != NO_ERROR) {
1244 hasErrors = true;
1245 }
1246 }
1247
1248 if (colors != NULL) {
1249 ResourceDirIterator it(colors, String8("color"));
1250 while ((err=it.next()) == NO_ERROR) {
Kenny Root19138462009-12-04 09:38:48 -08001251 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252 if (err != NO_ERROR) {
1253 hasErrors = true;
1254 }
1255 }
1256
1257 if (err < NO_ERROR) {
1258 hasErrors = true;
1259 }
1260 err = NO_ERROR;
1261 }
1262
1263 if (menus != NULL) {
1264 ResourceDirIterator it(menus, String8("menu"));
1265 while ((err=it.next()) == NO_ERROR) {
1266 String8 src = it.getFile()->getPrintableSource();
Kenny Root19138462009-12-04 09:38:48 -08001267 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 if (err != NO_ERROR) {
1269 hasErrors = true;
1270 }
1271 ResXMLTree block;
1272 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1273 checkForIds(src, block);
1274 }
1275
1276 if (err < NO_ERROR) {
1277 hasErrors = true;
1278 }
1279 err = NO_ERROR;
1280 }
1281
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001282 if (table.validateLocalizations()) {
1283 hasErrors = true;
1284 }
Elliott Hughesc367d482013-10-29 13:12:55 -07001285
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001286 if (hasErrors) {
1287 return UNKNOWN_ERROR;
1288 }
1289
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
1291 String8 manifestPath(manifestFile->getPrintableSource());
1292
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001293 // Generate final compiled manifest file.
1294 manifestFile->clearData();
1295 sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
1296 if (manifestTree == NULL) {
1297 return UNKNOWN_ERROR;
1298 }
1299 err = massageManifest(bundle, manifestTree);
1300 if (err < NO_ERROR) {
1301 return err;
1302 }
1303 err = compileXmlFile(assets, manifestTree, manifestFile, &table);
1304 if (err < NO_ERROR) {
1305 return err;
1306 }
1307
1308 //block.restart();
1309 //printXMLBlock(&block);
1310
1311 // --------------------------------------------------------------
1312 // Generate the final resource table.
1313 // Re-flatten because we may have added new resource IDs
1314 // --------------------------------------------------------------
1315
1316 ResTable finalResTable;
1317 sp<AaptFile> resFile;
Elliott Hughesc367d482013-10-29 13:12:55 -07001318
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001319 if (table.hasResources()) {
1320 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
1321 err = table.addSymbols(symbols);
1322 if (err < NO_ERROR) {
1323 return err;
1324 }
1325
1326 resFile = getResourceFile(assets);
1327 if (resFile == NULL) {
1328 fprintf(stderr, "Error: unable to generate entry for resource data\n");
1329 return UNKNOWN_ERROR;
1330 }
1331
1332 err = table.flatten(bundle, resFile);
1333 if (err < NO_ERROR) {
1334 return err;
1335 }
1336
1337 if (bundle->getPublicOutputFile()) {
1338 FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
1339 if (fp == NULL) {
1340 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
1341 (const char*)bundle->getPublicOutputFile(), strerror(errno));
1342 return UNKNOWN_ERROR;
1343 }
1344 if (bundle->getVerbose()) {
1345 printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile());
1346 }
1347 table.writePublicDefinitions(String16(assets->getPackage()), fp);
1348 fclose(fp);
1349 }
Elliott Hughesc367d482013-10-29 13:12:55 -07001350
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001351 // Read resources back in,
Narayan Kamath7c4887f2014-01-27 17:32:37 +00001352 finalResTable.add(resFile->getData(), resFile->getSize());
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001353 }
Elliott Hughesc367d482013-10-29 13:12:55 -07001354
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 // Perform a basic validation of the manifest file. This time we
1356 // parse it with the comments intact, so that we can use them to
1357 // generate java docs... so we are not going to write this one
1358 // back out to the final manifest data.
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001359 sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(),
1360 manifestFile->getGroupEntry(),
1361 manifestFile->getResourceType());
1362 err = compileXmlFile(assets, manifestFile,
1363 outManifestFile, &table,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001364 XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
1365 | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
1366 if (err < NO_ERROR) {
1367 return err;
1368 }
1369 ResXMLTree block;
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001370 block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 String16 manifest16("manifest");
1372 String16 permission16("permission");
1373 String16 permission_group16("permission-group");
1374 String16 uses_permission16("uses-permission");
1375 String16 instrumentation16("instrumentation");
1376 String16 application16("application");
1377 String16 provider16("provider");
1378 String16 service16("service");
1379 String16 receiver16("receiver");
1380 String16 activity16("activity");
1381 String16 action16("action");
1382 String16 category16("category");
1383 String16 data16("scheme");
1384 const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
1385 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
1386 const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
1387 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1388 const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz"
1389 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$";
1390 const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz"
1391 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:";
1392 const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz"
1393 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;";
1394 const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1395 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+";
1396 const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1397 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1398 ResXMLTree::event_code_t code;
1399 sp<AaptSymbols> permissionSymbols;
1400 sp<AaptSymbols> permissionGroupSymbols;
1401 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1402 && code > ResXMLTree::BAD_DOCUMENT) {
1403 if (code == ResXMLTree::START_TAG) {
1404 size_t len;
1405 if (block.getElementNamespace(&len) != NULL) {
1406 continue;
1407 }
1408 if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001409 if (validateAttr(manifestPath, finalResTable, block, NULL, "package",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 packageIdentChars, true) != ATTR_OKAY) {
1411 hasErrors = true;
1412 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001413 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1414 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) {
1415 hasErrors = true;
1416 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
1418 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
1419 const bool isGroup = strcmp16(block.getElementName(&len),
1420 permission_group16.string()) == 0;
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001421 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1422 "name", isGroup ? packageIdentCharsWithTheStupid
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 : packageIdentChars, true) != ATTR_OKAY) {
1424 hasErrors = true;
1425 }
1426 SourcePos srcPos(manifestPath, block.getLineNumber());
1427 sp<AaptSymbols> syms;
1428 if (!isGroup) {
1429 syms = permissionSymbols;
1430 if (syms == NULL) {
1431 sp<AaptSymbols> symbols =
1432 assets->getSymbolsFor(String8("Manifest"));
1433 syms = permissionSymbols = symbols->addNestedSymbol(
1434 String8("permission"), srcPos);
1435 }
1436 } else {
1437 syms = permissionGroupSymbols;
1438 if (syms == NULL) {
1439 sp<AaptSymbols> symbols =
1440 assets->getSymbolsFor(String8("Manifest"));
1441 syms = permissionGroupSymbols = symbols->addNestedSymbol(
1442 String8("permission_group"), srcPos);
1443 }
1444 }
1445 size_t len;
1446 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name");
Dan Albertf348c152014-09-08 18:28:00 -07001447 const char16_t* id = block.getAttributeStringValue(index, &len);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001448 if (id == NULL) {
Elliott Hughesc367d482013-10-29 13:12:55 -07001449 fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450 manifestPath.string(), block.getLineNumber(),
1451 String8(block.getElementName(&len)).string());
1452 hasErrors = true;
1453 break;
1454 }
1455 String8 idStr(id);
1456 char* p = idStr.lockBuffer(idStr.size());
1457 char* e = p + idStr.size();
1458 bool begins_with_digit = true; // init to true so an empty string fails
1459 while (e > p) {
1460 e--;
1461 if (*e >= '0' && *e <= '9') {
1462 begins_with_digit = true;
1463 continue;
1464 }
1465 if ((*e >= 'a' && *e <= 'z') ||
1466 (*e >= 'A' && *e <= 'Z') ||
1467 (*e == '_')) {
1468 begins_with_digit = false;
1469 continue;
1470 }
1471 if (isGroup && (*e == '-')) {
1472 *e = '_';
1473 begins_with_digit = false;
1474 continue;
1475 }
1476 e++;
1477 break;
1478 }
1479 idStr.unlockBuffer();
1480 // verify that we stopped because we hit a period or
1481 // the beginning of the string, and that the
1482 // identifier didn't begin with a digit.
1483 if (begins_with_digit || (e != p && *(e-1) != '.')) {
1484 fprintf(stderr,
1485 "%s:%d: Permission name <%s> is not a valid Java symbol\n",
1486 manifestPath.string(), block.getLineNumber(), idStr.string());
1487 hasErrors = true;
1488 }
1489 syms->addStringSymbol(String8(e), idStr, srcPos);
Dan Albertf348c152014-09-08 18:28:00 -07001490 const char16_t* cmt = block.getComment(&len);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 if (cmt != NULL && *cmt != 0) {
1492 //printf("Comment of %s: %s\n", String8(e).string(),
1493 // String8(cmt).string());
1494 syms->appendComment(String8(e), String16(cmt), srcPos);
1495 } else {
1496 //printf("No comment for %s\n", String8(e).string());
1497 }
1498 syms->makeSymbolPublic(String8(e), srcPos);
1499 } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001500 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1501 "name", packageIdentChars, true) != ATTR_OKAY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 hasErrors = true;
1503 }
1504 } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001505 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1506 "name", classIdentChars, true) != ATTR_OKAY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 hasErrors = true;
1508 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001509 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 RESOURCES_ANDROID_NAMESPACE, "targetPackage",
1511 packageIdentChars, true) != ATTR_OKAY) {
1512 hasErrors = true;
1513 }
1514 } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001515 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1516 "name", classIdentChars, false) != ATTR_OKAY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 hasErrors = true;
1518 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001519 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001520 RESOURCES_ANDROID_NAMESPACE, "permission",
1521 packageIdentChars, false) != ATTR_OKAY) {
1522 hasErrors = true;
1523 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001524 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 RESOURCES_ANDROID_NAMESPACE, "process",
1526 processIdentChars, false) != ATTR_OKAY) {
1527 hasErrors = true;
1528 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001529 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1531 processIdentChars, false) != ATTR_OKAY) {
1532 hasErrors = true;
1533 }
1534 } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001535 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1536 "name", classIdentChars, true) != ATTR_OKAY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 hasErrors = true;
1538 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001539 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 RESOURCES_ANDROID_NAMESPACE, "authorities",
1541 authoritiesIdentChars, true) != ATTR_OKAY) {
1542 hasErrors = true;
1543 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001544 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 RESOURCES_ANDROID_NAMESPACE, "permission",
1546 packageIdentChars, false) != ATTR_OKAY) {
1547 hasErrors = true;
1548 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001549 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 RESOURCES_ANDROID_NAMESPACE, "process",
1551 processIdentChars, false) != ATTR_OKAY) {
1552 hasErrors = true;
1553 }
1554 } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
1555 || strcmp16(block.getElementName(&len), receiver16.string()) == 0
1556 || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001557 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1558 "name", classIdentChars, true) != ATTR_OKAY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001559 hasErrors = true;
1560 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001561 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001562 RESOURCES_ANDROID_NAMESPACE, "permission",
1563 packageIdentChars, false) != ATTR_OKAY) {
1564 hasErrors = true;
1565 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001566 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 RESOURCES_ANDROID_NAMESPACE, "process",
1568 processIdentChars, false) != ATTR_OKAY) {
1569 hasErrors = true;
1570 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001571 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001572 RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1573 processIdentChars, false) != ATTR_OKAY) {
1574 hasErrors = true;
1575 }
1576 } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
1577 || strcmp16(block.getElementName(&len), category16.string()) == 0) {
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001578 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001579 RESOURCES_ANDROID_NAMESPACE, "name",
1580 packageIdentChars, true) != ATTR_OKAY) {
1581 hasErrors = true;
1582 }
1583 } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001584 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001585 RESOURCES_ANDROID_NAMESPACE, "mimeType",
1586 typeIdentChars, true) != ATTR_OKAY) {
1587 hasErrors = true;
1588 }
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001589 if (validateAttr(manifestPath, finalResTable, block,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001590 RESOURCES_ANDROID_NAMESPACE, "scheme",
1591 schemeIdentChars, true) != ATTR_OKAY) {
1592 hasErrors = true;
1593 }
1594 }
1595 }
1596 }
1597
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001598 if (resFile != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001599 // These resources are now considered to be a part of the included
1600 // resources, for others to reference.
1601 err = assets->addIncludedResources(resFile);
1602 if (err < NO_ERROR) {
1603 fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n");
1604 return err;
1605 }
1606 }
Elliott Hughesc367d482013-10-29 13:12:55 -07001607
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001608 return err;
1609}
1610
1611static const char* getIndentSpace(int indent)
1612{
1613static const char whitespace[] =
1614" ";
1615
1616 return whitespace + sizeof(whitespace) - 1 - indent*4;
1617}
1618
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001619static String8 flattenSymbol(const String8& symbol) {
1620 String8 result(symbol);
1621 ssize_t first;
1622 if ((first = symbol.find(":", 0)) >= 0
1623 || (first = symbol.find(".", 0)) >= 0) {
1624 size_t size = symbol.size();
1625 char* buf = result.lockBuffer(size);
1626 for (size_t i = first; i < size; i++) {
1627 if (buf[i] == ':' || buf[i] == '.') {
1628 buf[i] = '_';
1629 }
1630 }
1631 result.unlockBuffer(size);
1632 }
1633 return result;
1634}
1635
1636static String8 getSymbolPackage(const String8& symbol, const sp<AaptAssets>& assets, bool pub) {
1637 ssize_t colon = symbol.find(":", 0);
1638 if (colon >= 0) {
1639 return String8(symbol.string(), colon);
1640 }
1641 return pub ? assets->getPackage() : assets->getSymbolsPrivatePackage();
1642}
1643
1644static String8 getSymbolName(const String8& symbol) {
1645 ssize_t colon = symbol.find(":", 0);
1646 if (colon >= 0) {
1647 return String8(symbol.string() + colon + 1);
1648 }
1649 return symbol;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001650}
1651
1652static String16 getAttributeComment(const sp<AaptAssets>& assets,
1653 const String8& name,
1654 String16* outTypeComment = NULL)
1655{
1656 sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R"));
1657 if (asym != NULL) {
1658 //printf("Got R symbols!\n");
1659 asym = asym->getNestedSymbols().valueFor(String8("attr"));
1660 if (asym != NULL) {
1661 //printf("Got attrs symbols! comment %s=%s\n",
1662 // name.string(), String8(asym->getComment(name)).string());
1663 if (outTypeComment != NULL) {
1664 *outTypeComment = asym->getTypeComment(name);
1665 }
1666 return asym->getComment(name);
1667 }
1668 }
1669 return String16();
1670}
1671
1672static status_t writeLayoutClasses(
1673 FILE* fp, const sp<AaptAssets>& assets,
Xavier Ducroheta068eed2013-04-13 09:48:01 -07001674 const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001675{
1676 const char* indentStr = getIndentSpace(indent);
1677 if (!includePrivate) {
1678 fprintf(fp, "%s/** @doconly */\n", indentStr);
1679 }
1680 fprintf(fp, "%spublic static final class styleable {\n", indentStr);
1681 indent++;
1682
1683 String16 attr16("attr");
1684 String16 package16(assets->getPackage());
1685
1686 indentStr = getIndentSpace(indent);
1687 bool hasErrors = false;
1688
1689 size_t i;
1690 size_t N = symbols->getNestedSymbols().size();
1691 for (i=0; i<N; i++) {
1692 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001693 String8 realClassName(symbols->getNestedSymbols().keyAt(i));
1694 String8 nclassName(flattenSymbol(realClassName));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695
1696 SortedVector<uint32_t> idents;
1697 Vector<uint32_t> origOrder;
1698 Vector<bool> publicFlags;
1699
1700 size_t a;
1701 size_t NA = nsymbols->getSymbols().size();
1702 for (a=0; a<NA; a++) {
1703 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
1704 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
1705 ? sym.int32Val : 0;
1706 bool isPublic = true;
1707 if (code == 0) {
1708 String16 name16(sym.name);
1709 uint32_t typeSpecFlags;
1710 code = assets->getIncludedResources().identifierForName(
1711 name16.string(), name16.size(),
1712 attr16.string(), attr16.size(),
1713 package16.string(), package16.size(), &typeSpecFlags);
1714 if (code == 0) {
1715 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
1716 nclassName.string(), sym.name.string());
1717 hasErrors = true;
1718 }
1719 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
1720 }
1721 idents.add(code);
1722 origOrder.add(code);
1723 publicFlags.add(isPublic);
1724 }
1725
1726 NA = idents.size();
1727
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001728 bool deprecated = false;
Elliott Hughesc367d482013-10-29 13:12:55 -07001729
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730 String16 comment = symbols->getComment(realClassName);
1731 fprintf(fp, "%s/** ", indentStr);
1732 if (comment.size() > 0) {
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001733 String8 cmt(comment);
1734 fprintf(fp, "%s\n", cmt.string());
1735 if (strstr(cmt.string(), "@deprecated") != NULL) {
1736 deprecated = true;
1737 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001738 } else {
1739 fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
1740 }
1741 bool hasTable = false;
1742 for (a=0; a<NA; a++) {
1743 ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1744 if (pos >= 0) {
1745 if (!hasTable) {
1746 hasTable = true;
1747 fprintf(fp,
1748 "%s <p>Includes the following attributes:</p>\n"
Dirk Dougherty59ad2752009-11-03 15:33:37 -08001749 "%s <table>\n"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 "%s <colgroup align=\"left\" />\n"
1751 "%s <colgroup align=\"left\" />\n"
Dirk Dougherty59ad2752009-11-03 15:33:37 -08001752 "%s <tr><th>Attribute</th><th>Description</th></tr>\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001753 indentStr,
1754 indentStr,
1755 indentStr,
1756 indentStr,
1757 indentStr);
1758 }
1759 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1760 if (!publicFlags.itemAt(a) && !includePrivate) {
1761 continue;
1762 }
1763 String8 name8(sym.name);
1764 String16 comment(sym.comment);
1765 if (comment.size() <= 0) {
1766 comment = getAttributeComment(assets, name8);
1767 }
1768 if (comment.size() > 0) {
1769 const char16_t* p = comment.string();
1770 while (*p != 0 && *p != '.') {
1771 if (*p == '{') {
1772 while (*p != 0 && *p != '}') {
1773 p++;
1774 }
1775 } else {
1776 p++;
1777 }
1778 }
1779 if (*p == '.') {
1780 p++;
1781 }
1782 comment = String16(comment.string(), p-comment.string());
1783 }
Dirk Dougherty59ad2752009-11-03 15:33:37 -08001784 fprintf(fp, "%s <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 indentStr, nclassName.string(),
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001786 flattenSymbol(name8).string(),
1787 getSymbolPackage(name8, assets, true).string(),
1788 getSymbolName(name8).string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 String8(comment).string());
1790 }
1791 }
1792 if (hasTable) {
1793 fprintf(fp, "%s </table>\n", indentStr);
1794 }
1795 for (a=0; a<NA; a++) {
1796 ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1797 if (pos >= 0) {
1798 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1799 if (!publicFlags.itemAt(a) && !includePrivate) {
1800 continue;
1801 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 fprintf(fp, "%s @see #%s_%s\n",
1803 indentStr, nclassName.string(),
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001804 flattenSymbol(sym.name).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 }
1806 }
1807 fprintf(fp, "%s */\n", getIndentSpace(indent));
1808
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001809 if (deprecated) {
1810 fprintf(fp, "%s@Deprecated\n", indentStr);
1811 }
Elliott Hughesc367d482013-10-29 13:12:55 -07001812
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001813 fprintf(fp,
Xavier Ducroheta068eed2013-04-13 09:48:01 -07001814 "%spublic static final int[] %s = {\n"
1815 "%s",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 indentStr, nclassName.string(),
1817 getIndentSpace(indent+1));
1818
1819 for (a=0; a<NA; a++) {
1820 if (a != 0) {
1821 if ((a&3) == 0) {
1822 fprintf(fp, ",\n%s", getIndentSpace(indent+1));
1823 } else {
1824 fprintf(fp, ", ");
1825 }
1826 }
1827 fprintf(fp, "0x%08x", idents[a]);
1828 }
1829
1830 fprintf(fp, "\n%s};\n", indentStr);
1831
1832 for (a=0; a<NA; a++) {
1833 ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1834 if (pos >= 0) {
1835 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1836 if (!publicFlags.itemAt(a) && !includePrivate) {
1837 continue;
1838 }
1839 String8 name8(sym.name);
1840 String16 comment(sym.comment);
1841 String16 typeComment;
1842 if (comment.size() <= 0) {
1843 comment = getAttributeComment(assets, name8, &typeComment);
1844 } else {
1845 getAttributeComment(assets, name8, &typeComment);
1846 }
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001847
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 uint32_t typeSpecFlags = 0;
1849 String16 name16(sym.name);
1850 assets->getIncludedResources().identifierForName(
1851 name16.string(), name16.size(),
1852 attr16.string(), attr16.size(),
1853 package16.string(), package16.size(), &typeSpecFlags);
1854 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
1855 // String8(attr16).string(), String8(name16).string(), typeSpecFlags);
1856 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
Elliott Hughesc367d482013-10-29 13:12:55 -07001857
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001858 bool deprecated = false;
Elliott Hughesc367d482013-10-29 13:12:55 -07001859
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 fprintf(fp, "%s/**\n", indentStr);
1861 if (comment.size() > 0) {
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001862 String8 cmt(comment);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001863 fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr);
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001864 fprintf(fp, "%s %s\n", indentStr, cmt.string());
1865 if (strstr(cmt.string(), "@deprecated") != NULL) {
1866 deprecated = true;
1867 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001868 } else {
1869 fprintf(fp,
1870 "%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
1871 "%s attribute's value can be found in the {@link #%s} array.\n",
1872 indentStr,
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001873 getSymbolPackage(name8, assets, pub).string(),
1874 getSymbolName(name8).string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875 indentStr, nclassName.string());
1876 }
1877 if (typeComment.size() > 0) {
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001878 String8 cmt(typeComment);
1879 fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string());
1880 if (strstr(cmt.string(), "@deprecated") != NULL) {
1881 deprecated = true;
1882 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883 }
1884 if (comment.size() > 0) {
1885 if (pub) {
1886 fprintf(fp,
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001887 "%s <p>This corresponds to the global attribute\n"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 "%s resource symbol {@link %s.R.attr#%s}.\n",
1889 indentStr, indentStr,
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001890 getSymbolPackage(name8, assets, true).string(),
1891 getSymbolName(name8).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001892 } else {
1893 fprintf(fp,
1894 "%s <p>This is a private symbol.\n", indentStr);
1895 }
1896 }
1897 fprintf(fp, "%s @attr name %s:%s\n", indentStr,
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001898 getSymbolPackage(name8, assets, pub).string(),
1899 getSymbolName(name8).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001900 fprintf(fp, "%s*/\n", indentStr);
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001901 if (deprecated) {
1902 fprintf(fp, "%s@Deprecated\n", indentStr);
1903 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001904 fprintf(fp,
Xavier Ducroheta068eed2013-04-13 09:48:01 -07001905 "%spublic static final int %s_%s = %d;\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 indentStr, nclassName.string(),
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001907 flattenSymbol(name8).string(), (int)pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001908 }
1909 }
1910 }
1911
1912 indent--;
1913 fprintf(fp, "%s};\n", getIndentSpace(indent));
Andreas Gampe2412f842014-09-30 20:55:57 -07001914 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001915}
1916
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07001917static status_t writeTextLayoutClasses(
1918 FILE* fp, const sp<AaptAssets>& assets,
1919 const sp<AaptSymbols>& symbols, bool includePrivate)
1920{
1921 String16 attr16("attr");
1922 String16 package16(assets->getPackage());
1923
1924 bool hasErrors = false;
1925
1926 size_t i;
1927 size_t N = symbols->getNestedSymbols().size();
1928 for (i=0; i<N; i++) {
1929 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
Jeff Browncaf7b0a2013-04-25 21:24:44 -07001930 String8 realClassName(symbols->getNestedSymbols().keyAt(i));
1931 String8 nclassName(flattenSymbol(realClassName));
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07001932
1933 SortedVector<uint32_t> idents;
1934 Vector<uint32_t> origOrder;
1935 Vector<bool> publicFlags;
1936
1937 size_t a;
1938 size_t NA = nsymbols->getSymbols().size();
1939 for (a=0; a<NA; a++) {
1940 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
1941 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
1942 ? sym.int32Val : 0;
1943 bool isPublic = true;
1944 if (code == 0) {
1945 String16 name16(sym.name);
1946 uint32_t typeSpecFlags;
1947 code = assets->getIncludedResources().identifierForName(
1948 name16.string(), name16.size(),
1949 attr16.string(), attr16.size(),
1950 package16.string(), package16.size(), &typeSpecFlags);
1951 if (code == 0) {
1952 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
1953 nclassName.string(), sym.name.string());
1954 hasErrors = true;
1955 }
1956 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
1957 }
1958 idents.add(code);
1959 origOrder.add(code);
1960 publicFlags.add(isPublic);
1961 }
1962
1963 NA = idents.size();
1964
1965 fprintf(fp, "int[] styleable %s {", nclassName.string());
1966
1967 for (a=0; a<NA; a++) {
1968 if (a != 0) {
1969 fprintf(fp, ",");
1970 }
1971 fprintf(fp, " 0x%08x", idents[a]);
1972 }
1973
1974 fprintf(fp, " }\n");
1975
1976 for (a=0; a<NA; a++) {
1977 ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1978 if (pos >= 0) {
1979 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1980 if (!publicFlags.itemAt(a) && !includePrivate) {
1981 continue;
1982 }
1983 String8 name8(sym.name);
1984 String16 comment(sym.comment);
1985 String16 typeComment;
1986 if (comment.size() <= 0) {
1987 comment = getAttributeComment(assets, name8, &typeComment);
1988 } else {
1989 getAttributeComment(assets, name8, &typeComment);
1990 }
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07001991
1992 uint32_t typeSpecFlags = 0;
1993 String16 name16(sym.name);
1994 assets->getIncludedResources().identifierForName(
1995 name16.string(), name16.size(),
1996 attr16.string(), attr16.size(),
1997 package16.string(), package16.size(), &typeSpecFlags);
1998 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
1999 // String8(attr16).string(), String8(name16).string(), typeSpecFlags);
Andreas Gampe2412f842014-09-30 20:55:57 -07002000 //const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002001
2002 fprintf(fp,
Xavier Ducrohetd1604742012-09-26 10:11:54 -07002003 "int styleable %s_%s %d\n",
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002004 nclassName.string(),
Jeff Browncaf7b0a2013-04-25 21:24:44 -07002005 flattenSymbol(name8).string(), (int)pos);
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002006 }
2007 }
2008 }
2009
Andreas Gampe2412f842014-09-30 20:55:57 -07002010 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002011}
2012
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013static status_t writeSymbolClass(
2014 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
Xavier Ducrohetd06c1af2011-02-14 16:58:00 -08002015 const sp<AaptSymbols>& symbols, const String8& className, int indent,
2016 bool nonConstantId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002017{
2018 fprintf(fp, "%spublic %sfinal class %s {\n",
2019 getIndentSpace(indent),
2020 indent != 0 ? "static " : "", className.string());
2021 indent++;
2022
2023 size_t i;
2024 status_t err = NO_ERROR;
2025
Xavier Ducrohetd06c1af2011-02-14 16:58:00 -08002026 const char * id_format = nonConstantId ?
2027 "%spublic static int %s=0x%08x;\n" :
2028 "%spublic static final int %s=0x%08x;\n";
2029
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002030 size_t N = symbols->getSymbols().size();
2031 for (i=0; i<N; i++) {
2032 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
2033 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
2034 continue;
2035 }
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002036 if (!assets->isJavaSymbol(sym, includePrivate)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002037 continue;
2038 }
Jeff Browncaf7b0a2013-04-25 21:24:44 -07002039 String8 name8(sym.name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002040 String16 comment(sym.comment);
2041 bool haveComment = false;
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002042 bool deprecated = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002043 if (comment.size() > 0) {
2044 haveComment = true;
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002045 String8 cmt(comment);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002046 fprintf(fp,
2047 "%s/** %s\n",
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002048 getIndentSpace(indent), cmt.string());
2049 if (strstr(cmt.string(), "@deprecated") != NULL) {
2050 deprecated = true;
2051 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002052 } else if (sym.isPublic && !includePrivate) {
2053 sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
2054 assets->getPackage().string(), className.string(),
2055 String8(sym.name).string());
2056 }
2057 String16 typeComment(sym.typeComment);
2058 if (typeComment.size() > 0) {
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002059 String8 cmt(typeComment);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002060 if (!haveComment) {
2061 haveComment = true;
2062 fprintf(fp,
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002063 "%s/** %s\n", getIndentSpace(indent), cmt.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002064 } else {
2065 fprintf(fp,
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002066 "%s %s\n", getIndentSpace(indent), cmt.string());
2067 }
2068 if (strstr(cmt.string(), "@deprecated") != NULL) {
2069 deprecated = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002070 }
2071 }
2072 if (haveComment) {
2073 fprintf(fp,"%s */\n", getIndentSpace(indent));
2074 }
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002075 if (deprecated) {
2076 fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
2077 }
Xavier Ducrohetd06c1af2011-02-14 16:58:00 -08002078 fprintf(fp, id_format,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002079 getIndentSpace(indent),
Jeff Browncaf7b0a2013-04-25 21:24:44 -07002080 flattenSymbol(name8).string(), (int)sym.int32Val);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002081 }
2082
2083 for (i=0; i<N; i++) {
2084 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
2085 if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) {
2086 continue;
2087 }
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002088 if (!assets->isJavaSymbol(sym, includePrivate)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002089 continue;
2090 }
Jeff Browncaf7b0a2013-04-25 21:24:44 -07002091 String8 name8(sym.name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002092 String16 comment(sym.comment);
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002093 bool deprecated = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002094 if (comment.size() > 0) {
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002095 String8 cmt(comment);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002096 fprintf(fp,
2097 "%s/** %s\n"
2098 "%s */\n",
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002099 getIndentSpace(indent), cmt.string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002100 getIndentSpace(indent));
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002101 if (strstr(cmt.string(), "@deprecated") != NULL) {
2102 deprecated = true;
2103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002104 } else if (sym.isPublic && !includePrivate) {
2105 sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
2106 assets->getPackage().string(), className.string(),
2107 String8(sym.name).string());
2108 }
Dianne Hackborn4a51c202009-08-21 15:14:02 -07002109 if (deprecated) {
2110 fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
2111 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002112 fprintf(fp, "%spublic static final String %s=\"%s\";\n",
2113 getIndentSpace(indent),
Jeff Browncaf7b0a2013-04-25 21:24:44 -07002114 flattenSymbol(name8).string(), sym.stringVal.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002115 }
2116
2117 sp<AaptSymbols> styleableSymbols;
2118
2119 N = symbols->getNestedSymbols().size();
2120 for (i=0; i<N; i++) {
2121 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
2122 String8 nclassName(symbols->getNestedSymbols().keyAt(i));
2123 if (nclassName == "styleable") {
2124 styleableSymbols = nsymbols;
2125 } else {
Xavier Ducrohetd06c1af2011-02-14 16:58:00 -08002126 err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002127 }
2128 if (err != NO_ERROR) {
2129 return err;
2130 }
2131 }
2132
2133 if (styleableSymbols != NULL) {
Xavier Ducroheta068eed2013-04-13 09:48:01 -07002134 err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002135 if (err != NO_ERROR) {
2136 return err;
2137 }
2138 }
2139
2140 indent--;
2141 fprintf(fp, "%s}\n", getIndentSpace(indent));
2142 return NO_ERROR;
2143}
2144
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002145static status_t writeTextSymbolClass(
2146 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
2147 const sp<AaptSymbols>& symbols, const String8& className)
2148{
2149 size_t i;
2150 status_t err = NO_ERROR;
2151
2152 size_t N = symbols->getSymbols().size();
2153 for (i=0; i<N; i++) {
2154 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
2155 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
2156 continue;
2157 }
2158
2159 if (!assets->isJavaSymbol(sym, includePrivate)) {
2160 continue;
2161 }
2162
Jeff Browncaf7b0a2013-04-25 21:24:44 -07002163 String8 name8(sym.name);
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002164 fprintf(fp, "int %s %s 0x%08x\n",
2165 className.string(),
Jeff Browncaf7b0a2013-04-25 21:24:44 -07002166 flattenSymbol(name8).string(), (int)sym.int32Val);
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002167 }
2168
2169 N = symbols->getNestedSymbols().size();
2170 for (i=0; i<N; i++) {
2171 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
2172 String8 nclassName(symbols->getNestedSymbols().keyAt(i));
2173 if (nclassName == "styleable") {
2174 err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate);
2175 } else {
2176 err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName);
2177 }
2178 if (err != NO_ERROR) {
2179 return err;
2180 }
2181 }
2182
2183 return NO_ERROR;
2184}
2185
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002186status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
2187 const String8& package, bool includePrivate)
2188{
2189 if (!bundle->getRClassDir()) {
2190 return NO_ERROR;
2191 }
2192
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002193 const char* textSymbolsDest = bundle->getOutputTextSymbols();
2194
2195 String8 R("R");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002196 const size_t N = assets->getSymbols().size();
2197 for (size_t i=0; i<N; i++) {
2198 sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
2199 String8 className(assets->getSymbols().keyAt(i));
2200 String8 dest(bundle->getRClassDir());
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002202 if (bundle->getMakePackageDirs()) {
2203 String8 pkg(package);
2204 const char* last = pkg.string();
2205 const char* s = last-1;
2206 do {
2207 s++;
2208 if (s > last && (*s == '.' || *s == 0)) {
2209 String8 part(last, s-last);
2210 dest.appendPath(part);
2211#ifdef HAVE_MS_C_RUNTIME
2212 _mkdir(dest.string());
2213#else
2214 mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
2215#endif
2216 last = s+1;
2217 }
2218 } while (*s);
2219 }
2220 dest.appendPath(className);
2221 dest.append(".java");
2222 FILE* fp = fopen(dest.string(), "w+");
2223 if (fp == NULL) {
2224 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
2225 dest.string(), strerror(errno));
2226 return UNKNOWN_ERROR;
2227 }
2228 if (bundle->getVerbose()) {
2229 printf(" Writing symbols for class %s.\n", className.string());
2230 }
2231
2232 fprintf(fp,
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002233 "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
2234 " *\n"
2235 " * This class was automatically generated by the\n"
2236 " * aapt tool from the resource data it found. It\n"
2237 " * should not be modified by hand.\n"
2238 " */\n"
2239 "\n"
2240 "package %s;\n\n", package.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002241
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002242 status_t err = writeSymbolClass(fp, assets, includePrivate, symbols,
2243 className, 0, bundle->getNonConstantId());
Elliott Hughesc367d482013-10-29 13:12:55 -07002244 fclose(fp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002245 if (err != NO_ERROR) {
2246 return err;
2247 }
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002248
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002249 if (textSymbolsDest != NULL && R == className) {
2250 String8 textDest(textSymbolsDest);
2251 textDest.appendPath(className);
2252 textDest.append(".txt");
2253
2254 FILE* fp = fopen(textDest.string(), "w+");
2255 if (fp == NULL) {
2256 fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n",
2257 textDest.string(), strerror(errno));
2258 return UNKNOWN_ERROR;
2259 }
2260 if (bundle->getVerbose()) {
2261 printf(" Writing text symbols for class %s.\n", className.string());
2262 }
2263
2264 status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols,
2265 className);
Elliott Hughesc367d482013-10-29 13:12:55 -07002266 fclose(fp);
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002267 if (err != NO_ERROR) {
2268 return err;
2269 }
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002270 }
2271
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002272 // If we were asked to generate a dependency file, we'll go ahead and add this R.java
2273 // as a target in the dependency file right next to it.
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002274 if (bundle->getGenDependencies() && R == className) {
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002275 // Add this R.java to the dependency file
2276 String8 dependencyFile(bundle->getRClassDir());
Josiah Gaskinb711f3f2011-08-15 18:33:44 -07002277 dependencyFile.appendPath("R.java.d");
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002278
Xavier Ducrohetf5de6502012-09-11 14:45:22 -07002279 FILE *fp = fopen(dependencyFile.string(), "a");
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002280 fprintf(fp,"%s \\\n", dest.string());
2281 fclose(fp);
2282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002283 }
2284
2285 return NO_ERROR;
2286}
Joe Onorato1553c822009-08-30 13:36:22 -07002287
2288
Joe Onorato1553c822009-08-30 13:36:22 -07002289class ProguardKeepSet
2290{
2291public:
2292 // { rule --> { file locations } }
2293 KeyedVector<String8, SortedVector<String8> > rules;
2294
2295 void add(const String8& rule, const String8& where);
2296};
2297
2298void ProguardKeepSet::add(const String8& rule, const String8& where)
2299{
2300 ssize_t index = rules.indexOfKey(rule);
2301 if (index < 0) {
2302 index = rules.add(rule, SortedVector<String8>());
2303 }
2304 rules.editValueAt(index).add(where);
2305}
2306
Dianne Hackbornb0381ef2010-03-03 13:36:35 -08002307void
2308addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
2309 const char* pkg, const String8& srcName, int line)
2310{
2311 String8 className(inClassName);
2312 if (pkg != NULL) {
2313 // asdf --> package.asdf
2314 // .asdf .a.b --> package.asdf package.a.b
2315 // asdf.adsf --> asdf.asdf
2316 const char* p = className.string();
2317 const char* q = strchr(p, '.');
2318 if (p == q) {
2319 className = pkg;
2320 className.append(inClassName);
2321 } else if (q == NULL) {
2322 className = pkg;
2323 className.append(".");
2324 className.append(inClassName);
2325 }
2326 }
Ying Wang561a9182010-08-13 13:56:07 -07002327
Dianne Hackbornb0381ef2010-03-03 13:36:35 -08002328 String8 rule("-keep class ");
2329 rule += className;
2330 rule += " { <init>(...); }";
2331
2332 String8 location("view ");
2333 location += srcName;
2334 char lineno[20];
2335 sprintf(lineno, ":%d", line);
2336 location += lineno;
2337
2338 keep->add(rule, location);
2339}
2340
Dianne Hackborn92751972012-05-18 19:22:14 -07002341void
2342addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName,
Andreas Gampe2412f842014-09-30 20:55:57 -07002343 const char* /* pkg */, const String8& srcName, int line)
Dianne Hackborn92751972012-05-18 19:22:14 -07002344{
2345 String8 rule("-keepclassmembers class * { *** ");
2346 rule += memberName;
2347 rule += "(...); }";
2348
2349 String8 location("onClick ");
2350 location += srcName;
2351 char lineno[20];
2352 sprintf(lineno, ":%d", line);
2353 location += lineno;
2354
2355 keep->add(rule, location);
2356}
2357
Joe Onorato1553c822009-08-30 13:36:22 -07002358status_t
2359writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2360{
2361 status_t err;
2362 ResXMLTree tree;
2363 size_t len;
2364 ResXMLTree::event_code_t code;
2365 int depth = 0;
2366 bool inApplication = false;
2367 String8 error;
2368 sp<AaptGroup> assGroup;
2369 sp<AaptFile> assFile;
2370 String8 pkg;
2371
2372 // First, look for a package file to parse. This is required to
2373 // be able to generate the resource information.
2374 assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
2375 if (assGroup == NULL) {
2376 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
2377 return -1;
2378 }
2379
2380 if (assGroup->getFiles().size() != 1) {
2381 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
2382 assGroup->getFiles().valueAt(0)->getPrintableSource().string());
2383 }
2384
2385 assFile = assGroup->getFiles().valueAt(0);
2386
2387 err = parseXMLResource(assFile, &tree);
2388 if (err != NO_ERROR) {
2389 return err;
2390 }
2391
2392 tree.restart();
2393
2394 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2395 if (code == ResXMLTree::END_TAG) {
2396 if (/* name == "Application" && */ depth == 2) {
2397 inApplication = false;
2398 }
2399 depth--;
2400 continue;
2401 }
2402 if (code != ResXMLTree::START_TAG) {
2403 continue;
2404 }
2405 depth++;
2406 String8 tag(tree.getElementName(&len));
2407 // printf("Depth %d tag %s\n", depth, tag.string());
Ying Wang46f4b982010-01-13 14:18:11 -08002408 bool keepTag = false;
Joe Onorato1553c822009-08-30 13:36:22 -07002409 if (depth == 1) {
2410 if (tag != "manifest") {
2411 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
2412 return -1;
2413 }
2414 pkg = getAttribute(tree, NULL, "package", NULL);
Ying Wang46f4b982010-01-13 14:18:11 -08002415 } else if (depth == 2) {
2416 if (tag == "application") {
2417 inApplication = true;
2418 keepTag = true;
Ying Wang561a9182010-08-13 13:56:07 -07002419
Dianne Hackbornb0381ef2010-03-03 13:36:35 -08002420 String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
2421 "backupAgent", &error);
2422 if (agent.length() > 0) {
2423 addProguardKeepRule(keep, agent, pkg.string(),
2424 assFile->getPrintableSource(), tree.getLineNumber());
2425 }
Ying Wang46f4b982010-01-13 14:18:11 -08002426 } else if (tag == "instrumentation") {
2427 keepTag = true;
2428 }
Joe Onorato1553c822009-08-30 13:36:22 -07002429 }
Ying Wang46f4b982010-01-13 14:18:11 -08002430 if (!keepTag && inApplication && depth == 3) {
2431 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
2432 keepTag = true;
2433 }
2434 }
2435 if (keepTag) {
2436 String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
2437 "name", &error);
2438 if (error != "") {
2439 fprintf(stderr, "ERROR: %s\n", error.string());
2440 return -1;
2441 }
2442 if (name.length() > 0) {
Dianne Hackbornb0381ef2010-03-03 13:36:35 -08002443 addProguardKeepRule(keep, name, pkg.string(),
2444 assFile->getPrintableSource(), tree.getLineNumber());
Joe Onorato1553c822009-08-30 13:36:22 -07002445 }
2446 }
2447 }
2448
2449 return NO_ERROR;
2450}
2451
Ying Wang561a9182010-08-13 13:56:07 -07002452struct NamespaceAttributePair {
2453 const char* ns;
2454 const char* attr;
2455
2456 NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
2457 NamespaceAttributePair() : ns(NULL), attr(NULL) {}
2458};
2459
Joe Onorato1553c822009-08-30 13:36:22 -07002460status_t
Dianne Hackbornabd03652010-03-02 14:56:51 -08002461writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
Xavier Ducrohet095cd2e2012-07-18 18:06:09 -07002462 const char* startTag, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
Joe Onorato1553c822009-08-30 13:36:22 -07002463{
2464 status_t err;
2465 ResXMLTree tree;
2466 size_t len;
2467 ResXMLTree::event_code_t code;
2468
2469 err = parseXMLResource(layoutFile, &tree);
2470 if (err != NO_ERROR) {
2471 return err;
2472 }
2473
2474 tree.restart();
2475
Dianne Hackbornabd03652010-03-02 14:56:51 -08002476 if (startTag != NULL) {
2477 bool haveStart = false;
2478 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2479 if (code != ResXMLTree::START_TAG) {
2480 continue;
2481 }
2482 String8 tag(tree.getElementName(&len));
2483 if (tag == startTag) {
2484 haveStart = true;
2485 }
2486 break;
2487 }
2488 if (!haveStart) {
2489 return NO_ERROR;
2490 }
2491 }
Ying Wang561a9182010-08-13 13:56:07 -07002492
Joe Onorato1553c822009-08-30 13:36:22 -07002493 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2494 if (code != ResXMLTree::START_TAG) {
2495 continue;
2496 }
2497 String8 tag(tree.getElementName(&len));
2498
2499 // If there is no '.', we'll assume that it's one of the built in names.
2500 if (strchr(tag.string(), '.')) {
Dianne Hackbornb0381ef2010-03-03 13:36:35 -08002501 addProguardKeepRule(keep, tag, NULL,
Dianne Hackbornabd03652010-03-02 14:56:51 -08002502 layoutFile->getPrintableSource(), tree.getLineNumber());
Ying Wang561a9182010-08-13 13:56:07 -07002503 } else if (tagAttrPairs != NULL) {
2504 ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
2505 if (tagIndex >= 0) {
Xavier Ducrohet095cd2e2012-07-18 18:06:09 -07002506 const Vector<NamespaceAttributePair>& nsAttrVector = tagAttrPairs->valueAt(tagIndex);
2507 for (size_t i = 0; i < nsAttrVector.size(); i++) {
2508 const NamespaceAttributePair& nsAttr = nsAttrVector[i];
2509
2510 ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
2511 if (attrIndex < 0) {
2512 // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
2513 // layoutFile->getPrintableSource().string(), tree.getLineNumber(),
2514 // tag.string(), nsAttr.ns, nsAttr.attr);
2515 } else {
2516 size_t len;
2517 addProguardKeepRule(keep,
2518 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
2519 layoutFile->getPrintableSource(), tree.getLineNumber());
2520 }
Ying Wang561a9182010-08-13 13:56:07 -07002521 }
Dianne Hackbornabd03652010-03-02 14:56:51 -08002522 }
Joe Onorato1553c822009-08-30 13:36:22 -07002523 }
Dianne Hackborn92751972012-05-18 19:22:14 -07002524 ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick");
2525 if (attrIndex >= 0) {
2526 size_t len;
2527 addProguardKeepMethodRule(keep,
2528 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
2529 layoutFile->getPrintableSource(), tree.getLineNumber());
2530 }
Joe Onorato1553c822009-08-30 13:36:22 -07002531 }
2532
2533 return NO_ERROR;
2534}
2535
Xavier Ducrohet095cd2e2012-07-18 18:06:09 -07002536static void addTagAttrPair(KeyedVector<String8, Vector<NamespaceAttributePair> >* dest,
Ying Wang561a9182010-08-13 13:56:07 -07002537 const char* tag, const char* ns, const char* attr) {
Xavier Ducrohet095cd2e2012-07-18 18:06:09 -07002538 String8 tagStr(tag);
2539 ssize_t index = dest->indexOfKey(tagStr);
2540
2541 if (index < 0) {
2542 Vector<NamespaceAttributePair> vector;
2543 vector.add(NamespaceAttributePair(ns, attr));
2544 dest->add(tagStr, vector);
2545 } else {
2546 dest->editValueAt(index).add(NamespaceAttributePair(ns, attr));
2547 }
Ying Wang561a9182010-08-13 13:56:07 -07002548}
2549
Joe Onorato1553c822009-08-30 13:36:22 -07002550status_t
2551writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2552{
2553 status_t err;
Ying Wang561a9182010-08-13 13:56:07 -07002554
2555 // tag:attribute pairs that should be checked in layout files.
Xavier Ducrohet095cd2e2012-07-18 18:06:09 -07002556 KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs;
Ying Wang561a9182010-08-13 13:56:07 -07002557 addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
Dianne Hackborn8a44bb22010-08-19 12:56:10 -07002558 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
Ying Wang561a9182010-08-13 13:56:07 -07002559 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
2560
2561 // tag:attribute pairs that should be checked in xml files.
Xavier Ducrohet095cd2e2012-07-18 18:06:09 -07002562 KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs;
Ying Wang561a9182010-08-13 13:56:07 -07002563 addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
Dianne Hackborn8a44bb22010-08-19 12:56:10 -07002564 addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
Ying Wang561a9182010-08-13 13:56:07 -07002565
Ying Wangc1112962010-01-20 22:12:46 -08002566 const Vector<sp<AaptDir> >& dirs = assets->resDirs();
2567 const size_t K = dirs.size();
2568 for (size_t k=0; k<K; k++) {
2569 const sp<AaptDir>& d = dirs.itemAt(k);
2570 const String8& dirName = d->getLeaf();
Dianne Hackbornabd03652010-03-02 14:56:51 -08002571 const char* startTag = NULL;
Xavier Ducrohet095cd2e2012-07-18 18:06:09 -07002572 const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL;
Dianne Hackbornabd03652010-03-02 14:56:51 -08002573 if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
Ying Wang561a9182010-08-13 13:56:07 -07002574 tagAttrPairs = &kLayoutTagAttrPairs;
Dianne Hackbornabd03652010-03-02 14:56:51 -08002575 } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
2576 startTag = "PreferenceScreen";
Ying Wang561a9182010-08-13 13:56:07 -07002577 tagAttrPairs = &kXmlTagAttrPairs;
Dianne Hackborn92751972012-05-18 19:22:14 -07002578 } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
2579 startTag = "menu";
2580 tagAttrPairs = NULL;
Dianne Hackbornabd03652010-03-02 14:56:51 -08002581 } else {
Ying Wangc1112962010-01-20 22:12:46 -08002582 continue;
2583 }
Ying Wang561a9182010-08-13 13:56:07 -07002584
Ying Wangc1112962010-01-20 22:12:46 -08002585 const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
Joe Onorato1553c822009-08-30 13:36:22 -07002586 const size_t N = groups.size();
2587 for (size_t i=0; i<N; i++) {
2588 const sp<AaptGroup>& group = groups.valueAt(i);
2589 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
2590 const size_t M = files.size();
2591 for (size_t j=0; j<M; j++) {
Ying Wang561a9182010-08-13 13:56:07 -07002592 err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
Joe Onorato1553c822009-08-30 13:36:22 -07002593 if (err < 0) {
2594 return err;
2595 }
2596 }
2597 }
2598 }
Ying Wang45ccfa52011-06-20 15:41:08 -07002599 // Handle the overlays
2600 sp<AaptAssets> overlay = assets->getOverlay();
2601 if (overlay.get()) {
2602 return writeProguardForLayouts(keep, overlay);
2603 }
Xavier Ducrohet095cd2e2012-07-18 18:06:09 -07002604
Joe Onorato1553c822009-08-30 13:36:22 -07002605 return NO_ERROR;
2606}
2607
2608status_t
2609writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
2610{
2611 status_t err = -1;
2612
2613 if (!bundle->getProguardFile()) {
2614 return NO_ERROR;
2615 }
2616
2617 ProguardKeepSet keep;
2618
2619 err = writeProguardForAndroidManifest(&keep, assets);
2620 if (err < 0) {
2621 return err;
2622 }
2623
2624 err = writeProguardForLayouts(&keep, assets);
2625 if (err < 0) {
2626 return err;
2627 }
2628
2629 FILE* fp = fopen(bundle->getProguardFile(), "w+");
2630 if (fp == NULL) {
2631 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
2632 bundle->getProguardFile(), strerror(errno));
2633 return UNKNOWN_ERROR;
2634 }
2635
2636 const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
2637 const size_t N = rules.size();
2638 for (size_t i=0; i<N; i++) {
2639 const SortedVector<String8>& locations = rules.valueAt(i);
2640 const size_t M = locations.size();
2641 for (size_t j=0; j<M; j++) {
2642 fprintf(fp, "# %s\n", locations.itemAt(j).string());
2643 }
2644 fprintf(fp, "%s\n\n", rules.keyAt(i).string());
2645 }
2646 fclose(fp);
2647
2648 return err;
2649}
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002650
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002651// Loops through the string paths and writes them to the file pointer
2652// Each file path is written on its own line with a terminating backslash.
2653status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002654{
2655 status_t deps = -1;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002656 for (size_t file_i = 0; file_i < files->size(); ++file_i) {
2657 // Add the full file path to the dependency file
2658 fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
2659 deps++;
2660 }
2661 return deps;
Xavier Ducrohet91398682011-07-19 10:18:28 -07002662}
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002663
2664status_t
Andreas Gampe2412f842014-09-30 20:55:57 -07002665writeDependencyPreReqs(Bundle* /* bundle */, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw)
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002666{
2667 status_t deps = -1;
2668 deps += writePathsToFile(assets->getFullResPaths(), fp);
2669 if (includeRaw) {
2670 deps += writePathsToFile(assets->getFullAssetPaths(), fp);
2671 }
2672 return deps;
2673}