1 //
2 // Copyright 2006 The Android Open Source Project
3 //
4 // Android Asset Packaging Tool main entry point.
5 //
6 #include "AaptXml.h"
7 #include "ApkBuilder.h"
8 #include "Bundle.h"
9 #include "Images.h"
10 #include "Main.h"
11 #include "ResourceFilter.h"
12 #include "ResourceTable.h"
13 #include "XMLNode.h"
14
15 #include <utils/Errors.h>
16 #include <utils/KeyedVector.h>
17 #include <utils/List.h>
18 #include <utils/Log.h>
19 #include <utils/SortedVector.h>
20 #include <utils/threads.h>
21 #include <utils/Vector.h>
22
23 #include <errno.h>
24 #include <fcntl.h>
25
26 #include <iostream>
27 #include <string>
28 #include <sstream>
29
30 using namespace android;
31
32 #ifndef AAPT_VERSION
33 #define AAPT_VERSION ""
34 #endif
35
36 /*
37 * Show version info. All the cool kids do it.
38 */
doVersion(Bundle * bundle)39 int doVersion(Bundle* bundle)
40 {
41 if (bundle->getFileSpecCount() != 0) {
42 printf("(ignoring extra arguments)\n");
43 }
44 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
45
46 return 0;
47 }
48
49
50 /*
51 * Open the file read only. The call fails if the file doesn't exist.
52 *
53 * Returns NULL on failure.
54 */
openReadOnly(const char * fileName)55 ZipFile* openReadOnly(const char* fileName)
56 {
57 ZipFile* zip;
58 status_t result;
59
60 zip = new ZipFile;
61 result = zip->open(fileName, ZipFile::kOpenReadOnly);
62 if (result != NO_ERROR) {
63 if (result == NAME_NOT_FOUND) {
64 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
65 } else if (result == PERMISSION_DENIED) {
66 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
67 } else {
68 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
69 fileName);
70 }
71 delete zip;
72 return NULL;
73 }
74
75 return zip;
76 }
77
78 /*
79 * Open the file read-write. The file will be created if it doesn't
80 * already exist and "okayToCreate" is set.
81 *
82 * Returns NULL on failure.
83 */
openReadWrite(const char * fileName,bool okayToCreate)84 ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
85 {
86 ZipFile* zip = NULL;
87 status_t result;
88 int flags;
89
90 flags = ZipFile::kOpenReadWrite;
91 if (okayToCreate) {
92 flags |= ZipFile::kOpenCreate;
93 }
94
95 zip = new ZipFile;
96 result = zip->open(fileName, flags);
97 if (result != NO_ERROR) {
98 delete zip;
99 zip = NULL;
100 goto bail;
101 }
102
103 bail:
104 return zip;
105 }
106
107
108 /*
109 * Return a short string describing the compression method.
110 */
compressionName(int method)111 const char* compressionName(int method)
112 {
113 if (method == ZipEntry::kCompressStored) {
114 return "Stored";
115 } else if (method == ZipEntry::kCompressDeflated) {
116 return "Deflated";
117 } else {
118 return "Unknown";
119 }
120 }
121
122 /*
123 * Return the percent reduction in size (0% == no compression).
124 */
calcPercent(long uncompressedLen,long compressedLen)125 int calcPercent(long uncompressedLen, long compressedLen)
126 {
127 if (!uncompressedLen) {
128 return 0;
129 } else {
130 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
131 }
132 }
133
134 /*
135 * Handle the "list" command, which can be a simple file dump or
136 * a verbose listing.
137 *
138 * The verbose listing closely matches the output of the Info-ZIP "unzip"
139 * command.
140 */
doList(Bundle * bundle)141 int doList(Bundle* bundle)
142 {
143 int result = 1;
144 ZipFile* zip = NULL;
145 const ZipEntry* entry;
146 long totalUncLen, totalCompLen;
147 const char* zipFileName;
148
149 if (bundle->getFileSpecCount() != 1) {
150 fprintf(stderr, "ERROR: specify zip file name (only)\n");
151 goto bail;
152 }
153 zipFileName = bundle->getFileSpecEntry(0);
154
155 zip = openReadOnly(zipFileName);
156 if (zip == NULL) {
157 goto bail;
158 }
159
160 int count, i;
161
162 if (bundle->getVerbose()) {
163 printf("Archive: %s\n", zipFileName);
164 printf(
165 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
166 printf(
167 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
168 }
169
170 totalUncLen = totalCompLen = 0;
171
172 count = zip->getNumEntries();
173 for (i = 0; i < count; i++) {
174 entry = zip->getEntryByIndex(i);
175 if (bundle->getVerbose()) {
176 char dateBuf[32];
177 time_t when;
178
179 when = entry->getModWhen();
180 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
181 localtime(&when));
182
183 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
184 (long) entry->getUncompressedLen(),
185 compressionName(entry->getCompressionMethod()),
186 (long) entry->getCompressedLen(),
187 calcPercent(entry->getUncompressedLen(),
188 entry->getCompressedLen()),
189 (size_t) entry->getLFHOffset(),
190 dateBuf,
191 entry->getCRC32(),
192 entry->getFileName());
193 } else {
194 printf("%s\n", entry->getFileName());
195 }
196
197 totalUncLen += entry->getUncompressedLen();
198 totalCompLen += entry->getCompressedLen();
199 }
200
201 if (bundle->getVerbose()) {
202 printf(
203 "-------- ------- --- -------\n");
204 printf("%8ld %7ld %2d%% %d files\n",
205 totalUncLen,
206 totalCompLen,
207 calcPercent(totalUncLen, totalCompLen),
208 zip->getNumEntries());
209 }
210
211 if (bundle->getAndroidList()) {
212 AssetManager assets;
213 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
214 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
215 goto bail;
216 }
217
218 #ifdef HAVE_ANDROID_OS
219 static const bool kHaveAndroidOs = true;
220 #else
221 static const bool kHaveAndroidOs = false;
222 #endif
223 const ResTable& res = assets.getResources(false);
224 if (!kHaveAndroidOs) {
225 printf("\nResource table:\n");
226 res.print(false);
227 }
228
229 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
230 Asset::ACCESS_BUFFER);
231 if (manifestAsset == NULL) {
232 printf("\nNo AndroidManifest.xml found.\n");
233 } else {
234 printf("\nAndroid manifest:\n");
235 ResXMLTree tree;
236 tree.setTo(manifestAsset->getBuffer(true),
237 manifestAsset->getLength());
238 printXMLBlock(&tree);
239 }
240 delete manifestAsset;
241 }
242
243 result = 0;
244
245 bail:
246 delete zip;
247 return result;
248 }
249
printResolvedResourceAttribute(const ResTable & resTable,const ResXMLTree & tree,uint32_t attrRes,String8 attrLabel,String8 * outError)250 static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
251 uint32_t attrRes, String8 attrLabel, String8* outError)
252 {
253 Res_value value;
254 AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
255 if (*outError != "") {
256 *outError = "error print resolved resource attribute";
257 return;
258 }
259 if (value.dataType == Res_value::TYPE_STRING) {
260 String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
261 printf("%s='%s'", attrLabel.string(),
262 ResTable::normalizeForOutput(result.string()).string());
263 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
264 value.dataType <= Res_value::TYPE_LAST_INT) {
265 printf("%s='%d'", attrLabel.string(), value.data);
266 } else {
267 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
268 }
269 }
270
271 // These are attribute resource constants for the platform, as found
272 // in android.R.attr
273 enum {
274 LABEL_ATTR = 0x01010001,
275 ICON_ATTR = 0x01010002,
276 NAME_ATTR = 0x01010003,
277 PERMISSION_ATTR = 0x01010006,
278 EXPORTED_ATTR = 0x01010010,
279 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
280 RESOURCE_ATTR = 0x01010025,
281 DEBUGGABLE_ATTR = 0x0101000f,
282 VALUE_ATTR = 0x01010024,
283 VERSION_CODE_ATTR = 0x0101021b,
284 VERSION_NAME_ATTR = 0x0101021c,
285 SCREEN_ORIENTATION_ATTR = 0x0101001e,
286 MIN_SDK_VERSION_ATTR = 0x0101020c,
287 MAX_SDK_VERSION_ATTR = 0x01010271,
288 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
289 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
290 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
291 REQ_NAVIGATION_ATTR = 0x0101022a,
292 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
293 TARGET_SDK_VERSION_ATTR = 0x01010270,
294 TEST_ONLY_ATTR = 0x01010272,
295 ANY_DENSITY_ATTR = 0x0101026c,
296 GL_ES_VERSION_ATTR = 0x01010281,
297 SMALL_SCREEN_ATTR = 0x01010284,
298 NORMAL_SCREEN_ATTR = 0x01010285,
299 LARGE_SCREEN_ATTR = 0x01010286,
300 XLARGE_SCREEN_ATTR = 0x010102bf,
301 REQUIRED_ATTR = 0x0101028e,
302 INSTALL_LOCATION_ATTR = 0x010102b7,
303 SCREEN_SIZE_ATTR = 0x010102ca,
304 SCREEN_DENSITY_ATTR = 0x010102cb,
305 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
306 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
307 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
308 PUBLIC_KEY_ATTR = 0x010103a6,
309 CATEGORY_ATTR = 0x010103e8,
310 BANNER_ATTR = 0x10103f2,
311 ISGAME_ATTR = 0x10103f4,
312 };
313
getComponentName(String8 & pkgName,String8 & componentName)314 String8 getComponentName(String8 &pkgName, String8 &componentName) {
315 ssize_t idx = componentName.find(".");
316 String8 retStr(pkgName);
317 if (idx == 0) {
318 retStr += componentName;
319 } else if (idx < 0) {
320 retStr += ".";
321 retStr += componentName;
322 } else {
323 return componentName;
324 }
325 return retStr;
326 }
327
printCompatibleScreens(ResXMLTree & tree,String8 * outError)328 static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
329 size_t len;
330 ResXMLTree::event_code_t code;
331 int depth = 0;
332 bool first = true;
333 printf("compatible-screens:");
334 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
335 if (code == ResXMLTree::END_TAG) {
336 depth--;
337 if (depth < 0) {
338 break;
339 }
340 continue;
341 }
342 if (code != ResXMLTree::START_TAG) {
343 continue;
344 }
345 depth++;
346 const char16_t* ctag16 = tree.getElementName(&len);
347 if (ctag16 == NULL) {
348 *outError = "failed to get XML element name (bad string pool)";
349 return;
350 }
351 String8 tag(ctag16);
352 if (tag == "screen") {
353 int32_t screenSize = AaptXml::getIntegerAttribute(tree,
354 SCREEN_SIZE_ATTR);
355 int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
356 SCREEN_DENSITY_ATTR);
357 if (screenSize > 0 && screenDensity > 0) {
358 if (!first) {
359 printf(",");
360 }
361 first = false;
362 printf("'%d/%d'", screenSize, screenDensity);
363 }
364 }
365 }
366 printf("\n");
367 }
368
printUsesPermission(const String8 & name,bool optional=false,int maxSdkVersion=-1)369 static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
370 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
371 if (maxSdkVersion != -1) {
372 printf(" maxSdkVersion='%d'", maxSdkVersion);
373 }
374 printf("\n");
375
376 if (optional) {
377 printf("optional-permission: name='%s'",
378 ResTable::normalizeForOutput(name.string()).string());
379 if (maxSdkVersion != -1) {
380 printf(" maxSdkVersion='%d'", maxSdkVersion);
381 }
382 printf("\n");
383 }
384 }
385
printUsesImpliedPermission(const String8 & name,const String8 & reason)386 static void printUsesImpliedPermission(const String8& name, const String8& reason) {
387 printf("uses-implied-permission: name='%s' reason='%s'\n",
388 ResTable::normalizeForOutput(name.string()).string(),
389 ResTable::normalizeForOutput(reason.string()).string());
390 }
391
getNfcAidCategories(AssetManager & assets,String8 xmlPath,bool offHost,String8 * outError=NULL)392 Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
393 String8 *outError = NULL)
394 {
395 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
396 if (aidAsset == NULL) {
397 if (outError != NULL) *outError = "xml resource does not exist";
398 return Vector<String8>();
399 }
400
401 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
402
403 bool withinApduService = false;
404 Vector<String8> categories;
405
406 String8 error;
407 ResXMLTree tree;
408 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
409
410 size_t len;
411 int depth = 0;
412 ResXMLTree::event_code_t code;
413 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
414 if (code == ResXMLTree::END_TAG) {
415 depth--;
416 const char16_t* ctag16 = tree.getElementName(&len);
417 if (ctag16 == NULL) {
418 *outError = "failed to get XML element name (bad string pool)";
419 return Vector<String8>();
420 }
421 String8 tag(ctag16);
422
423 if (depth == 0 && tag == serviceTagName) {
424 withinApduService = false;
425 }
426
427 } else if (code == ResXMLTree::START_TAG) {
428 depth++;
429 const char16_t* ctag16 = tree.getElementName(&len);
430 if (ctag16 == NULL) {
431 *outError = "failed to get XML element name (bad string pool)";
432 return Vector<String8>();
433 }
434 String8 tag(ctag16);
435
436 if (depth == 1) {
437 if (tag == serviceTagName) {
438 withinApduService = true;
439 }
440 } else if (depth == 2 && withinApduService) {
441 if (tag == "aid-group") {
442 String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
443 if (error != "") {
444 if (outError != NULL) *outError = error;
445 return Vector<String8>();
446 }
447
448 categories.add(category);
449 }
450 }
451 }
452 }
453 aidAsset->close();
454 return categories;
455 }
456
printComponentPresence(const char * componentName)457 static void printComponentPresence(const char* componentName) {
458 printf("provides-component:'%s'\n", componentName);
459 }
460
461 /**
462 * Represents a feature that has been automatically added due to
463 * a pre-requisite or some other reason.
464 */
465 struct ImpliedFeature {
466 /**
467 * Name of the implied feature.
468 */
469 String8 name;
470
471 /**
472 * List of human-readable reasons for why this feature was implied.
473 */
474 SortedVector<String8> reasons;
475 };
476
477 /**
478 * Represents a <feature-group> tag in the AndroidManifest.xml
479 */
480 struct FeatureGroup {
FeatureGroupFeatureGroup481 FeatureGroup() : openGLESVersion(-1) {}
482
483 /**
484 * Human readable label
485 */
486 String8 label;
487
488 /**
489 * Explicit features defined in the group
490 */
491 KeyedVector<String8, bool> features;
492
493 /**
494 * OpenGL ES version required
495 */
496 int openGLESVersion;
497 };
498
addImpliedFeature(KeyedVector<String8,ImpliedFeature> * impliedFeatures,const char * name,const char * reason)499 static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
500 const char* name, const char* reason) {
501 String8 name8(name);
502 ssize_t idx = impliedFeatures->indexOfKey(name8);
503 if (idx < 0) {
504 idx = impliedFeatures->add(name8, ImpliedFeature());
505 impliedFeatures->editValueAt(idx).name = name8;
506 }
507 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
508 }
509
printFeatureGroup(const FeatureGroup & grp,const KeyedVector<String8,ImpliedFeature> * impliedFeatures=NULL)510 static void printFeatureGroup(const FeatureGroup& grp,
511 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
512 printf("feature-group: label='%s'\n", grp.label.string());
513
514 if (grp.openGLESVersion > 0) {
515 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
516 }
517
518 const size_t numFeatures = grp.features.size();
519 for (size_t i = 0; i < numFeatures; i++) {
520 const bool required = grp.features[i];
521
522 const String8& featureName = grp.features.keyAt(i);
523 printf(" uses-feature%s: name='%s'\n", (required ? "" : "-not-required"),
524 ResTable::normalizeForOutput(featureName.string()).string());
525 }
526
527 const size_t numImpliedFeatures =
528 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
529 for (size_t i = 0; i < numImpliedFeatures; i++) {
530 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
531 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
532 // The feature is explicitly set, no need to use implied
533 // definition.
534 continue;
535 }
536
537 String8 printableFeatureName(ResTable::normalizeForOutput(
538 impliedFeature.name.string()));
539 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
540 printf(" uses-implied-feature: name='%s' reason='",
541 printableFeatureName.string());
542 const size_t numReasons = impliedFeature.reasons.size();
543 for (size_t j = 0; j < numReasons; j++) {
544 printf("%s", impliedFeature.reasons[j].string());
545 if (j + 2 < numReasons) {
546 printf(", ");
547 } else if (j + 1 < numReasons) {
548 printf(", and ");
549 }
550 }
551 printf("'\n");
552 }
553 }
554
addParentFeatures(FeatureGroup * grp,const String8 & name)555 static void addParentFeatures(FeatureGroup* grp, const String8& name) {
556 if (name == "android.hardware.camera.autofocus" ||
557 name == "android.hardware.camera.flash") {
558 grp->features.add(String8("android.hardware.camera"), true);
559 } else if (name == "android.hardware.location.gps" ||
560 name == "android.hardware.location.network") {
561 grp->features.add(String8("android.hardware.location"), true);
562 } else if (name == "android.hardware.touchscreen.multitouch") {
563 grp->features.add(String8("android.hardware.touchscreen"), true);
564 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
565 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
566 grp->features.add(String8("android.hardware.touchscreen"), true);
567 } else if (name == "android.hardware.opengles.aep") {
568 const int openGLESVersion31 = 0x00030001;
569 if (openGLESVersion31 > grp->openGLESVersion) {
570 grp->openGLESVersion = openGLESVersion31;
571 }
572 }
573 }
574
575 /*
576 * Handle the "dump" command, to extract select data from an archive.
577 */
578 extern char CONSOLE_DATA[2925]; // see EOF
doDump(Bundle * bundle)579 int doDump(Bundle* bundle)
580 {
581 status_t result = UNKNOWN_ERROR;
582
583 if (bundle->getFileSpecCount() < 1) {
584 fprintf(stderr, "ERROR: no dump option specified\n");
585 return 1;
586 }
587
588 if (bundle->getFileSpecCount() < 2) {
589 fprintf(stderr, "ERROR: no dump file specified\n");
590 return 1;
591 }
592
593 const char* option = bundle->getFileSpecEntry(0);
594 const char* filename = bundle->getFileSpecEntry(1);
595
596 AssetManager assets;
597 int32_t assetsCookie;
598 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
599 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
600 return 1;
601 }
602
603 // Make a dummy config for retrieving resources... we need to supply
604 // non-default values for some configs so that we can retrieve resources
605 // in the app that don't have a default. The most important of these is
606 // the API version because key resources like icons will have an implicit
607 // version if they are using newer config types like density.
608 ResTable_config config;
609 memset(&config, 0, sizeof(ResTable_config));
610 config.language[0] = 'e';
611 config.language[1] = 'n';
612 config.country[0] = 'U';
613 config.country[1] = 'S';
614 config.orientation = ResTable_config::ORIENTATION_PORT;
615 config.density = ResTable_config::DENSITY_MEDIUM;
616 config.sdkVersion = 10000; // Very high.
617 config.screenWidthDp = 320;
618 config.screenHeightDp = 480;
619 config.smallestScreenWidthDp = 320;
620 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
621 assets.setConfiguration(config);
622
623 const ResTable& res = assets.getResources(false);
624 if (res.getError() != NO_ERROR) {
625 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
626 return 1;
627 }
628
629 // The dynamicRefTable can be null if there are no resources for this asset cookie.
630 // This fine.
631 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
632
633 Asset* asset = NULL;
634
635 if (strcmp("resources", option) == 0) {
636 #ifndef HAVE_ANDROID_OS
637 res.print(bundle->getValues());
638 #endif
639
640 } else if (strcmp("strings", option) == 0) {
641 const ResStringPool* pool = res.getTableStringBlock(0);
642 printStringPool(pool);
643
644 } else if (strcmp("xmltree", option) == 0) {
645 if (bundle->getFileSpecCount() < 3) {
646 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
647 goto bail;
648 }
649
650 for (int i=2; i<bundle->getFileSpecCount(); i++) {
651 const char* resname = bundle->getFileSpecEntry(i);
652 ResXMLTree tree(dynamicRefTable);
653 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
654 if (asset == NULL) {
655 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
656 goto bail;
657 }
658
659 if (tree.setTo(asset->getBuffer(true),
660 asset->getLength()) != NO_ERROR) {
661 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
662 goto bail;
663 }
664 tree.restart();
665 printXMLBlock(&tree);
666 tree.uninit();
667 delete asset;
668 asset = NULL;
669 }
670
671 } else if (strcmp("xmlstrings", option) == 0) {
672 if (bundle->getFileSpecCount() < 3) {
673 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
674 goto bail;
675 }
676
677 for (int i=2; i<bundle->getFileSpecCount(); i++) {
678 const char* resname = bundle->getFileSpecEntry(i);
679 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
680 if (asset == NULL) {
681 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
682 goto bail;
683 }
684
685 ResXMLTree tree(dynamicRefTable);
686 if (tree.setTo(asset->getBuffer(true),
687 asset->getLength()) != NO_ERROR) {
688 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
689 goto bail;
690 }
691 printStringPool(&tree.getStrings());
692 delete asset;
693 asset = NULL;
694 }
695
696 } else {
697 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
698 if (asset == NULL) {
699 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
700 goto bail;
701 }
702
703 ResXMLTree tree(dynamicRefTable);
704 if (tree.setTo(asset->getBuffer(true),
705 asset->getLength()) != NO_ERROR) {
706 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
707 goto bail;
708 }
709 tree.restart();
710
711 if (strcmp("permissions", option) == 0) {
712 size_t len;
713 ResXMLTree::event_code_t code;
714 int depth = 0;
715 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
716 if (code == ResXMLTree::END_TAG) {
717 depth--;
718 continue;
719 }
720 if (code != ResXMLTree::START_TAG) {
721 continue;
722 }
723 depth++;
724 const char16_t* ctag16 = tree.getElementName(&len);
725 if (ctag16 == NULL) {
726 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
727 goto bail;
728 }
729 String8 tag(ctag16);
730 //printf("Depth %d tag %s\n", depth, tag.string());
731 if (depth == 1) {
732 if (tag != "manifest") {
733 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
734 goto bail;
735 }
736 String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
737 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
738 } else if (depth == 2 && tag == "permission") {
739 String8 error;
740 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
741 if (error != "") {
742 fprintf(stderr, "ERROR: %s\n", error.string());
743 goto bail;
744 }
745 printf("permission: %s\n",
746 ResTable::normalizeForOutput(name.string()).string());
747 } else if (depth == 2 && tag == "uses-permission") {
748 String8 error;
749 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
750 if (error != "") {
751 fprintf(stderr, "ERROR: %s\n", error.string());
752 goto bail;
753 }
754 printUsesPermission(name,
755 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
756 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
757 }
758 }
759 } else if (strcmp("badging", option) == 0) {
760 Vector<String8> locales;
761 res.getLocales(&locales);
762
763 Vector<ResTable_config> configs;
764 res.getConfigurations(&configs);
765 SortedVector<int> densities;
766 const size_t NC = configs.size();
767 for (size_t i=0; i<NC; i++) {
768 int dens = configs[i].density;
769 if (dens == 0) {
770 dens = 160;
771 }
772 densities.add(dens);
773 }
774
775 size_t len;
776 ResXMLTree::event_code_t code;
777 int depth = 0;
778 String8 error;
779 bool withinActivity = false;
780 bool isMainActivity = false;
781 bool isLauncherActivity = false;
782 bool isLeanbackLauncherActivity = false;
783 bool isSearchable = false;
784 bool withinApplication = false;
785 bool withinSupportsInput = false;
786 bool withinFeatureGroup = false;
787 bool withinReceiver = false;
788 bool withinService = false;
789 bool withinProvider = false;
790 bool withinIntentFilter = false;
791 bool hasMainActivity = false;
792 bool hasOtherActivities = false;
793 bool hasOtherReceivers = false;
794 bool hasOtherServices = false;
795 bool hasIntentFilter = false;
796
797 bool hasWallpaperService = false;
798 bool hasImeService = false;
799 bool hasAccessibilityService = false;
800 bool hasPrintService = false;
801 bool hasWidgetReceivers = false;
802 bool hasDeviceAdminReceiver = false;
803 bool hasPaymentService = false;
804 bool hasDocumentsProvider = false;
805 bool hasCameraActivity = false;
806 bool hasCameraSecureActivity = false;
807 bool hasLauncher = false;
808 bool hasNotificationListenerService = false;
809 bool hasDreamService = false;
810
811 bool actMainActivity = false;
812 bool actWidgetReceivers = false;
813 bool actDeviceAdminEnabled = false;
814 bool actImeService = false;
815 bool actWallpaperService = false;
816 bool actAccessibilityService = false;
817 bool actPrintService = false;
818 bool actHostApduService = false;
819 bool actOffHostApduService = false;
820 bool actDocumentsProvider = false;
821 bool actNotificationListenerService = false;
822 bool actDreamService = false;
823 bool actCamera = false;
824 bool actCameraSecure = false;
825 bool catLauncher = false;
826 bool hasMetaHostPaymentCategory = false;
827 bool hasMetaOffHostPaymentCategory = false;
828
829 // These permissions are required by services implementing services
830 // the system binds to (IME, Accessibility, PrintServices, etc.)
831 bool hasBindDeviceAdminPermission = false;
832 bool hasBindInputMethodPermission = false;
833 bool hasBindAccessibilityServicePermission = false;
834 bool hasBindPrintServicePermission = false;
835 bool hasBindNfcServicePermission = false;
836 bool hasRequiredSafAttributes = false;
837 bool hasBindNotificationListenerServicePermission = false;
838 bool hasBindDreamServicePermission = false;
839
840 // These two implement the implicit permissions that are granted
841 // to pre-1.6 applications.
842 bool hasWriteExternalStoragePermission = false;
843 bool hasReadPhoneStatePermission = false;
844
845 // If an app requests write storage, they will also get read storage.
846 bool hasReadExternalStoragePermission = false;
847
848 // Implement transition to read and write call log.
849 bool hasReadContactsPermission = false;
850 bool hasWriteContactsPermission = false;
851 bool hasReadCallLogPermission = false;
852 bool hasWriteCallLogPermission = false;
853
854 // If an app declares itself as multiArch, we report the
855 // native libraries differently.
856 bool hasMultiArch = false;
857
858 // This next group of variables is used to implement a group of
859 // backward-compatibility heuristics necessitated by the addition of
860 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
861 // heuristic is "if an app requests a permission but doesn't explicitly
862 // request the corresponding <uses-feature>, presume it's there anyway".
863
864 // 2.2 also added some other features that apps can request, but that
865 // have no corresponding permission, so we cannot implement any
866 // back-compatibility heuristic for them. The below are thus unnecessary
867 // (but are retained here for documentary purposes.)
868 //bool specCompassFeature = false;
869 //bool specAccelerometerFeature = false;
870 //bool specProximityFeature = false;
871 //bool specAmbientLightFeature = false;
872 //bool specLiveWallpaperFeature = false;
873
874 int targetSdk = 0;
875 int smallScreen = 1;
876 int normalScreen = 1;
877 int largeScreen = 1;
878 int xlargeScreen = 1;
879 int anyDensity = 1;
880 int requiresSmallestWidthDp = 0;
881 int compatibleWidthLimitDp = 0;
882 int largestWidthLimitDp = 0;
883 String8 pkg;
884 String8 activityName;
885 String8 activityLabel;
886 String8 activityIcon;
887 String8 activityBanner;
888 String8 receiverName;
889 String8 serviceName;
890 Vector<String8> supportedInput;
891
892 FeatureGroup commonFeatures;
893 Vector<FeatureGroup> featureGroups;
894 KeyedVector<String8, ImpliedFeature> impliedFeatures;
895
896 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
897 if (code == ResXMLTree::END_TAG) {
898 depth--;
899 if (depth < 2) {
900 if (withinSupportsInput && !supportedInput.isEmpty()) {
901 printf("supports-input: '");
902 const size_t N = supportedInput.size();
903 for (size_t i=0; i<N; i++) {
904 printf("%s", ResTable::normalizeForOutput(
905 supportedInput[i].string()).string());
906 if (i != N - 1) {
907 printf("' '");
908 } else {
909 printf("'\n");
910 }
911 }
912 supportedInput.clear();
913 }
914 withinApplication = false;
915 withinSupportsInput = false;
916 withinFeatureGroup = false;
917 } else if (depth < 3) {
918 if (withinActivity && isMainActivity) {
919 String8 aName(getComponentName(pkg, activityName));
920 if (isLauncherActivity) {
921 printf("launchable-activity:");
922 if (aName.length() > 0) {
923 printf(" name='%s' ",
924 ResTable::normalizeForOutput(aName.string()).string());
925 }
926 printf(" label='%s' icon='%s'\n",
927 ResTable::normalizeForOutput(activityLabel.string()).string(),
928 ResTable::normalizeForOutput(activityIcon.string()).string());
929 }
930 if (isLeanbackLauncherActivity) {
931 printf("leanback-launchable-activity:");
932 if (aName.length() > 0) {
933 printf(" name='%s' ",
934 ResTable::normalizeForOutput(aName.string()).string());
935 }
936 printf(" label='%s' icon='%s' banner='%s'\n",
937 ResTable::normalizeForOutput(activityLabel.string()).string(),
938 ResTable::normalizeForOutput(activityIcon.string()).string(),
939 ResTable::normalizeForOutput(activityBanner.string()).string());
940 }
941 }
942 if (!hasIntentFilter) {
943 hasOtherActivities |= withinActivity;
944 hasOtherReceivers |= withinReceiver;
945 hasOtherServices |= withinService;
946 } else {
947 if (withinService) {
948 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
949 hasBindNfcServicePermission);
950 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
951 hasBindNfcServicePermission);
952 }
953 }
954 withinActivity = false;
955 withinService = false;
956 withinReceiver = false;
957 withinProvider = false;
958 hasIntentFilter = false;
959 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
960 } else if (depth < 4) {
961 if (withinIntentFilter) {
962 if (withinActivity) {
963 hasMainActivity |= actMainActivity;
964 hasLauncher |= catLauncher;
965 hasCameraActivity |= actCamera;
966 hasCameraSecureActivity |= actCameraSecure;
967 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
968 } else if (withinReceiver) {
969 hasWidgetReceivers |= actWidgetReceivers;
970 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
971 hasBindDeviceAdminPermission);
972 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
973 } else if (withinService) {
974 hasImeService |= actImeService;
975 hasWallpaperService |= actWallpaperService;
976 hasAccessibilityService |= (actAccessibilityService &&
977 hasBindAccessibilityServicePermission);
978 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
979 hasNotificationListenerService |= actNotificationListenerService &&
980 hasBindNotificationListenerServicePermission;
981 hasDreamService |= actDreamService && hasBindDreamServicePermission;
982 hasOtherServices |= (!actImeService && !actWallpaperService &&
983 !actAccessibilityService && !actPrintService &&
984 !actHostApduService && !actOffHostApduService &&
985 !actNotificationListenerService);
986 } else if (withinProvider) {
987 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
988 }
989 }
990 withinIntentFilter = false;
991 }
992 continue;
993 }
994 if (code != ResXMLTree::START_TAG) {
995 continue;
996 }
997 depth++;
998
999 const char16_t* ctag16 = tree.getElementName(&len);
1000 if (ctag16 == NULL) {
1001 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1002 goto bail;
1003 }
1004 String8 tag(ctag16);
1005 //printf("Depth %d, %s\n", depth, tag.string());
1006 if (depth == 1) {
1007 if (tag != "manifest") {
1008 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1009 goto bail;
1010 }
1011 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
1012 printf("package: name='%s' ",
1013 ResTable::normalizeForOutput(pkg.string()).string());
1014 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1015 &error);
1016 if (error != "") {
1017 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
1018 error.string());
1019 goto bail;
1020 }
1021 if (versionCode > 0) {
1022 printf("versionCode='%d' ", versionCode);
1023 } else {
1024 printf("versionCode='' ");
1025 }
1026 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1027 VERSION_NAME_ATTR, &error);
1028 if (error != "") {
1029 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
1030 error.string());
1031 goto bail;
1032 }
1033 printf("versionName='%s'",
1034 ResTable::normalizeForOutput(versionName.string()).string());
1035
1036 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
1037 if (!splitName.isEmpty()) {
1038 printf(" split='%s'", ResTable::normalizeForOutput(
1039 splitName.string()).string());
1040 }
1041
1042 String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
1043 "platformBuildVersionName");
1044 printf(" platformBuildVersionName='%s'", platformVersionName.string());
1045 printf("\n");
1046
1047 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1048 INSTALL_LOCATION_ATTR, &error);
1049 if (error != "") {
1050 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1051 error.string());
1052 goto bail;
1053 }
1054
1055 if (installLocation >= 0) {
1056 printf("install-location:'");
1057 switch (installLocation) {
1058 case 0:
1059 printf("auto");
1060 break;
1061 case 1:
1062 printf("internalOnly");
1063 break;
1064 case 2:
1065 printf("preferExternal");
1066 break;
1067 default:
1068 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1069 goto bail;
1070 }
1071 printf("'\n");
1072 }
1073 } else if (depth == 2) {
1074 withinApplication = false;
1075 if (tag == "application") {
1076 withinApplication = true;
1077
1078 String8 label;
1079 const size_t NL = locales.size();
1080 for (size_t i=0; i<NL; i++) {
1081 const char* localeStr = locales[i].string();
1082 assets.setLocale(localeStr != NULL ? localeStr : "");
1083 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1084 &error);
1085 if (llabel != "") {
1086 if (localeStr == NULL || strlen(localeStr) == 0) {
1087 label = llabel;
1088 printf("application-label:'%s'\n",
1089 ResTable::normalizeForOutput(llabel.string()).string());
1090 } else {
1091 if (label == "") {
1092 label = llabel;
1093 }
1094 printf("application-label-%s:'%s'\n", localeStr,
1095 ResTable::normalizeForOutput(llabel.string()).string());
1096 }
1097 }
1098 }
1099
1100 ResTable_config tmpConfig = config;
1101 const size_t ND = densities.size();
1102 for (size_t i=0; i<ND; i++) {
1103 tmpConfig.density = densities[i];
1104 assets.setConfiguration(tmpConfig);
1105 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1106 &error);
1107 if (icon != "") {
1108 printf("application-icon-%d:'%s'\n", densities[i],
1109 ResTable::normalizeForOutput(icon.string()).string());
1110 }
1111 }
1112 assets.setConfiguration(config);
1113
1114 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
1115 if (error != "") {
1116 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1117 error.string());
1118 goto bail;
1119 }
1120 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1121 &error);
1122 if (error != "") {
1123 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
1124 error.string());
1125 goto bail;
1126 }
1127
1128 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error);
1129 if (error != "") {
1130 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1131 error.string());
1132 goto bail;
1133 }
1134 printf("application: label='%s' ",
1135 ResTable::normalizeForOutput(label.string()).string());
1136 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1137 if (banner != "") {
1138 printf(" banner='%s'", ResTable::normalizeForOutput(banner.string()).string());
1139 }
1140 printf("\n");
1141 if (testOnly != 0) {
1142 printf("testOnly='%d'\n", testOnly);
1143 }
1144
1145 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1146 ISGAME_ATTR, 0, &error);
1147 if (error != "") {
1148 fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n",
1149 error.string());
1150 goto bail;
1151 }
1152 if (isGame != 0) {
1153 printf("application-isGame\n");
1154 }
1155
1156 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1157 DEBUGGABLE_ATTR, 0, &error);
1158 if (error != "") {
1159 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
1160 error.string());
1161 goto bail;
1162 }
1163 if (debuggable != 0) {
1164 printf("application-debuggable\n");
1165 }
1166
1167 // We must search by name because the multiArch flag hasn't been API
1168 // frozen yet.
1169 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1170 "multiArch");
1171 if (multiArchIndex >= 0) {
1172 Res_value value;
1173 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1174 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1175 value.dataType <= Res_value::TYPE_LAST_INT) {
1176 hasMultiArch = value.data;
1177 }
1178 }
1179 }
1180 } else if (tag == "uses-sdk") {
1181 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1182 if (error != "") {
1183 error = "";
1184 String8 name = AaptXml::getResolvedAttribute(res, tree,
1185 MIN_SDK_VERSION_ATTR, &error);
1186 if (error != "") {
1187 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1188 error.string());
1189 goto bail;
1190 }
1191 if (name == "Donut") targetSdk = 4;
1192 printf("sdkVersion:'%s'\n",
1193 ResTable::normalizeForOutput(name.string()).string());
1194 } else if (code != -1) {
1195 targetSdk = code;
1196 printf("sdkVersion:'%d'\n", code);
1197 }
1198 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
1199 if (code != -1) {
1200 printf("maxSdkVersion:'%d'\n", code);
1201 }
1202 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1203 if (error != "") {
1204 error = "";
1205 String8 name = AaptXml::getResolvedAttribute(res, tree,
1206 TARGET_SDK_VERSION_ATTR, &error);
1207 if (error != "") {
1208 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1209 error.string());
1210 goto bail;
1211 }
1212 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
1213 printf("targetSdkVersion:'%s'\n",
1214 ResTable::normalizeForOutput(name.string()).string());
1215 } else if (code != -1) {
1216 if (targetSdk < code) {
1217 targetSdk = code;
1218 }
1219 printf("targetSdkVersion:'%d'\n", code);
1220 }
1221 } else if (tag == "uses-configuration") {
1222 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1223 REQ_TOUCH_SCREEN_ATTR, 0);
1224 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1225 REQ_KEYBOARD_TYPE_ATTR, 0);
1226 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1227 REQ_HARD_KEYBOARD_ATTR, 0);
1228 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1229 REQ_NAVIGATION_ATTR, 0);
1230 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1231 REQ_FIVE_WAY_NAV_ATTR, 0);
1232 printf("uses-configuration:");
1233 if (reqTouchScreen != 0) {
1234 printf(" reqTouchScreen='%d'", reqTouchScreen);
1235 }
1236 if (reqKeyboardType != 0) {
1237 printf(" reqKeyboardType='%d'", reqKeyboardType);
1238 }
1239 if (reqHardKeyboard != 0) {
1240 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1241 }
1242 if (reqNavigation != 0) {
1243 printf(" reqNavigation='%d'", reqNavigation);
1244 }
1245 if (reqFiveWayNav != 0) {
1246 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1247 }
1248 printf("\n");
1249 } else if (tag == "supports-input") {
1250 withinSupportsInput = true;
1251 } else if (tag == "supports-screens") {
1252 smallScreen = AaptXml::getIntegerAttribute(tree,
1253 SMALL_SCREEN_ATTR, 1);
1254 normalScreen = AaptXml::getIntegerAttribute(tree,
1255 NORMAL_SCREEN_ATTR, 1);
1256 largeScreen = AaptXml::getIntegerAttribute(tree,
1257 LARGE_SCREEN_ATTR, 1);
1258 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1259 XLARGE_SCREEN_ATTR, 1);
1260 anyDensity = AaptXml::getIntegerAttribute(tree,
1261 ANY_DENSITY_ATTR, 1);
1262 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1263 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1264 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1265 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1266 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1267 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
1268 } else if (tag == "feature-group") {
1269 withinFeatureGroup = true;
1270 FeatureGroup group;
1271 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
1272 if (error != "") {
1273 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1274 " %s\n", error.string());
1275 goto bail;
1276 }
1277 featureGroups.add(group);
1278
1279 } else if (tag == "uses-feature") {
1280 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1281 if (name != "" && error == "") {
1282 int req = AaptXml::getIntegerAttribute(tree,
1283 REQUIRED_ATTR, 1);
1284
1285 commonFeatures.features.add(name, req);
1286 if (req) {
1287 addParentFeatures(&commonFeatures, name);
1288 }
1289 } else {
1290 int vers = AaptXml::getIntegerAttribute(tree,
1291 GL_ES_VERSION_ATTR, &error);
1292 if (error == "") {
1293 if (vers > commonFeatures.openGLESVersion) {
1294 commonFeatures.openGLESVersion = vers;
1295 }
1296 }
1297 }
1298 } else if (tag == "uses-permission") {
1299 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1300 if (name != "" && error == "") {
1301 if (name == "android.permission.CAMERA") {
1302 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
1303 String8::format("requested %s permission", name.string())
1304 .string());
1305 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1306 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1307 String8::format("requested %s permission", name.string())
1308 .string());
1309 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1310 String8::format("requested %s permission", name.string())
1311 .string());
1312 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1313 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1314 String8::format("requested %s permission", name.string())
1315 .string());
1316 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1317 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1318 String8::format("requested %s permission", name.string())
1319 .string());
1320 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1321 String8::format("requested %s permission", name.string())
1322 .string());
1323 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1324 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1325 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1326 String8::format("requested %s permission", name.string())
1327 .string());
1328 } else if (name == "android.permission.BLUETOOTH" ||
1329 name == "android.permission.BLUETOOTH_ADMIN") {
1330 if (targetSdk > 4) {
1331 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1332 String8::format("requested %s permission", name.string())
1333 .string());
1334 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1335 "targetSdkVersion > 4");
1336 }
1337 } else if (name == "android.permission.RECORD_AUDIO") {
1338 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1339 String8::format("requested %s permission", name.string())
1340 .string());
1341 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1342 name == "android.permission.CHANGE_WIFI_STATE" ||
1343 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1344 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1345 String8::format("requested %s permission", name.string())
1346 .string());
1347 } else if (name == "android.permission.CALL_PHONE" ||
1348 name == "android.permission.CALL_PRIVILEGED" ||
1349 name == "android.permission.MODIFY_PHONE_STATE" ||
1350 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1351 name == "android.permission.READ_SMS" ||
1352 name == "android.permission.RECEIVE_SMS" ||
1353 name == "android.permission.RECEIVE_MMS" ||
1354 name == "android.permission.RECEIVE_WAP_PUSH" ||
1355 name == "android.permission.SEND_SMS" ||
1356 name == "android.permission.WRITE_APN_SETTINGS" ||
1357 name == "android.permission.WRITE_SMS") {
1358 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1359 String8("requested a telephony permission").string());
1360 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1361 hasWriteExternalStoragePermission = true;
1362 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1363 hasReadExternalStoragePermission = true;
1364 } else if (name == "android.permission.READ_PHONE_STATE") {
1365 hasReadPhoneStatePermission = true;
1366 } else if (name == "android.permission.READ_CONTACTS") {
1367 hasReadContactsPermission = true;
1368 } else if (name == "android.permission.WRITE_CONTACTS") {
1369 hasWriteContactsPermission = true;
1370 } else if (name == "android.permission.READ_CALL_LOG") {
1371 hasReadCallLogPermission = true;
1372 } else if (name == "android.permission.WRITE_CALL_LOG") {
1373 hasWriteCallLogPermission = true;
1374 }
1375
1376 printUsesPermission(name,
1377 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1378 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1379 } else {
1380 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1381 error.string());
1382 goto bail;
1383 }
1384 } else if (tag == "uses-package") {
1385 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1386 if (name != "" && error == "") {
1387 printf("uses-package:'%s'\n",
1388 ResTable::normalizeForOutput(name.string()).string());
1389 } else {
1390 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1391 error.string());
1392 goto bail;
1393 }
1394 } else if (tag == "original-package") {
1395 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1396 if (name != "" && error == "") {
1397 printf("original-package:'%s'\n",
1398 ResTable::normalizeForOutput(name.string()).string());
1399 } else {
1400 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1401 error.string());
1402 goto bail;
1403 }
1404 } else if (tag == "supports-gl-texture") {
1405 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1406 if (name != "" && error == "") {
1407 printf("supports-gl-texture:'%s'\n",
1408 ResTable::normalizeForOutput(name.string()).string());
1409 } else {
1410 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1411 error.string());
1412 goto bail;
1413 }
1414 } else if (tag == "compatible-screens") {
1415 printCompatibleScreens(tree, &error);
1416 if (error != "") {
1417 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1418 error.string());
1419 goto bail;
1420 }
1421 depth--;
1422 } else if (tag == "package-verifier") {
1423 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1424 if (name != "" && error == "") {
1425 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1426 if (publicKey != "" && error == "") {
1427 printf("package-verifier: name='%s' publicKey='%s'\n",
1428 ResTable::normalizeForOutput(name.string()).string(),
1429 ResTable::normalizeForOutput(publicKey.string()).string());
1430 }
1431 }
1432 }
1433 } else if (depth == 3) {
1434 withinActivity = false;
1435 withinReceiver = false;
1436 withinService = false;
1437 withinProvider = false;
1438 hasIntentFilter = false;
1439 hasMetaHostPaymentCategory = false;
1440 hasMetaOffHostPaymentCategory = false;
1441 hasBindDeviceAdminPermission = false;
1442 hasBindInputMethodPermission = false;
1443 hasBindAccessibilityServicePermission = false;
1444 hasBindPrintServicePermission = false;
1445 hasBindNfcServicePermission = false;
1446 hasRequiredSafAttributes = false;
1447 hasBindNotificationListenerServicePermission = false;
1448 hasBindDreamServicePermission = false;
1449 if (withinApplication) {
1450 if(tag == "activity") {
1451 withinActivity = true;
1452 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1453 if (error != "") {
1454 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1455 error.string());
1456 goto bail;
1457 }
1458
1459 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1460 &error);
1461 if (error != "") {
1462 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1463 error.string());
1464 goto bail;
1465 }
1466
1467 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1468 &error);
1469 if (error != "") {
1470 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1471 error.string());
1472 goto bail;
1473 }
1474
1475 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1476 &error);
1477 if (error != "") {
1478 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1479 error.string());
1480 goto bail;
1481 }
1482
1483 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
1484 SCREEN_ORIENTATION_ATTR, &error);
1485 if (error == "") {
1486 if (orien == 0 || orien == 6 || orien == 8) {
1487 // Requests landscape, sensorLandscape, or reverseLandscape.
1488 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1489 "one or more activities have specified a landscape orientation");
1490 } else if (orien == 1 || orien == 7 || orien == 9) {
1491 // Requests portrait, sensorPortrait, or reversePortrait.
1492 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1493 "one or more activities have specified a portrait orientation");
1494 }
1495 }
1496 } else if (tag == "uses-library") {
1497 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1498 if (error != "") {
1499 fprintf(stderr,
1500 "ERROR getting 'android:name' attribute for uses-library"
1501 " %s\n", error.string());
1502 goto bail;
1503 }
1504 int req = AaptXml::getIntegerAttribute(tree,
1505 REQUIRED_ATTR, 1);
1506 printf("uses-library%s:'%s'\n",
1507 req ? "" : "-not-required", ResTable::normalizeForOutput(
1508 libraryName.string()).string());
1509 } else if (tag == "receiver") {
1510 withinReceiver = true;
1511 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1512
1513 if (error != "") {
1514 fprintf(stderr,
1515 "ERROR getting 'android:name' attribute for receiver:"
1516 " %s\n", error.string());
1517 goto bail;
1518 }
1519
1520 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1521 &error);
1522 if (error == "") {
1523 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1524 hasBindDeviceAdminPermission = true;
1525 }
1526 } else {
1527 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1528 " receiver '%s': %s\n", receiverName.string(), error.string());
1529 }
1530 } else if (tag == "service") {
1531 withinService = true;
1532 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1533
1534 if (error != "") {
1535 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1536 "service:%s\n", error.string());
1537 goto bail;
1538 }
1539
1540 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1541 &error);
1542 if (error == "") {
1543 if (permission == "android.permission.BIND_INPUT_METHOD") {
1544 hasBindInputMethodPermission = true;
1545 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1546 hasBindAccessibilityServicePermission = true;
1547 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1548 hasBindPrintServicePermission = true;
1549 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1550 hasBindNfcServicePermission = true;
1551 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1552 hasBindNotificationListenerServicePermission = true;
1553 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1554 hasBindDreamServicePermission = true;
1555 }
1556 } else {
1557 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1558 " service '%s': %s\n", serviceName.string(), error.string());
1559 }
1560 } else if (tag == "provider") {
1561 withinProvider = true;
1562
1563 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1564 EXPORTED_ATTR, &error);
1565 if (error != "") {
1566 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1567 " %s\n", error.string());
1568 goto bail;
1569 }
1570
1571 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1572 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
1573 if (error != "") {
1574 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1575 " %s\n", error.string());
1576 goto bail;
1577 }
1578
1579 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1580 PERMISSION_ATTR, &error);
1581 if (error != "") {
1582 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1583 " %s\n", error.string());
1584 goto bail;
1585 }
1586
1587 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1588 permission == "android.permission.MANAGE_DOCUMENTS";
1589
1590 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1591 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1592 NAME_ATTR, &error);
1593 if (error != "") {
1594 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1595 "meta-data:%s\n", error.string());
1596 goto bail;
1597 }
1598 printf("meta-data: name='%s' ",
1599 ResTable::normalizeForOutput(metaDataName.string()).string());
1600 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
1601 &error);
1602 if (error != "") {
1603 // Try looking for a RESOURCE_ATTR
1604 error = "";
1605 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
1606 String8("resource"), &error);
1607 if (error != "") {
1608 fprintf(stderr, "ERROR getting 'android:value' or "
1609 "'android:resource' attribute for "
1610 "meta-data:%s\n", error.string());
1611 goto bail;
1612 }
1613 }
1614 printf("\n");
1615 } else if (withinSupportsInput && tag == "input-type") {
1616 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1617 if (name != "" && error == "") {
1618 supportedInput.add(name);
1619 } else {
1620 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1621 error.string());
1622 goto bail;
1623 }
1624 }
1625 } else if (withinFeatureGroup && tag == "uses-feature") {
1626 FeatureGroup& top = featureGroups.editTop();
1627
1628 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
1629 if (name != "" && error == "") {
1630 top.features.add(name, true);
1631 addParentFeatures(&top, name);
1632 } else {
1633 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1634 &error);
1635 if (error == "") {
1636 if (vers > top.openGLESVersion) {
1637 top.openGLESVersion = vers;
1638 }
1639 }
1640 }
1641 }
1642 } else if (depth == 4) {
1643 if (tag == "intent-filter") {
1644 hasIntentFilter = true;
1645 withinIntentFilter = true;
1646 actMainActivity = false;
1647 actWidgetReceivers = false;
1648 actImeService = false;
1649 actWallpaperService = false;
1650 actAccessibilityService = false;
1651 actPrintService = false;
1652 actDeviceAdminEnabled = false;
1653 actHostApduService = false;
1654 actOffHostApduService = false;
1655 actDocumentsProvider = false;
1656 actNotificationListenerService = false;
1657 actDreamService = false;
1658 actCamera = false;
1659 actCameraSecure = false;
1660 catLauncher = false;
1661 } else if (withinService && tag == "meta-data") {
1662 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1663 if (error != "") {
1664 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1665 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1666 goto bail;
1667 }
1668
1669 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1670 name == "android.nfc.cardemulation.off_host_apdu_service") {
1671 bool offHost = true;
1672 if (name == "android.nfc.cardemulation.host_apdu_service") {
1673 offHost = false;
1674 }
1675
1676 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1677 RESOURCE_ATTR, &error);
1678 if (error != "") {
1679 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1680 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1681 goto bail;
1682 }
1683
1684 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1685 offHost, &error);
1686 if (error != "") {
1687 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1688 serviceName.string());
1689 goto bail;
1690 }
1691
1692 const size_t catLen = categories.size();
1693 for (size_t i = 0; i < catLen; i++) {
1694 bool paymentCategory = (categories[i] == "payment");
1695 if (offHost) {
1696 hasMetaOffHostPaymentCategory |= paymentCategory;
1697 } else {
1698 hasMetaHostPaymentCategory |= paymentCategory;
1699 }
1700 }
1701 }
1702 }
1703 } else if ((depth == 5) && withinIntentFilter) {
1704 String8 action;
1705 if (tag == "action") {
1706 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1707 if (error != "") {
1708 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1709 error.string());
1710 goto bail;
1711 }
1712
1713 if (withinActivity) {
1714 if (action == "android.intent.action.MAIN") {
1715 isMainActivity = true;
1716 actMainActivity = true;
1717 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1718 action == "android.media.action.VIDEO_CAMERA") {
1719 actCamera = true;
1720 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1721 actCameraSecure = true;
1722 }
1723 } else if (withinReceiver) {
1724 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1725 actWidgetReceivers = true;
1726 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1727 actDeviceAdminEnabled = true;
1728 }
1729 } else if (withinService) {
1730 if (action == "android.view.InputMethod") {
1731 actImeService = true;
1732 } else if (action == "android.service.wallpaper.WallpaperService") {
1733 actWallpaperService = true;
1734 } else if (action == "android.accessibilityservice.AccessibilityService") {
1735 actAccessibilityService = true;
1736 } else if (action == "android.printservice.PrintService") {
1737 actPrintService = true;
1738 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1739 actHostApduService = true;
1740 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1741 actOffHostApduService = true;
1742 } else if (action == "android.service.notification.NotificationListenerService") {
1743 actNotificationListenerService = true;
1744 } else if (action == "android.service.dreams.DreamService") {
1745 actDreamService = true;
1746 }
1747 } else if (withinProvider) {
1748 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1749 actDocumentsProvider = true;
1750 }
1751 }
1752 if (action == "android.intent.action.SEARCH") {
1753 isSearchable = true;
1754 }
1755 }
1756
1757 if (tag == "category") {
1758 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1759 if (error != "") {
1760 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1761 error.string());
1762 goto bail;
1763 }
1764 if (withinActivity) {
1765 if (category == "android.intent.category.LAUNCHER") {
1766 isLauncherActivity = true;
1767 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1768 isLeanbackLauncherActivity = true;
1769 } else if (category == "android.intent.category.HOME") {
1770 catLauncher = true;
1771 }
1772 }
1773 }
1774 }
1775 }
1776
1777 // Pre-1.6 implicitly granted permission compatibility logic
1778 if (targetSdk < 4) {
1779 if (!hasWriteExternalStoragePermission) {
1780 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1781 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1782 String8("targetSdkVersion < 4"));
1783 hasWriteExternalStoragePermission = true;
1784 }
1785 if (!hasReadPhoneStatePermission) {
1786 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1787 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1788 String8("targetSdkVersion < 4"));
1789 }
1790 }
1791
1792 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1793 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1794 // do this (regardless of target API version) because we can't have
1795 // an app with write permission but not read permission.
1796 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1797 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1798 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1799 String8("requested WRITE_EXTERNAL_STORAGE"));
1800 }
1801
1802 // Pre-JellyBean call log permission compatibility.
1803 if (targetSdk < 16) {
1804 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1805 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1806 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1807 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
1808 }
1809 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1810 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1811 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1812 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
1813 }
1814 }
1815
1816 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1817 "default feature for all apps");
1818
1819 const size_t numFeatureGroups = featureGroups.size();
1820 if (numFeatureGroups == 0) {
1821 // If no <feature-group> tags were defined, apply auto-implied features.
1822 printFeatureGroup(commonFeatures, &impliedFeatures);
1823
1824 } else {
1825 // <feature-group> tags are defined, so we ignore implied features and
1826 for (size_t i = 0; i < numFeatureGroups; i++) {
1827 FeatureGroup& grp = featureGroups.editItemAt(i);
1828
1829 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1830 grp.openGLESVersion = commonFeatures.openGLESVersion;
1831 }
1832
1833 // Merge the features defined in the top level (not inside a <feature-group>)
1834 // with this feature group.
1835 const size_t numCommonFeatures = commonFeatures.features.size();
1836 for (size_t j = 0; j < numCommonFeatures; j++) {
1837 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
1838 grp.features.add(commonFeatures.features.keyAt(j),
1839 commonFeatures.features[j]);
1840 }
1841 }
1842
1843 if (!grp.features.isEmpty()) {
1844 printFeatureGroup(grp);
1845 }
1846 }
1847 }
1848
1849
1850 if (hasWidgetReceivers) {
1851 printComponentPresence("app-widget");
1852 }
1853 if (hasDeviceAdminReceiver) {
1854 printComponentPresence("device-admin");
1855 }
1856 if (hasImeService) {
1857 printComponentPresence("ime");
1858 }
1859 if (hasWallpaperService) {
1860 printComponentPresence("wallpaper");
1861 }
1862 if (hasAccessibilityService) {
1863 printComponentPresence("accessibility");
1864 }
1865 if (hasPrintService) {
1866 printComponentPresence("print-service");
1867 }
1868 if (hasPaymentService) {
1869 printComponentPresence("payment");
1870 }
1871 if (isSearchable) {
1872 printComponentPresence("search");
1873 }
1874 if (hasDocumentsProvider) {
1875 printComponentPresence("document-provider");
1876 }
1877 if (hasLauncher) {
1878 printComponentPresence("launcher");
1879 }
1880 if (hasNotificationListenerService) {
1881 printComponentPresence("notification-listener");
1882 }
1883 if (hasDreamService) {
1884 printComponentPresence("dream");
1885 }
1886 if (hasCameraActivity) {
1887 printComponentPresence("camera");
1888 }
1889 if (hasCameraSecureActivity) {
1890 printComponentPresence("camera-secure");
1891 }
1892
1893 if (hasMainActivity) {
1894 printf("main\n");
1895 }
1896 if (hasOtherActivities) {
1897 printf("other-activities\n");
1898 }
1899 if (hasOtherReceivers) {
1900 printf("other-receivers\n");
1901 }
1902 if (hasOtherServices) {
1903 printf("other-services\n");
1904 }
1905
1906 // For modern apps, if screen size buckets haven't been specified
1907 // but the new width ranges have, then infer the buckets from them.
1908 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1909 && requiresSmallestWidthDp > 0) {
1910 int compatWidth = compatibleWidthLimitDp;
1911 if (compatWidth <= 0) {
1912 compatWidth = requiresSmallestWidthDp;
1913 }
1914 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1915 smallScreen = -1;
1916 } else {
1917 smallScreen = 0;
1918 }
1919 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1920 normalScreen = -1;
1921 } else {
1922 normalScreen = 0;
1923 }
1924 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1925 largeScreen = -1;
1926 } else {
1927 largeScreen = 0;
1928 }
1929 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1930 xlargeScreen = -1;
1931 } else {
1932 xlargeScreen = 0;
1933 }
1934 }
1935
1936 // Determine default values for any unspecified screen sizes,
1937 // based on the target SDK of the package. As of 4 (donut)
1938 // the screen size support was introduced, so all default to
1939 // enabled.
1940 if (smallScreen > 0) {
1941 smallScreen = targetSdk >= 4 ? -1 : 0;
1942 }
1943 if (normalScreen > 0) {
1944 normalScreen = -1;
1945 }
1946 if (largeScreen > 0) {
1947 largeScreen = targetSdk >= 4 ? -1 : 0;
1948 }
1949 if (xlargeScreen > 0) {
1950 // Introduced in Gingerbread.
1951 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1952 }
1953 if (anyDensity > 0) {
1954 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1955 || compatibleWidthLimitDp > 0) ? -1 : 0;
1956 }
1957 printf("supports-screens:");
1958 if (smallScreen != 0) {
1959 printf(" 'small'");
1960 }
1961 if (normalScreen != 0) {
1962 printf(" 'normal'");
1963 }
1964 if (largeScreen != 0) {
1965 printf(" 'large'");
1966 }
1967 if (xlargeScreen != 0) {
1968 printf(" 'xlarge'");
1969 }
1970 printf("\n");
1971 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1972 if (requiresSmallestWidthDp > 0) {
1973 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1974 }
1975 if (compatibleWidthLimitDp > 0) {
1976 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1977 }
1978 if (largestWidthLimitDp > 0) {
1979 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1980 }
1981
1982 printf("locales:");
1983 const size_t NL = locales.size();
1984 for (size_t i=0; i<NL; i++) {
1985 const char* localeStr = locales[i].string();
1986 if (localeStr == NULL || strlen(localeStr) == 0) {
1987 localeStr = "--_--";
1988 }
1989 printf(" '%s'", localeStr);
1990 }
1991 printf("\n");
1992
1993 printf("densities:");
1994 const size_t ND = densities.size();
1995 for (size_t i=0; i<ND; i++) {
1996 printf(" '%d'", densities[i]);
1997 }
1998 printf("\n");
1999
2000 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2001 if (dir != NULL) {
2002 if (dir->getFileCount() > 0) {
2003 SortedVector<String8> architectures;
2004 for (size_t i=0; i<dir->getFileCount(); i++) {
2005 architectures.add(ResTable::normalizeForOutput(
2006 dir->getFileName(i).string()));
2007 }
2008
2009 bool outputAltNativeCode = false;
2010 // A multiArch package is one that contains 64-bit and
2011 // 32-bit versions of native code and expects 3rd-party
2012 // apps to load these native code libraries. Since most
2013 // 64-bit systems also support 32-bit apps, the apps
2014 // loading this multiArch package's code may be either
2015 // 32-bit or 64-bit.
2016 if (hasMultiArch) {
2017 // If this is a multiArch package, report the 64-bit
2018 // version only. Then as a separate entry, report the
2019 // rest.
2020 //
2021 // If we report the 32-bit architecture, this APK will
2022 // be installed on a 32-bit device, causing a large waste
2023 // of bandwidth and disk space. This assumes that
2024 // the developer of the multiArch package has also
2025 // made a version that is 32-bit only.
2026 String8 intel64("x86_64");
2027 String8 arm64("arm64-v8a");
2028 ssize_t index = architectures.indexOf(intel64);
2029 if (index < 0) {
2030 index = architectures.indexOf(arm64);
2031 }
2032
2033 if (index >= 0) {
2034 printf("native-code: '%s'\n", architectures[index].string());
2035 architectures.removeAt(index);
2036 outputAltNativeCode = true;
2037 }
2038 }
2039
2040 const size_t archCount = architectures.size();
2041 if (archCount > 0) {
2042 if (outputAltNativeCode) {
2043 printf("alt-");
2044 }
2045 printf("native-code:");
2046 for (size_t i = 0; i < archCount; i++) {
2047 printf(" '%s'", architectures[i].string());
2048 }
2049 printf("\n");
2050 }
2051 }
2052 delete dir;
2053 }
2054 } else if (strcmp("badger", option) == 0) {
2055 printf("%s", CONSOLE_DATA);
2056 } else if (strcmp("configurations", option) == 0) {
2057 Vector<ResTable_config> configs;
2058 res.getConfigurations(&configs);
2059 const size_t N = configs.size();
2060 for (size_t i=0; i<N; i++) {
2061 printf("%s\n", configs[i].toString().string());
2062 }
2063 } else {
2064 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2065 goto bail;
2066 }
2067 }
2068
2069 result = NO_ERROR;
2070
2071 bail:
2072 if (asset) {
2073 delete asset;
2074 }
2075 return (result != NO_ERROR);
2076 }
2077
2078
2079 /*
2080 * Handle the "add" command, which wants to add files to a new or
2081 * pre-existing archive.
2082 */
doAdd(Bundle * bundle)2083 int doAdd(Bundle* bundle)
2084 {
2085 ZipFile* zip = NULL;
2086 status_t result = UNKNOWN_ERROR;
2087 const char* zipFileName;
2088
2089 if (bundle->getUpdate()) {
2090 /* avoid confusion */
2091 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2092 goto bail;
2093 }
2094
2095 if (bundle->getFileSpecCount() < 1) {
2096 fprintf(stderr, "ERROR: must specify zip file name\n");
2097 goto bail;
2098 }
2099 zipFileName = bundle->getFileSpecEntry(0);
2100
2101 if (bundle->getFileSpecCount() < 2) {
2102 fprintf(stderr, "NOTE: nothing to do\n");
2103 goto bail;
2104 }
2105
2106 zip = openReadWrite(zipFileName, true);
2107 if (zip == NULL) {
2108 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2109 goto bail;
2110 }
2111
2112 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2113 const char* fileName = bundle->getFileSpecEntry(i);
2114
2115 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2116 printf(" '%s'... (from gzip)\n", fileName);
2117 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2118 } else {
2119 if (bundle->getJunkPath()) {
2120 String8 storageName = String8(fileName).getPathLeaf();
2121 printf(" '%s' as '%s'...\n", fileName,
2122 ResTable::normalizeForOutput(storageName.string()).string());
2123 result = zip->add(fileName, storageName.string(),
2124 bundle->getCompressionMethod(), NULL);
2125 } else {
2126 printf(" '%s'...\n", fileName);
2127 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2128 }
2129 }
2130 if (result != NO_ERROR) {
2131 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2132 if (result == NAME_NOT_FOUND) {
2133 fprintf(stderr, ": file not found\n");
2134 } else if (result == ALREADY_EXISTS) {
2135 fprintf(stderr, ": already exists in archive\n");
2136 } else {
2137 fprintf(stderr, "\n");
2138 }
2139 goto bail;
2140 }
2141 }
2142
2143 result = NO_ERROR;
2144
2145 bail:
2146 delete zip;
2147 return (result != NO_ERROR);
2148 }
2149
2150
2151 /*
2152 * Delete files from an existing archive.
2153 */
doRemove(Bundle * bundle)2154 int doRemove(Bundle* bundle)
2155 {
2156 ZipFile* zip = NULL;
2157 status_t result = UNKNOWN_ERROR;
2158 const char* zipFileName;
2159
2160 if (bundle->getFileSpecCount() < 1) {
2161 fprintf(stderr, "ERROR: must specify zip file name\n");
2162 goto bail;
2163 }
2164 zipFileName = bundle->getFileSpecEntry(0);
2165
2166 if (bundle->getFileSpecCount() < 2) {
2167 fprintf(stderr, "NOTE: nothing to do\n");
2168 goto bail;
2169 }
2170
2171 zip = openReadWrite(zipFileName, false);
2172 if (zip == NULL) {
2173 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2174 zipFileName);
2175 goto bail;
2176 }
2177
2178 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2179 const char* fileName = bundle->getFileSpecEntry(i);
2180 ZipEntry* entry;
2181
2182 entry = zip->getEntryByName(fileName);
2183 if (entry == NULL) {
2184 printf(" '%s' NOT FOUND\n", fileName);
2185 continue;
2186 }
2187
2188 result = zip->remove(entry);
2189
2190 if (result != NO_ERROR) {
2191 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2192 bundle->getFileSpecEntry(i), zipFileName);
2193 goto bail;
2194 }
2195 }
2196
2197 /* update the archive */
2198 zip->flush();
2199
2200 bail:
2201 delete zip;
2202 return (result != NO_ERROR);
2203 }
2204
addResourcesToBuilder(const sp<AaptDir> & dir,const sp<ApkBuilder> & builder,bool ignoreConfig=false)2205 static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
2206 const size_t numDirs = dir->getDirs().size();
2207 for (size_t i = 0; i < numDirs; i++) {
2208 bool ignore = ignoreConfig;
2209 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2210 const char* dirStr = subDir->getLeaf().string();
2211 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2212 ignore = true;
2213 }
2214 status_t err = addResourcesToBuilder(subDir, builder, ignore);
2215 if (err != NO_ERROR) {
2216 return err;
2217 }
2218 }
2219
2220 const size_t numFiles = dir->getFiles().size();
2221 for (size_t i = 0; i < numFiles; i++) {
2222 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2223 const size_t numConfigs = gp->getFiles().size();
2224 for (size_t j = 0; j < numConfigs; j++) {
2225 status_t err = NO_ERROR;
2226 if (ignoreConfig) {
2227 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2228 } else {
2229 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2230 }
2231 if (err != NO_ERROR) {
2232 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2233 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2234 return err;
2235 }
2236 }
2237 }
2238 return NO_ERROR;
2239 }
2240
buildApkName(const String8 & original,const sp<ApkSplit> & split)2241 static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2242 if (split->isBase()) {
2243 return original;
2244 }
2245
2246 String8 ext(original.getPathExtension());
2247 if (ext == String8(".apk")) {
2248 return String8::format("%s_%s%s",
2249 original.getBasePath().string(),
2250 split->getDirectorySafeName().string(),
2251 ext.string());
2252 }
2253
2254 return String8::format("%s_%s", original.string(),
2255 split->getDirectorySafeName().string());
2256 }
2257
2258 /*
2259 * Package up an asset directory and associated application files.
2260 */
doPackage(Bundle * bundle)2261 int doPackage(Bundle* bundle)
2262 {
2263 const char* outputAPKFile;
2264 int retVal = 1;
2265 status_t err;
2266 sp<AaptAssets> assets;
2267 int N;
2268 FILE* fp;
2269 String8 dependencyFile;
2270 sp<ApkBuilder> builder;
2271
2272 // -c en_XA or/and ar_XB means do pseudolocalization
2273 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2274 err = configFilter->parse(bundle->getConfigurations());
2275 if (err != NO_ERROR) {
2276 goto bail;
2277 }
2278 if (configFilter->containsPseudo()) {
2279 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2280 }
2281 if (configFilter->containsPseudoBidi()) {
2282 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
2283 }
2284
2285 N = bundle->getFileSpecCount();
2286 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
2287 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
2288 fprintf(stderr, "ERROR: no input files\n");
2289 goto bail;
2290 }
2291
2292 outputAPKFile = bundle->getOutputAPKFile();
2293
2294 // Make sure the filenames provided exist and are of the appropriate type.
2295 if (outputAPKFile) {
2296 FileType type;
2297 type = getFileType(outputAPKFile);
2298 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2299 fprintf(stderr,
2300 "ERROR: output file '%s' exists but is not regular file\n",
2301 outputAPKFile);
2302 goto bail;
2303 }
2304 }
2305
2306 // Load the assets.
2307 assets = new AaptAssets();
2308
2309 // Set up the resource gathering in assets if we're going to generate
2310 // dependency files. Every time we encounter a resource while slurping
2311 // the tree, we'll add it to these stores so we have full resource paths
2312 // to write to a dependency file.
2313 if (bundle->getGenDependencies()) {
2314 sp<FilePathStore> resPathStore = new FilePathStore;
2315 assets->setFullResPaths(resPathStore);
2316 sp<FilePathStore> assetPathStore = new FilePathStore;
2317 assets->setFullAssetPaths(assetPathStore);
2318 }
2319
2320 err = assets->slurpFromArgs(bundle);
2321 if (err < 0) {
2322 goto bail;
2323 }
2324
2325 if (bundle->getVerbose()) {
2326 assets->print(String8());
2327 }
2328
2329 // Create the ApkBuilder, which will collect the compiled files
2330 // to write to the final APK (or sets of APKs if we are building
2331 // a Split APK.
2332 builder = new ApkBuilder(configFilter);
2333
2334 // If we are generating a Split APK, find out which configurations to split on.
2335 if (bundle->getSplitConfigurations().size() > 0) {
2336 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2337 const size_t numSplits = splitStrs.size();
2338 for (size_t i = 0; i < numSplits; i++) {
2339 std::set<ConfigDescription> configs;
2340 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2341 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2342 goto bail;
2343 }
2344
2345 err = builder->createSplitForConfigs(configs);
2346 if (err != NO_ERROR) {
2347 goto bail;
2348 }
2349 }
2350 }
2351
2352 // If they asked for any fileAs that need to be compiled, do so.
2353 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
2354 err = buildResources(bundle, assets, builder);
2355 if (err != 0) {
2356 goto bail;
2357 }
2358 }
2359
2360 // At this point we've read everything and processed everything. From here
2361 // on out it's just writing output files.
2362 if (SourcePos::hasErrors()) {
2363 goto bail;
2364 }
2365
2366 // Update symbols with information about which ones are needed as Java symbols.
2367 assets->applyJavaSymbols();
2368 if (SourcePos::hasErrors()) {
2369 goto bail;
2370 }
2371
2372 // If we've been asked to generate a dependency file, do that here
2373 if (bundle->getGenDependencies()) {
2374 // If this is the packaging step, generate the dependency file next to
2375 // the output apk (e.g. bin/resources.ap_.d)
2376 if (outputAPKFile) {
2377 dependencyFile = String8(outputAPKFile);
2378 // Add the .d extension to the dependency file.
2379 dependencyFile.append(".d");
2380 } else {
2381 // Else if this is the R.java dependency generation step,
2382 // generate the dependency file in the R.java package subdirectory
2383 // e.g. gen/com/foo/app/R.java.d
2384 dependencyFile = String8(bundle->getRClassDir());
2385 dependencyFile.appendPath("R.java.d");
2386 }
2387 // Make sure we have a clean dependency file to start with
2388 fp = fopen(dependencyFile, "w");
2389 fclose(fp);
2390 }
2391
2392 // Write out R.java constants
2393 if (!assets->havePrivateSymbols()) {
2394 if (bundle->getCustomPackage() == NULL) {
2395 // Write the R.java file into the appropriate class directory
2396 // e.g. gen/com/foo/app/R.java
2397 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2398 bundle->getBuildSharedLibrary());
2399 } else {
2400 const String8 customPkg(bundle->getCustomPackage());
2401 err = writeResourceSymbols(bundle, assets, customPkg, true,
2402 bundle->getBuildSharedLibrary());
2403 }
2404 if (err < 0) {
2405 goto bail;
2406 }
2407 // If we have library files, we're going to write our R.java file into
2408 // the appropriate class directory for those libraries as well.
2409 // e.g. gen/com/foo/app/lib/R.java
2410 if (bundle->getExtraPackages() != NULL) {
2411 // Split on colon
2412 String8 libs(bundle->getExtraPackages());
2413 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2414 while (packageString != NULL) {
2415 // Write the R.java file out with the correct package name
2416 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
2417 bundle->getBuildSharedLibrary());
2418 if (err < 0) {
2419 goto bail;
2420 }
2421 packageString = strtok(NULL, ":");
2422 }
2423 libs.unlockBuffer();
2424 }
2425 } else {
2426 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
2427 if (err < 0) {
2428 goto bail;
2429 }
2430 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
2431 if (err < 0) {
2432 goto bail;
2433 }
2434 }
2435
2436 // Write out the ProGuard file
2437 err = writeProguardFile(bundle, assets);
2438 if (err < 0) {
2439 goto bail;
2440 }
2441
2442 // Write the apk
2443 if (outputAPKFile) {
2444 // Gather all resources and add them to the APK Builder. The builder will then
2445 // figure out which Split they belong in.
2446 err = addResourcesToBuilder(assets, builder);
2447 if (err != NO_ERROR) {
2448 goto bail;
2449 }
2450
2451 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2452 const size_t numSplits = splits.size();
2453 for (size_t i = 0; i < numSplits; i++) {
2454 const sp<ApkSplit>& split = splits[i];
2455 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2456 err = writeAPK(bundle, outputPath, split);
2457 if (err != NO_ERROR) {
2458 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2459 goto bail;
2460 }
2461 }
2462 }
2463
2464 // If we've been asked to generate a dependency file, we need to finish up here.
2465 // the writeResourceSymbols and writeAPK functions have already written the target
2466 // half of the dependency file, now we need to write the prerequisites. (files that
2467 // the R.java file or .ap_ file depend on)
2468 if (bundle->getGenDependencies()) {
2469 // Now that writeResourceSymbols or writeAPK has taken care of writing
2470 // the targets to our dependency file, we'll write the prereqs
2471 fp = fopen(dependencyFile, "a+");
2472 fprintf(fp, " : ");
2473 bool includeRaw = (outputAPKFile != NULL);
2474 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2475 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2476 // and therefore was not added to our pathstores during slurping
2477 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2478 fclose(fp);
2479 }
2480
2481 retVal = 0;
2482 bail:
2483 if (SourcePos::hasErrors()) {
2484 SourcePos::printErrors(stderr);
2485 }
2486 return retVal;
2487 }
2488
2489 /*
2490 * Do PNG Crunching
2491 * PRECONDITIONS
2492 * -S flag points to a source directory containing drawable* folders
2493 * -C flag points to destination directory. The folder structure in the
2494 * source directory will be mirrored to the destination (cache) directory
2495 *
2496 * POSTCONDITIONS
2497 * Destination directory will be updated to match the PNG files in
2498 * the source directory.
2499 */
doCrunch(Bundle * bundle)2500 int doCrunch(Bundle* bundle)
2501 {
2502 fprintf(stdout, "Crunching PNG Files in ");
2503 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2504 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2505
2506 updatePreProcessedCache(bundle);
2507
2508 return NO_ERROR;
2509 }
2510
2511 /*
2512 * Do PNG Crunching on a single flag
2513 * -i points to a single png file
2514 * -o points to a single png output file
2515 */
doSingleCrunch(Bundle * bundle)2516 int doSingleCrunch(Bundle* bundle)
2517 {
2518 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2519 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2520
2521 String8 input(bundle->getSingleCrunchInputFile());
2522 String8 output(bundle->getSingleCrunchOutputFile());
2523
2524 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2525 // we can't return the status_t as it gets truncate to the lower 8 bits.
2526 return 42;
2527 }
2528
2529 return NO_ERROR;
2530 }
2531
runInDaemonMode(Bundle * bundle)2532 int runInDaemonMode(Bundle* bundle) {
2533 std::cout << "Ready" << std::endl;
2534 for (std::string cmd; std::getline(std::cin, cmd);) {
2535 if (cmd == "quit") {
2536 return NO_ERROR;
2537 } else if (cmd == "s") {
2538 // Two argument crunch
2539 std::string inputFile, outputFile;
2540 std::getline(std::cin, inputFile);
2541 std::getline(std::cin, outputFile);
2542 bundle->setSingleCrunchInputFile(inputFile.c_str());
2543 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2544 std::cout << "Crunching " << inputFile << std::endl;
2545 if (doSingleCrunch(bundle) != NO_ERROR) {
2546 std::cout << "Error" << std::endl;
2547 }
2548 std::cout << "Done" << std::endl;
2549 } else {
2550 // in case of invalid command, just bail out.
2551 std::cerr << "Unknown command" << std::endl;
2552 return -1;
2553 }
2554 }
2555 return -1;
2556 }
2557
2558 char CONSOLE_DATA[2925] = {
2559 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2560 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2561 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2562 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2563 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2564 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2565 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2566 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2567 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2568 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2569 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2570 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2571 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2572 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2573 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2574 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2575 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2576 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2577 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2578 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2579 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2580 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2581 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2582 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2583 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2584 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2585 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2586 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2587 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2588 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2589 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2590 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2591 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2592 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2593 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2594 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2595 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2596 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2597 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2598 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2599 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2600 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2601 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2602 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2603 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2604 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2605 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2606 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2607 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2608 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2609 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2610 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2611 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2612 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2613 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2614 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2615 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2616 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2617 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2618 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2619 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2620 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2621 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2622 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2623 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2624 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2625 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2626 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2627 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2628 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2629 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2630 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2631 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2632 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2633 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2634 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2635 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2636 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2637 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2638 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2639 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2640 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2641 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2642 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2643 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2644 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2645 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2646 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2647 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2648 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2649 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2650 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2651 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2652 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2653 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2654 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2655 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2656 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2657 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2658 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2659 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2660 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2661 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2662 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2663 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2664 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2665 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2666 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2667 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2668 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2669 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2670 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2671 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2672 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2673 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2674 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2675 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2676 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2677 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2678 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2679 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2680 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2681 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2682 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2683 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2684 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2685 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2686 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2687 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2688 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2689 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2690 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2691 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2692 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2693 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2694 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2695 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2696 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2697 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2698 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2699 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2700 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2701 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2702 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2703 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2704 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2705 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2706 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2707 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2708 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2709 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2710 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2711 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2712 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2713 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2714 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2715 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2716 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2717 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2718 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2719 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2720 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2721 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2722 };
2723