1 //
2 // Copyright 2006 The Android Open Source Project
3 //
4 // Information about assets being operated on.
5 //
6 #ifndef __AAPT_ASSETS_H
7 #define __AAPT_ASSETS_H
8
9 #include <stdlib.h>
10 #include <androidfw/AssetManager.h>
11 #include <androidfw/ResourceTypes.h>
12 #include <utils/KeyedVector.h>
13 #include <utils/RefBase.h>
14 #include <utils/SortedVector.h>
15 #include <utils/String8.h>
16 #include <utils/String8.h>
17 #include <utils/Vector.h>
18 #include "ZipFile.h"
19
20 #include "Bundle.h"
21 #include "SourcePos.h"
22
23 using namespace android;
24
25
26 extern const char * const gDefaultIgnoreAssets;
27 extern const char * gUserIgnoreAssets;
28
29 bool valid_symbol_name(const String8& str);
30
31 class AaptAssets;
32
33 enum {
34 AXIS_NONE = 0,
35 AXIS_MCC = 1,
36 AXIS_MNC,
37 AXIS_LANGUAGE,
38 AXIS_REGION,
39 AXIS_SCREENLAYOUTSIZE,
40 AXIS_SCREENLAYOUTLONG,
41 AXIS_ORIENTATION,
42 AXIS_UIMODETYPE,
43 AXIS_UIMODENIGHT,
44 AXIS_DENSITY,
45 AXIS_TOUCHSCREEN,
46 AXIS_KEYSHIDDEN,
47 AXIS_KEYBOARD,
48 AXIS_NAVHIDDEN,
49 AXIS_NAVIGATION,
50 AXIS_SCREENSIZE,
51 AXIS_SMALLESTSCREENWIDTHDP,
52 AXIS_SCREENWIDTHDP,
53 AXIS_SCREENHEIGHTDP,
54 AXIS_VERSION,
55
56 AXIS_START = AXIS_MCC,
57 AXIS_END = AXIS_VERSION,
58 };
59
60 /**
61 * This structure contains a specific variation of a single file out
62 * of all the variations it can have that we can have.
63 */
64 struct AaptGroupEntry
65 {
66 public:
AaptGroupEntryAaptGroupEntry67 AaptGroupEntry() : mParamsChanged(true) { }
AaptGroupEntryAaptGroupEntry68 AaptGroupEntry(const String8& _locale, const String8& _vendor)
69 : locale(_locale), vendor(_vendor), mParamsChanged(true) { }
70
71 bool initFromDirName(const char* dir, String8* resType);
72
73 static status_t parseNamePart(const String8& part, int* axis, uint32_t* value);
74
75 static uint32_t getConfigValueForAxis(const ResTable_config& config, int axis);
76
77 static bool configSameExcept(const ResTable_config& config,
78 const ResTable_config& otherConfig, int axis);
79
80 static bool getMccName(const char* name, ResTable_config* out = NULL);
81 static bool getMncName(const char* name, ResTable_config* out = NULL);
82 static bool getLocaleName(const char* name, ResTable_config* out = NULL);
83 static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL);
84 static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL);
85 static bool getOrientationName(const char* name, ResTable_config* out = NULL);
86 static bool getUiModeTypeName(const char* name, ResTable_config* out = NULL);
87 static bool getUiModeNightName(const char* name, ResTable_config* out = NULL);
88 static bool getDensityName(const char* name, ResTable_config* out = NULL);
89 static bool getTouchscreenName(const char* name, ResTable_config* out = NULL);
90 static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL);
91 static bool getKeyboardName(const char* name, ResTable_config* out = NULL);
92 static bool getNavigationName(const char* name, ResTable_config* out = NULL);
93 static bool getNavHiddenName(const char* name, ResTable_config* out = NULL);
94 static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
95 static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL);
96 static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL);
97 static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL);
98 static bool getVersionName(const char* name, ResTable_config* out = NULL);
99
100 int compare(const AaptGroupEntry& o) const;
101
102 const ResTable_config& toParams() const;
103
104 inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
105 inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
106 inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
107 inline bool operator!=(const AaptGroupEntry& o) const { return compare(o) != 0; }
108 inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
109 inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
110
111 String8 toString() const;
112 String8 toDirName(const String8& resType) const;
113
getVersionStringAaptGroupEntry114 const String8& getVersionString() const { return version; }
115
116 private:
117 String8 mcc;
118 String8 mnc;
119 String8 locale;
120 String8 vendor;
121 String8 smallestScreenWidthDp;
122 String8 screenWidthDp;
123 String8 screenHeightDp;
124 String8 screenLayoutSize;
125 String8 screenLayoutLong;
126 String8 orientation;
127 String8 uiModeType;
128 String8 uiModeNight;
129 String8 density;
130 String8 touchscreen;
131 String8 keysHidden;
132 String8 keyboard;
133 String8 navHidden;
134 String8 navigation;
135 String8 screenSize;
136 String8 version;
137
138 mutable bool mParamsChanged;
139 mutable ResTable_config mParams;
140 };
141
compare_type(const AaptGroupEntry & lhs,const AaptGroupEntry & rhs)142 inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
143 {
144 return lhs.compare(rhs);
145 }
146
strictly_order_type(const AaptGroupEntry & lhs,const AaptGroupEntry & rhs)147 inline int strictly_order_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
148 {
149 return compare_type(lhs, rhs) < 0;
150 }
151
152 class AaptGroup;
153 class FilePathStore;
154
155 /**
156 * A single asset file we know about.
157 */
158 class AaptFile : public RefBase
159 {
160 public:
AaptFile(const String8 & sourceFile,const AaptGroupEntry & groupEntry,const String8 & resType)161 AaptFile(const String8& sourceFile, const AaptGroupEntry& groupEntry,
162 const String8& resType)
163 : mGroupEntry(groupEntry)
164 , mResourceType(resType)
165 , mSourceFile(sourceFile)
166 , mData(NULL)
167 , mDataSize(0)
168 , mBufferSize(0)
169 , mCompression(ZipEntry::kCompressStored)
170 {
171 //printf("new AaptFile created %s\n", (const char*)sourceFile);
172 }
~AaptFile()173 virtual ~AaptFile() {
174 free(mData);
175 }
176
getPath()177 const String8& getPath() const { return mPath; }
getGroupEntry()178 const AaptGroupEntry& getGroupEntry() const { return mGroupEntry; }
179
180 // Data API. If there is data attached to the file,
181 // getSourceFile() is not used.
hasData()182 bool hasData() const { return mData != NULL; }
getData()183 const void* getData() const { return mData; }
getSize()184 size_t getSize() const { return mDataSize; }
185 void* editData(size_t size);
186 void* editData(size_t* outSize = NULL);
187 void* padData(size_t wordSize);
188 status_t writeData(const void* data, size_t size);
189 void clearData();
190
getResourceType()191 const String8& getResourceType() const { return mResourceType; }
192
193 // File API. If the file does not hold raw data, this is
194 // a full path to a file on the filesystem that holds its data.
getSourceFile()195 const String8& getSourceFile() const { return mSourceFile; }
196
197 String8 getPrintableSource() const;
198
199 // Desired compression method, as per utils/ZipEntry.h. For example,
200 // no compression is ZipEntry::kCompressStored.
getCompressionMethod()201 int getCompressionMethod() const { return mCompression; }
setCompressionMethod(int c)202 void setCompressionMethod(int c) { mCompression = c; }
203 private:
204 friend class AaptGroup;
205
206 String8 mPath;
207 AaptGroupEntry mGroupEntry;
208 String8 mResourceType;
209 String8 mSourceFile;
210 void* mData;
211 size_t mDataSize;
212 size_t mBufferSize;
213 int mCompression;
214 };
215
216 /**
217 * A group of related files (the same file, with different
218 * vendor/locale variations).
219 */
220 class AaptGroup : public RefBase
221 {
222 public:
AaptGroup(const String8 & leaf,const String8 & path)223 AaptGroup(const String8& leaf, const String8& path)
224 : mLeaf(leaf), mPath(path) { }
~AaptGroup()225 virtual ~AaptGroup() { }
226
getLeaf()227 const String8& getLeaf() const { return mLeaf; }
228
229 // Returns the relative path after the AaptGroupEntry dirs.
getPath()230 const String8& getPath() const { return mPath; }
231
getFiles()232 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& getFiles() const
233 { return mFiles; }
234
235 status_t addFile(const sp<AaptFile>& file);
236 void removeFile(size_t index);
237
238 void print(const String8& prefix) const;
239
240 String8 getPrintableSource() const;
241
242 private:
243 String8 mLeaf;
244 String8 mPath;
245
246 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > mFiles;
247 };
248
249 /**
250 * A single directory of assets, which can contain files and other
251 * sub-directories.
252 */
253 class AaptDir : public RefBase
254 {
255 public:
AaptDir(const String8 & leaf,const String8 & path)256 AaptDir(const String8& leaf, const String8& path)
257 : mLeaf(leaf), mPath(path) { }
~AaptDir()258 virtual ~AaptDir() { }
259
getLeaf()260 const String8& getLeaf() const { return mLeaf; }
261
getPath()262 const String8& getPath() const { return mPath; }
263
getFiles()264 const DefaultKeyedVector<String8, sp<AaptGroup> >& getFiles() const { return mFiles; }
getDirs()265 const DefaultKeyedVector<String8, sp<AaptDir> >& getDirs() const { return mDirs; }
266
267 virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
268
269 void removeFile(const String8& name);
270 void removeDir(const String8& name);
271
272 /*
273 * Perform some sanity checks on the names of files and directories here.
274 * In particular:
275 * - Check for illegal chars in filenames.
276 * - Check filename length.
277 * - Check for presence of ".gz" and non-".gz" copies of same file.
278 * - Check for multiple files whose names match in a case-insensitive
279 * fashion (problematic for some systems).
280 *
281 * Comparing names against all other names is O(n^2). We could speed
282 * it up some by sorting the entries and being smarter about what we
283 * compare against, but I'm not expecting to have enough files in a
284 * single directory to make a noticeable difference in speed.
285 *
286 * Note that sorting here is not enough to guarantee that the package
287 * contents are sorted -- subsequent updates can rearrange things.
288 */
289 status_t validate() const;
290
291 void print(const String8& prefix) const;
292
293 String8 getPrintableSource() const;
294
295 private:
296 friend class AaptAssets;
297
298 status_t addDir(const String8& name, const sp<AaptDir>& dir);
299 sp<AaptDir> makeDir(const String8& name);
300 status_t addLeafFile(const String8& leafName,
301 const sp<AaptFile>& file);
302 virtual ssize_t slurpFullTree(Bundle* bundle,
303 const String8& srcDir,
304 const AaptGroupEntry& kind,
305 const String8& resType,
306 sp<FilePathStore>& fullResPaths);
307
308 String8 mLeaf;
309 String8 mPath;
310
311 DefaultKeyedVector<String8, sp<AaptGroup> > mFiles;
312 DefaultKeyedVector<String8, sp<AaptDir> > mDirs;
313 };
314
315 /**
316 * All information we know about a particular symbol.
317 */
318 class AaptSymbolEntry
319 {
320 public:
AaptSymbolEntry()321 AaptSymbolEntry()
322 : isPublic(false), isJavaSymbol(false), typeCode(TYPE_UNKNOWN)
323 {
324 }
AaptSymbolEntry(const String8 & _name)325 AaptSymbolEntry(const String8& _name)
326 : name(_name), isPublic(false), isJavaSymbol(false), typeCode(TYPE_UNKNOWN)
327 {
328 }
AaptSymbolEntry(const AaptSymbolEntry & o)329 AaptSymbolEntry(const AaptSymbolEntry& o)
330 : name(o.name), sourcePos(o.sourcePos), isPublic(o.isPublic)
331 , isJavaSymbol(o.isJavaSymbol), comment(o.comment), typeComment(o.typeComment)
332 , typeCode(o.typeCode), int32Val(o.int32Val), stringVal(o.stringVal)
333 {
334 }
335 AaptSymbolEntry operator=(const AaptSymbolEntry& o)
336 {
337 sourcePos = o.sourcePos;
338 isPublic = o.isPublic;
339 isJavaSymbol = o.isJavaSymbol;
340 comment = o.comment;
341 typeComment = o.typeComment;
342 typeCode = o.typeCode;
343 int32Val = o.int32Val;
344 stringVal = o.stringVal;
345 return *this;
346 }
347
348 const String8 name;
349
350 SourcePos sourcePos;
351 bool isPublic;
352 bool isJavaSymbol;
353
354 String16 comment;
355 String16 typeComment;
356
357 enum {
358 TYPE_UNKNOWN = 0,
359 TYPE_INT32,
360 TYPE_STRING
361 };
362
363 int typeCode;
364
365 // Value. May be one of these.
366 int32_t int32Val;
367 String8 stringVal;
368 };
369
370 /**
371 * A group of related symbols (such as indices into a string block)
372 * that have been generated from the assets.
373 */
374 class AaptSymbols : public RefBase
375 {
376 public:
AaptSymbols()377 AaptSymbols() { }
~AaptSymbols()378 virtual ~AaptSymbols() { }
379
addSymbol(const String8 & name,int32_t value,const SourcePos & pos)380 status_t addSymbol(const String8& name, int32_t value, const SourcePos& pos) {
381 if (!check_valid_symbol_name(name, pos, "symbol")) {
382 return BAD_VALUE;
383 }
384 AaptSymbolEntry& sym = edit_symbol(name, &pos);
385 sym.typeCode = AaptSymbolEntry::TYPE_INT32;
386 sym.int32Val = value;
387 return NO_ERROR;
388 }
389
addStringSymbol(const String8 & name,const String8 & value,const SourcePos & pos)390 status_t addStringSymbol(const String8& name, const String8& value,
391 const SourcePos& pos) {
392 if (!check_valid_symbol_name(name, pos, "symbol")) {
393 return BAD_VALUE;
394 }
395 AaptSymbolEntry& sym = edit_symbol(name, &pos);
396 sym.typeCode = AaptSymbolEntry::TYPE_STRING;
397 sym.stringVal = value;
398 return NO_ERROR;
399 }
400
makeSymbolPublic(const String8 & name,const SourcePos & pos)401 status_t makeSymbolPublic(const String8& name, const SourcePos& pos) {
402 if (!check_valid_symbol_name(name, pos, "symbol")) {
403 return BAD_VALUE;
404 }
405 AaptSymbolEntry& sym = edit_symbol(name, &pos);
406 sym.isPublic = true;
407 return NO_ERROR;
408 }
409
makeSymbolJavaSymbol(const String8 & name,const SourcePos & pos)410 status_t makeSymbolJavaSymbol(const String8& name, const SourcePos& pos) {
411 if (!check_valid_symbol_name(name, pos, "symbol")) {
412 return BAD_VALUE;
413 }
414 AaptSymbolEntry& sym = edit_symbol(name, &pos);
415 sym.isJavaSymbol = true;
416 return NO_ERROR;
417 }
418
appendComment(const String8 & name,const String16 & comment,const SourcePos & pos)419 void appendComment(const String8& name, const String16& comment, const SourcePos& pos) {
420 if (comment.size() <= 0) {
421 return;
422 }
423 AaptSymbolEntry& sym = edit_symbol(name, &pos);
424 if (sym.comment.size() == 0) {
425 sym.comment = comment;
426 } else {
427 sym.comment.append(String16("\n"));
428 sym.comment.append(comment);
429 }
430 }
431
appendTypeComment(const String8 & name,const String16 & comment)432 void appendTypeComment(const String8& name, const String16& comment) {
433 if (comment.size() <= 0) {
434 return;
435 }
436 AaptSymbolEntry& sym = edit_symbol(name, NULL);
437 if (sym.typeComment.size() == 0) {
438 sym.typeComment = comment;
439 } else {
440 sym.typeComment.append(String16("\n"));
441 sym.typeComment.append(comment);
442 }
443 }
444
addNestedSymbol(const String8 & name,const SourcePos & pos)445 sp<AaptSymbols> addNestedSymbol(const String8& name, const SourcePos& pos) {
446 if (!check_valid_symbol_name(name, pos, "nested symbol")) {
447 return NULL;
448 }
449
450 sp<AaptSymbols> sym = mNestedSymbols.valueFor(name);
451 if (sym == NULL) {
452 sym = new AaptSymbols();
453 mNestedSymbols.add(name, sym);
454 }
455
456 return sym;
457 }
458
459 status_t applyJavaSymbols(const sp<AaptSymbols>& javaSymbols);
460
getSymbols()461 const KeyedVector<String8, AaptSymbolEntry>& getSymbols() const
462 { return mSymbols; }
getNestedSymbols()463 const DefaultKeyedVector<String8, sp<AaptSymbols> >& getNestedSymbols() const
464 { return mNestedSymbols; }
465
getComment(const String8 & name)466 const String16& getComment(const String8& name) const
467 { return get_symbol(name).comment; }
getTypeComment(const String8 & name)468 const String16& getTypeComment(const String8& name) const
469 { return get_symbol(name).typeComment; }
470
471 private:
check_valid_symbol_name(const String8 & symbol,const SourcePos & pos,const char * label)472 bool check_valid_symbol_name(const String8& symbol, const SourcePos& pos, const char* label) {
473 if (valid_symbol_name(symbol)) {
474 return true;
475 }
476 pos.error("invalid %s: '%s'\n", label, symbol.string());
477 return false;
478 }
edit_symbol(const String8 & symbol,const SourcePos * pos)479 AaptSymbolEntry& edit_symbol(const String8& symbol, const SourcePos* pos) {
480 ssize_t i = mSymbols.indexOfKey(symbol);
481 if (i < 0) {
482 i = mSymbols.add(symbol, AaptSymbolEntry(symbol));
483 }
484 AaptSymbolEntry& sym = mSymbols.editValueAt(i);
485 if (pos != NULL && sym.sourcePos.line < 0) {
486 sym.sourcePos = *pos;
487 }
488 return sym;
489 }
get_symbol(const String8 & symbol)490 const AaptSymbolEntry& get_symbol(const String8& symbol) const {
491 ssize_t i = mSymbols.indexOfKey(symbol);
492 if (i >= 0) {
493 return mSymbols.valueAt(i);
494 }
495 return mDefSymbol;
496 }
497
498 KeyedVector<String8, AaptSymbolEntry> mSymbols;
499 DefaultKeyedVector<String8, sp<AaptSymbols> > mNestedSymbols;
500 AaptSymbolEntry mDefSymbol;
501 };
502
503 class ResourceTypeSet : public RefBase,
504 public KeyedVector<String8,sp<AaptGroup> >
505 {
506 public:
507 ResourceTypeSet();
508 };
509
510 // Storage for lists of fully qualified paths for
511 // resources encountered during slurping.
512 class FilePathStore : public RefBase,
513 public Vector<String8>
514 {
515 public:
516 FilePathStore();
517 };
518
519 /**
520 * Asset hierarchy being operated on.
521 */
522 class AaptAssets : public AaptDir
523 {
524 public:
525 AaptAssets();
~AaptAssets()526 virtual ~AaptAssets() { delete mRes; }
527
getPackage()528 const String8& getPackage() const { return mPackage; }
setPackage(const String8 & package)529 void setPackage(const String8& package) {
530 mPackage = package;
531 mSymbolsPrivatePackage = package;
532 mHavePrivateSymbols = false;
533 }
534
535 const SortedVector<AaptGroupEntry>& getGroupEntries() const;
536
537 virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
538
539 sp<AaptFile> addFile(const String8& filePath,
540 const AaptGroupEntry& entry,
541 const String8& srcDir,
542 sp<AaptGroup>* outGroup,
543 const String8& resType);
544
545 void addResource(const String8& leafName,
546 const String8& path,
547 const sp<AaptFile>& file,
548 const String8& resType);
549
addGroupEntry(const AaptGroupEntry & entry)550 void addGroupEntry(const AaptGroupEntry& entry) { mGroupEntries.add(entry); }
551
552 ssize_t slurpFromArgs(Bundle* bundle);
553
554 sp<AaptSymbols> getSymbolsFor(const String8& name);
555
556 sp<AaptSymbols> getJavaSymbolsFor(const String8& name);
557
558 status_t applyJavaSymbols();
559
getSymbols()560 const DefaultKeyedVector<String8, sp<AaptSymbols> >& getSymbols() const { return mSymbols; }
561
getSymbolsPrivatePackage()562 String8 getSymbolsPrivatePackage() const { return mSymbolsPrivatePackage; }
setSymbolsPrivatePackage(const String8 & pkg)563 void setSymbolsPrivatePackage(const String8& pkg) {
564 mSymbolsPrivatePackage = pkg;
565 mHavePrivateSymbols = mSymbolsPrivatePackage != mPackage;
566 }
567
havePrivateSymbols()568 bool havePrivateSymbols() const { return mHavePrivateSymbols; }
569
570 bool isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const;
571
572 status_t buildIncludedResources(Bundle* bundle);
573 status_t addIncludedResources(const sp<AaptFile>& file);
574 const ResTable& getIncludedResources() const;
575
576 void print(const String8& prefix) const;
577
resDirs()578 inline const Vector<sp<AaptDir> >& resDirs() const { return mResDirs; }
579 sp<AaptDir> resDir(const String8& name) const;
580
getOverlay()581 inline sp<AaptAssets> getOverlay() { return mOverlay; }
setOverlay(sp<AaptAssets> & overlay)582 inline void setOverlay(sp<AaptAssets>& overlay) { mOverlay = overlay; }
583
getResources()584 inline KeyedVector<String8, sp<ResourceTypeSet> >* getResources() { return mRes; }
585 inline void
setResources(KeyedVector<String8,sp<ResourceTypeSet>> * res)586 setResources(KeyedVector<String8, sp<ResourceTypeSet> >* res) { delete mRes; mRes = res; }
587
getFullResPaths()588 inline sp<FilePathStore>& getFullResPaths() { return mFullResPaths; }
589 inline void
setFullResPaths(sp<FilePathStore> & res)590 setFullResPaths(sp<FilePathStore>& res) { mFullResPaths = res; }
591
getFullAssetPaths()592 inline sp<FilePathStore>& getFullAssetPaths() { return mFullAssetPaths; }
593 inline void
setFullAssetPaths(sp<FilePathStore> & res)594 setFullAssetPaths(sp<FilePathStore>& res) { mFullAssetPaths = res; }
595
596 private:
597 virtual ssize_t slurpFullTree(Bundle* bundle,
598 const String8& srcDir,
599 const AaptGroupEntry& kind,
600 const String8& resType,
601 sp<FilePathStore>& fullResPaths);
602
603 ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
604 ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
605
606 status_t filter(Bundle* bundle);
607
608 String8 mPackage;
609 SortedVector<AaptGroupEntry> mGroupEntries;
610 DefaultKeyedVector<String8, sp<AaptSymbols> > mSymbols;
611 DefaultKeyedVector<String8, sp<AaptSymbols> > mJavaSymbols;
612 String8 mSymbolsPrivatePackage;
613 bool mHavePrivateSymbols;
614
615 Vector<sp<AaptDir> > mResDirs;
616
617 bool mChanged;
618
619 bool mHaveIncludedAssets;
620 AssetManager mIncludedAssets;
621
622 sp<AaptAssets> mOverlay;
623 KeyedVector<String8, sp<ResourceTypeSet> >* mRes;
624
625 sp<FilePathStore> mFullResPaths;
626 sp<FilePathStore> mFullAssetPaths;
627 };
628
629 #endif // __AAPT_ASSETS_H
630
631