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