blob: 7fa8c9d89b3ccd2f443d4e6e0a854db4a16454b5 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
6#include "Main.h"
7#include "Bundle.h"
8#include "ResourceFilter.h"
9#include "ResourceTable.h"
10#include "Images.h"
11#include "XMLNode.h"
12
13#include <utils/Log.h>
14#include <utils/threads.h>
15#include <utils/List.h>
16#include <utils/Errors.h>
17
18#include <fcntl.h>
19#include <errno.h>
20
21using namespace android;
22
23/*
24 * Show version info. All the cool kids do it.
25 */
26int doVersion(Bundle* bundle)
27{
28 if (bundle->getFileSpecCount() != 0) {
29 printf("(ignoring extra arguments)\n");
30 }
31 printf("Android Asset Packaging Tool, v0.2\n");
32
33 return 0;
34}
35
36
37/*
38 * Open the file read only. The call fails if the file doesn't exist.
39 *
40 * Returns NULL on failure.
41 */
42ZipFile* openReadOnly(const char* fileName)
43{
44 ZipFile* zip;
45 status_t result;
46
47 zip = new ZipFile;
48 result = zip->open(fileName, ZipFile::kOpenReadOnly);
49 if (result != NO_ERROR) {
50 if (result == NAME_NOT_FOUND) {
51 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
52 } else if (result == PERMISSION_DENIED) {
53 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
54 } else {
55 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
56 fileName);
57 }
58 delete zip;
59 return NULL;
60 }
61
62 return zip;
63}
64
65/*
66 * Open the file read-write. The file will be created if it doesn't
67 * already exist and "okayToCreate" is set.
68 *
69 * Returns NULL on failure.
70 */
71ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
72{
73 ZipFile* zip = NULL;
74 status_t result;
75 int flags;
76
77 flags = ZipFile::kOpenReadWrite;
78 if (okayToCreate) {
79 flags |= ZipFile::kOpenCreate;
80 }
81
82 zip = new ZipFile;
83 result = zip->open(fileName, flags);
84 if (result != NO_ERROR) {
85 delete zip;
86 zip = NULL;
87 goto bail;
88 }
89
90bail:
91 return zip;
92}
93
94
95/*
96 * Return a short string describing the compression method.
97 */
98const char* compressionName(int method)
99{
100 if (method == ZipEntry::kCompressStored) {
101 return "Stored";
102 } else if (method == ZipEntry::kCompressDeflated) {
103 return "Deflated";
104 } else {
105 return "Unknown";
106 }
107}
108
109/*
110 * Return the percent reduction in size (0% == no compression).
111 */
112int calcPercent(long uncompressedLen, long compressedLen)
113{
114 if (!uncompressedLen) {
115 return 0;
116 } else {
117 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
118 }
119}
120
121/*
122 * Handle the "list" command, which can be a simple file dump or
123 * a verbose listing.
124 *
125 * The verbose listing closely matches the output of the Info-ZIP "unzip"
126 * command.
127 */
128int doList(Bundle* bundle)
129{
130 int result = 1;
131 ZipFile* zip = NULL;
132 const ZipEntry* entry;
133 long totalUncLen, totalCompLen;
134 const char* zipFileName;
135
136 if (bundle->getFileSpecCount() != 1) {
137 fprintf(stderr, "ERROR: specify zip file name (only)\n");
138 goto bail;
139 }
140 zipFileName = bundle->getFileSpecEntry(0);
141
142 zip = openReadOnly(zipFileName);
143 if (zip == NULL) {
144 goto bail;
145 }
146
147 int count, i;
148
149 if (bundle->getVerbose()) {
150 printf("Archive: %s\n", zipFileName);
151 printf(
152 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
153 printf(
154 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
155 }
156
157 totalUncLen = totalCompLen = 0;
158
159 count = zip->getNumEntries();
160 for (i = 0; i < count; i++) {
161 entry = zip->getEntryByIndex(i);
162 if (bundle->getVerbose()) {
163 char dateBuf[32];
164 time_t when;
165
166 when = entry->getModWhen();
167 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
168 localtime(&when));
169
170 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
171 (long) entry->getUncompressedLen(),
172 compressionName(entry->getCompressionMethod()),
173 (long) entry->getCompressedLen(),
174 calcPercent(entry->getUncompressedLen(),
175 entry->getCompressedLen()),
176 (size_t) entry->getLFHOffset(),
177 dateBuf,
178 entry->getCRC32(),
179 entry->getFileName());
180 } else {
181 printf("%s\n", entry->getFileName());
182 }
183
184 totalUncLen += entry->getUncompressedLen();
185 totalCompLen += entry->getCompressedLen();
186 }
187
188 if (bundle->getVerbose()) {
189 printf(
190 "-------- ------- --- -------\n");
191 printf("%8ld %7ld %2d%% %d files\n",
192 totalUncLen,
193 totalCompLen,
194 calcPercent(totalUncLen, totalCompLen),
195 zip->getNumEntries());
196 }
197
198 if (bundle->getAndroidList()) {
199 AssetManager assets;
200 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
201 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
202 goto bail;
203 }
204
205 const ResTable& res = assets.getResources(false);
206 if (&res == NULL) {
207 printf("\nNo resource table found.\n");
208 } else {
209#ifndef HAVE_ANDROID_OS
210 printf("\nResource table:\n");
211 res.print(false);
212#endif
213 }
214
215 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
216 Asset::ACCESS_BUFFER);
217 if (manifestAsset == NULL) {
218 printf("\nNo AndroidManifest.xml found.\n");
219 } else {
220 printf("\nAndroid manifest:\n");
221 ResXMLTree tree;
222 tree.setTo(manifestAsset->getBuffer(true),
223 manifestAsset->getLength());
224 printXMLBlock(&tree);
225 }
226 delete manifestAsset;
227 }
228
229 result = 0;
230
231bail:
232 delete zip;
233 return result;
234}
235
236static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
237{
238 size_t N = tree.getAttributeCount();
239 for (size_t i=0; i<N; i++) {
240 if (tree.getAttributeNameResID(i) == attrRes) {
241 return (ssize_t)i;
242 }
243 }
244 return -1;
245}
246
247String8 getAttribute(const ResXMLTree& tree, const char* ns,
248 const char* attr, String8* outError)
249{
250 ssize_t idx = tree.indexOfAttribute(ns, attr);
251 if (idx < 0) {
252 return String8();
253 }
254 Res_value value;
255 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
256 if (value.dataType != Res_value::TYPE_STRING) {
257 if (outError != NULL) {
258 *outError = "attribute is not a string value";
259 }
260 return String8();
261 }
262 }
263 size_t len;
264 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
265 return str ? String8(str, len) : String8();
266}
267
268static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
269{
270 ssize_t idx = indexOfAttribute(tree, attrRes);
271 if (idx < 0) {
272 return String8();
273 }
274 Res_value value;
275 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
276 if (value.dataType != Res_value::TYPE_STRING) {
277 if (outError != NULL) {
278 *outError = "attribute is not a string value";
279 }
280 return String8();
281 }
282 }
283 size_t len;
284 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
285 return str ? String8(str, len) : String8();
286}
287
288static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
289 String8* outError, int32_t defValue = -1)
290{
291 ssize_t idx = indexOfAttribute(tree, attrRes);
292 if (idx < 0) {
293 return defValue;
294 }
295 Res_value value;
296 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
297 if (value.dataType < Res_value::TYPE_FIRST_INT
298 || value.dataType > Res_value::TYPE_LAST_INT) {
299 if (outError != NULL) {
300 *outError = "attribute is not an integer value";
301 }
302 return defValue;
303 }
304 }
305 return value.data;
306}
307
308static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
309 uint32_t attrRes, String8* outError, int32_t defValue = -1)
310{
311 ssize_t idx = indexOfAttribute(tree, attrRes);
312 if (idx < 0) {
313 return defValue;
314 }
315 Res_value value;
316 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
317 if (value.dataType == Res_value::TYPE_REFERENCE) {
318 resTable->resolveReference(&value, 0);
319 }
320 if (value.dataType < Res_value::TYPE_FIRST_INT
321 || value.dataType > Res_value::TYPE_LAST_INT) {
322 if (outError != NULL) {
323 *outError = "attribute is not an integer value";
324 }
325 return defValue;
326 }
327 }
328 return value.data;
329}
330
331static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
332 uint32_t attrRes, String8* outError)
333{
334 ssize_t idx = indexOfAttribute(tree, attrRes);
335 if (idx < 0) {
336 return String8();
337 }
338 Res_value value;
339 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
340 if (value.dataType == Res_value::TYPE_STRING) {
341 size_t len;
342 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
343 return str ? String8(str, len) : String8();
344 }
345 resTable->resolveReference(&value, 0);
346 if (value.dataType != Res_value::TYPE_STRING) {
347 if (outError != NULL) {
348 *outError = "attribute is not a string value";
349 }
350 return String8();
351 }
352 }
353 size_t len;
354 const Res_value* value2 = &value;
355 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
356 return str ? String8(str, len) : String8();
357}
358
359static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
360 const ResXMLTree& tree, uint32_t attrRes, String8* outError)
361{
362 ssize_t idx = indexOfAttribute(tree, attrRes);
363 if (idx < 0) {
364 if (outError != NULL) {
365 *outError = "attribute could not be found";
366 }
367 return;
368 }
369 if (tree.getAttributeValue(idx, value) != NO_ERROR) {
370 if (value->dataType == Res_value::TYPE_REFERENCE) {
371 resTable->resolveReference(value, 0);
372 }
373 // The attribute was found and was resolved if need be.
374 return;
375 }
376 if (outError != NULL) {
377 *outError = "error getting resolved resource attribute";
378 }
379}
380
381// These are attribute resource constants for the platform, as found
382// in android.R.attr
383enum {
384 LABEL_ATTR = 0x01010001,
385 ICON_ATTR = 0x01010002,
386 NAME_ATTR = 0x01010003,
387 DEBUGGABLE_ATTR = 0x0101000f,
388 VALUE_ATTR = 0x01010024,
389 VERSION_CODE_ATTR = 0x0101021b,
390 VERSION_NAME_ATTR = 0x0101021c,
391 SCREEN_ORIENTATION_ATTR = 0x0101001e,
392 MIN_SDK_VERSION_ATTR = 0x0101020c,
393 MAX_SDK_VERSION_ATTR = 0x01010271,
394 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
395 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
396 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
397 REQ_NAVIGATION_ATTR = 0x0101022a,
398 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
399 TARGET_SDK_VERSION_ATTR = 0x01010270,
400 TEST_ONLY_ATTR = 0x01010272,
401 ANY_DENSITY_ATTR = 0x0101026c,
402 GL_ES_VERSION_ATTR = 0x01010281,
403 SMALL_SCREEN_ATTR = 0x01010284,
404 NORMAL_SCREEN_ATTR = 0x01010285,
405 LARGE_SCREEN_ATTR = 0x01010286,
406 XLARGE_SCREEN_ATTR = 0x010102bf,
407 REQUIRED_ATTR = 0x0101028e,
408 SCREEN_SIZE_ATTR = 0x010102ca,
409 SCREEN_DENSITY_ATTR = 0x010102cb,
410 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
411 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
412 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
413 PUBLIC_KEY_ATTR = 0x010103a6,
414};
415
416const char *getComponentName(String8 &pkgName, String8 &componentName) {
417 ssize_t idx = componentName.find(".");
418 String8 retStr(pkgName);
419 if (idx == 0) {
420 retStr += componentName;
421 } else if (idx < 0) {
422 retStr += ".";
423 retStr += componentName;
424 } else {
425 return componentName.string();
426 }
427 return retStr.string();
428}
429
430static void printCompatibleScreens(ResXMLTree& tree) {
431 size_t len;
432 ResXMLTree::event_code_t code;
433 int depth = 0;
434 bool first = true;
435 printf("compatible-screens:");
436 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
437 if (code == ResXMLTree::END_TAG) {
438 depth--;
439 if (depth < 0) {
440 break;
441 }
442 continue;
443 }
444 if (code != ResXMLTree::START_TAG) {
445 continue;
446 }
447 depth++;
448 String8 tag(tree.getElementName(&len));
449 if (tag == "screen") {
450 int32_t screenSize = getIntegerAttribute(tree,
451 SCREEN_SIZE_ATTR, NULL, -1);
452 int32_t screenDensity = getIntegerAttribute(tree,
453 SCREEN_DENSITY_ATTR, NULL, -1);
454 if (screenSize > 0 && screenDensity > 0) {
455 if (!first) {
456 printf(",");
457 }
458 first = false;
459 printf("'%d/%d'", screenSize, screenDensity);
460 }
461 }
462 }
463 printf("\n");
464}
465
466/*
467 * Handle the "dump" command, to extract select data from an archive.
468 */
469extern char CONSOLE_DATA[2925]; // see EOF
470int doDump(Bundle* bundle)
471{
472 status_t result = UNKNOWN_ERROR;
473 Asset* asset = NULL;
474
475 if (bundle->getFileSpecCount() < 1) {
476 fprintf(stderr, "ERROR: no dump option specified\n");
477 return 1;
478 }
479
480 if (bundle->getFileSpecCount() < 2) {
481 fprintf(stderr, "ERROR: no dump file specified\n");
482 return 1;
483 }
484
485 const char* option = bundle->getFileSpecEntry(0);
486 const char* filename = bundle->getFileSpecEntry(1);
487
488 AssetManager assets;
489 void* assetsCookie;
490 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
491 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
492 return 1;
493 }
494
495 // Make a dummy config for retrieving resources... we need to supply
496 // non-default values for some configs so that we can retrieve resources
497 // in the app that don't have a default. The most important of these is
498 // the API version because key resources like icons will have an implicit
499 // version if they are using newer config types like density.
500 ResTable_config config;
501 config.language[0] = 'e';
502 config.language[1] = 'n';
503 config.country[0] = 'U';
504 config.country[1] = 'S';
505 config.orientation = ResTable_config::ORIENTATION_PORT;
506 config.density = ResTable_config::DENSITY_MEDIUM;
507 config.sdkVersion = 10000; // Very high.
508 config.screenWidthDp = 320;
509 config.screenHeightDp = 480;
510 config.smallestScreenWidthDp = 320;
511 assets.setConfiguration(config);
512
513 const ResTable& res = assets.getResources(false);
514 if (&res == NULL) {
515 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
516 goto bail;
517 }
518
519 if (strcmp("resources", option) == 0) {
520#ifndef HAVE_ANDROID_OS
521 res.print(bundle->getValues());
522#endif
523
524 } else if (strcmp("strings", option) == 0) {
525 const ResStringPool* pool = res.getTableStringBlock(0);
526 printStringPool(pool);
527
528 } else if (strcmp("xmltree", option) == 0) {
529 if (bundle->getFileSpecCount() < 3) {
530 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
531 goto bail;
532 }
533
534 for (int i=2; i<bundle->getFileSpecCount(); i++) {
535 const char* resname = bundle->getFileSpecEntry(i);
536 ResXMLTree tree;
537 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
538 if (asset == NULL) {
539 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
540 goto bail;
541 }
542
543 if (tree.setTo(asset->getBuffer(true),
544 asset->getLength()) != NO_ERROR) {
545 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
546 goto bail;
547 }
548 tree.restart();
549 printXMLBlock(&tree);
550 tree.uninit();
551 delete asset;
552 asset = NULL;
553 }
554
555 } else if (strcmp("xmlstrings", option) == 0) {
556 if (bundle->getFileSpecCount() < 3) {
557 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
558 goto bail;
559 }
560
561 for (int i=2; i<bundle->getFileSpecCount(); i++) {
562 const char* resname = bundle->getFileSpecEntry(i);
563 ResXMLTree tree;
564 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
565 if (asset == NULL) {
566 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
567 goto bail;
568 }
569
570 if (tree.setTo(asset->getBuffer(true),
571 asset->getLength()) != NO_ERROR) {
572 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
573 goto bail;
574 }
575 printStringPool(&tree.getStrings());
576 delete asset;
577 asset = NULL;
578 }
579
580 } else {
581 ResXMLTree tree;
582 asset = assets.openNonAsset("AndroidManifest.xml",
583 Asset::ACCESS_BUFFER);
584 if (asset == NULL) {
585 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
586 goto bail;
587 }
588
589 if (tree.setTo(asset->getBuffer(true),
590 asset->getLength()) != NO_ERROR) {
591 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
592 goto bail;
593 }
594 tree.restart();
595
596 if (strcmp("permissions", option) == 0) {
597 size_t len;
598 ResXMLTree::event_code_t code;
599 int depth = 0;
600 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
601 if (code == ResXMLTree::END_TAG) {
602 depth--;
603 continue;
604 }
605 if (code != ResXMLTree::START_TAG) {
606 continue;
607 }
608 depth++;
609 String8 tag(tree.getElementName(&len));
610 //printf("Depth %d tag %s\n", depth, tag.string());
611 if (depth == 1) {
612 if (tag != "manifest") {
613 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
614 goto bail;
615 }
616 String8 pkg = getAttribute(tree, NULL, "package", NULL);
617 printf("package: %s\n", pkg.string());
618 } else if (depth == 2 && tag == "permission") {
619 String8 error;
620 String8 name = getAttribute(tree, NAME_ATTR, &error);
621 if (error != "") {
622 fprintf(stderr, "ERROR: %s\n", error.string());
623 goto bail;
624 }
625 printf("permission: %s\n", name.string());
626 } else if (depth == 2 && tag == "uses-permission") {
627 String8 error;
628 String8 name = getAttribute(tree, NAME_ATTR, &error);
629 if (error != "") {
630 fprintf(stderr, "ERROR: %s\n", error.string());
631 goto bail;
632 }
633 printf("uses-permission: %s\n", name.string());
634 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
635 if (!req) {
636 printf("optional-permission: %s\n", name.string());
637 }
638 }
639 }
640 } else if (strcmp("badging", option) == 0) {
641 Vector<String8> locales;
642 res.getLocales(&locales);
643
644 Vector<ResTable_config> configs;
645 res.getConfigurations(&configs);
646 SortedVector<int> densities;
647 const size_t NC = configs.size();
648 for (size_t i=0; i<NC; i++) {
649 int dens = configs[i].density;
650 if (dens == 0) {
651 dens = 160;
652 }
653 densities.add(dens);
654 }
655
656 size_t len;
657 ResXMLTree::event_code_t code;
658 int depth = 0;
659 String8 error;
660 bool withinActivity = false;
661 bool isMainActivity = false;
662 bool isLauncherActivity = false;
663 bool isSearchable = false;
664 bool withinApplication = false;
665 bool withinReceiver = false;
666 bool withinService = false;
667 bool withinIntentFilter = false;
668 bool hasMainActivity = false;
669 bool hasOtherActivities = false;
670 bool hasOtherReceivers = false;
671 bool hasOtherServices = false;
672 bool hasWallpaperService = false;
673 bool hasImeService = false;
674 bool hasWidgetReceivers = false;
675 bool hasIntentFilter = false;
676 bool actMainActivity = false;
677 bool actWidgetReceivers = false;
678 bool actImeService = false;
679 bool actWallpaperService = false;
680
681 // These two implement the implicit permissions that are granted
682 // to pre-1.6 applications.
683 bool hasWriteExternalStoragePermission = false;
684 bool hasReadPhoneStatePermission = false;
685
686 // If an app requests write storage, they will also get read storage.
687 bool hasReadExternalStoragePermission = false;
688
689 // Implement transition to read and write call log.
690 bool hasReadContactsPermission = false;
691 bool hasWriteContactsPermission = false;
692 bool hasReadCallLogPermission = false;
693 bool hasWriteCallLogPermission = false;
694
695 // This next group of variables is used to implement a group of
696 // backward-compatibility heuristics necessitated by the addition of
697 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
698 // heuristic is "if an app requests a permission but doesn't explicitly
699 // request the corresponding <uses-feature>, presume it's there anyway".
700 bool specCameraFeature = false; // camera-related
701 bool specCameraAutofocusFeature = false;
702 bool reqCameraAutofocusFeature = false;
703 bool reqCameraFlashFeature = false;
704 bool hasCameraPermission = false;
705 bool specLocationFeature = false; // location-related
706 bool specNetworkLocFeature = false;
707 bool reqNetworkLocFeature = false;
708 bool specGpsFeature = false;
709 bool reqGpsFeature = false;
710 bool hasMockLocPermission = false;
711 bool hasCoarseLocPermission = false;
712 bool hasGpsPermission = false;
713 bool hasGeneralLocPermission = false;
714 bool specBluetoothFeature = false; // Bluetooth API-related
715 bool hasBluetoothPermission = false;
716 bool specMicrophoneFeature = false; // microphone-related
717 bool hasRecordAudioPermission = false;
718 bool specWiFiFeature = false;
719 bool hasWiFiPermission = false;
720 bool specTelephonyFeature = false; // telephony-related
721 bool reqTelephonySubFeature = false;
722 bool hasTelephonyPermission = false;
723 bool specTouchscreenFeature = false; // touchscreen-related
724 bool specMultitouchFeature = false;
725 bool reqDistinctMultitouchFeature = false;
726 bool specScreenPortraitFeature = false;
727 bool specScreenLandscapeFeature = false;
728 bool reqScreenPortraitFeature = false;
729 bool reqScreenLandscapeFeature = false;
730 // 2.2 also added some other features that apps can request, but that
731 // have no corresponding permission, so we cannot implement any
732 // back-compatibility heuristic for them. The below are thus unnecessary
733 // (but are retained here for documentary purposes.)
734 //bool specCompassFeature = false;
735 //bool specAccelerometerFeature = false;
736 //bool specProximityFeature = false;
737 //bool specAmbientLightFeature = false;
738 //bool specLiveWallpaperFeature = false;
739
740 int targetSdk = 0;
741 int smallScreen = 1;
742 int normalScreen = 1;
743 int largeScreen = 1;
744 int xlargeScreen = 1;
745 int anyDensity = 1;
746 int requiresSmallestWidthDp = 0;
747 int compatibleWidthLimitDp = 0;
748 int largestWidthLimitDp = 0;
749 String8 pkg;
750 String8 activityName;
751 String8 activityLabel;
752 String8 activityIcon;
753 String8 receiverName;
754 String8 serviceName;
755 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
756 if (code == ResXMLTree::END_TAG) {
757 depth--;
758 if (depth < 2) {
759 withinApplication = false;
760 } else if (depth < 3) {
761 if (withinActivity && isMainActivity && isLauncherActivity) {
762 const char *aName = getComponentName(pkg, activityName);
763 printf("launchable-activity:");
764 if (aName != NULL) {
765 printf(" name='%s' ", aName);
766 }
767 printf(" label='%s' icon='%s'\n",
768 activityLabel.string(),
769 activityIcon.string());
770 }
771 if (!hasIntentFilter) {
772 hasOtherActivities |= withinActivity;
773 hasOtherReceivers |= withinReceiver;
774 hasOtherServices |= withinService;
775 }
776 withinActivity = false;
777 withinService = false;
778 withinReceiver = false;
779 hasIntentFilter = false;
780 isMainActivity = isLauncherActivity = false;
781 } else if (depth < 4) {
782 if (withinIntentFilter) {
783 if (withinActivity) {
784 hasMainActivity |= actMainActivity;
785 hasOtherActivities |= !actMainActivity;
786 } else if (withinReceiver) {
787 hasWidgetReceivers |= actWidgetReceivers;
788 hasOtherReceivers |= !actWidgetReceivers;
789 } else if (withinService) {
790 hasImeService |= actImeService;
791 hasWallpaperService |= actWallpaperService;
792 hasOtherServices |= (!actImeService && !actWallpaperService);
793 }
794 }
795 withinIntentFilter = false;
796 }
797 continue;
798 }
799 if (code != ResXMLTree::START_TAG) {
800 continue;
801 }
802 depth++;
803 String8 tag(tree.getElementName(&len));
804 //printf("Depth %d, %s\n", depth, tag.string());
805 if (depth == 1) {
806 if (tag != "manifest") {
807 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
808 goto bail;
809 }
810 pkg = getAttribute(tree, NULL, "package", NULL);
811 printf("package: name='%s' ", pkg.string());
812 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
813 if (error != "") {
814 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
815 goto bail;
816 }
817 if (versionCode > 0) {
818 printf("versionCode='%d' ", versionCode);
819 } else {
820 printf("versionCode='' ");
821 }
822 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
823 if (error != "") {
824 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
825 goto bail;
826 }
827 printf("versionName='%s'\n", versionName.string());
828 } else if (depth == 2) {
829 withinApplication = false;
830 if (tag == "application") {
831 withinApplication = true;
832
833 String8 label;
834 const size_t NL = locales.size();
835 for (size_t i=0; i<NL; i++) {
836 const char* localeStr = locales[i].string();
837 assets.setLocale(localeStr != NULL ? localeStr : "");
838 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
839 if (llabel != "") {
840 if (localeStr == NULL || strlen(localeStr) == 0) {
841 label = llabel;
842 printf("application-label:'%s'\n", llabel.string());
843 } else {
844 if (label == "") {
845 label = llabel;
846 }
847 printf("application-label-%s:'%s'\n", localeStr,
848 llabel.string());
849 }
850 }
851 }
852
853 ResTable_config tmpConfig = config;
854 const size_t ND = densities.size();
855 for (size_t i=0; i<ND; i++) {
856 tmpConfig.density = densities[i];
857 assets.setConfiguration(tmpConfig);
858 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
859 if (icon != "") {
860 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
861 }
862 }
863 assets.setConfiguration(config);
864
865 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
866 if (error != "") {
867 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
868 goto bail;
869 }
870 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
871 if (error != "") {
872 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
873 goto bail;
874 }
875 printf("application: label='%s' ", label.string());
876 printf("icon='%s'\n", icon.string());
877 if (testOnly != 0) {
878 printf("testOnly='%d'\n", testOnly);
879 }
880
881 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
882 if (error != "") {
883 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
884 goto bail;
885 }
886 if (debuggable != 0) {
887 printf("application-debuggable\n");
888 }
889 } else if (tag == "uses-sdk") {
890 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
891 if (error != "") {
892 error = "";
893 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
894 if (error != "") {
895 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
896 error.string());
897 goto bail;
898 }
899 if (name == "Donut") targetSdk = 4;
900 printf("sdkVersion:'%s'\n", name.string());
901 } else if (code != -1) {
902 targetSdk = code;
903 printf("sdkVersion:'%d'\n", code);
904 }
905 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
906 if (code != -1) {
907 printf("maxSdkVersion:'%d'\n", code);
908 }
909 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
910 if (error != "") {
911 error = "";
912 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
913 if (error != "") {
914 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
915 error.string());
916 goto bail;
917 }
918 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
919 printf("targetSdkVersion:'%s'\n", name.string());
920 } else if (code != -1) {
921 if (targetSdk < code) {
922 targetSdk = code;
923 }
924 printf("targetSdkVersion:'%d'\n", code);
925 }
926 } else if (tag == "uses-configuration") {
927 int32_t reqTouchScreen = getIntegerAttribute(tree,
928 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
929 int32_t reqKeyboardType = getIntegerAttribute(tree,
930 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
931 int32_t reqHardKeyboard = getIntegerAttribute(tree,
932 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
933 int32_t reqNavigation = getIntegerAttribute(tree,
934 REQ_NAVIGATION_ATTR, NULL, 0);
935 int32_t reqFiveWayNav = getIntegerAttribute(tree,
936 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
937 printf("uses-configuration:");
938 if (reqTouchScreen != 0) {
939 printf(" reqTouchScreen='%d'", reqTouchScreen);
940 }
941 if (reqKeyboardType != 0) {
942 printf(" reqKeyboardType='%d'", reqKeyboardType);
943 }
944 if (reqHardKeyboard != 0) {
945 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
946 }
947 if (reqNavigation != 0) {
948 printf(" reqNavigation='%d'", reqNavigation);
949 }
950 if (reqFiveWayNav != 0) {
951 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
952 }
953 printf("\n");
954 } else if (tag == "supports-screens") {
955 smallScreen = getIntegerAttribute(tree,
956 SMALL_SCREEN_ATTR, NULL, 1);
957 normalScreen = getIntegerAttribute(tree,
958 NORMAL_SCREEN_ATTR, NULL, 1);
959 largeScreen = getIntegerAttribute(tree,
960 LARGE_SCREEN_ATTR, NULL, 1);
961 xlargeScreen = getIntegerAttribute(tree,
962 XLARGE_SCREEN_ATTR, NULL, 1);
963 anyDensity = getIntegerAttribute(tree,
964 ANY_DENSITY_ATTR, NULL, 1);
965 requiresSmallestWidthDp = getIntegerAttribute(tree,
966 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
967 compatibleWidthLimitDp = getIntegerAttribute(tree,
968 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
969 largestWidthLimitDp = getIntegerAttribute(tree,
970 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
971 } else if (tag == "uses-feature") {
972 String8 name = getAttribute(tree, NAME_ATTR, &error);
973
974 if (name != "" && error == "") {
975 int req = getIntegerAttribute(tree,
976 REQUIRED_ATTR, NULL, 1);
977
978 if (name == "android.hardware.camera") {
979 specCameraFeature = true;
980 } else if (name == "android.hardware.camera.autofocus") {
981 // these have no corresponding permission to check for,
982 // but should imply the foundational camera permission
983 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
984 specCameraAutofocusFeature = true;
985 } else if (req && (name == "android.hardware.camera.flash")) {
986 // these have no corresponding permission to check for,
987 // but should imply the foundational camera permission
988 reqCameraFlashFeature = true;
989 } else if (name == "android.hardware.location") {
990 specLocationFeature = true;
991 } else if (name == "android.hardware.location.network") {
992 specNetworkLocFeature = true;
993 reqNetworkLocFeature = reqNetworkLocFeature || req;
994 } else if (name == "android.hardware.location.gps") {
995 specGpsFeature = true;
996 reqGpsFeature = reqGpsFeature || req;
997 } else if (name == "android.hardware.bluetooth") {
998 specBluetoothFeature = true;
999 } else if (name == "android.hardware.touchscreen") {
1000 specTouchscreenFeature = true;
1001 } else if (name == "android.hardware.touchscreen.multitouch") {
1002 specMultitouchFeature = true;
1003 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1004 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1005 } else if (name == "android.hardware.microphone") {
1006 specMicrophoneFeature = true;
1007 } else if (name == "android.hardware.wifi") {
1008 specWiFiFeature = true;
1009 } else if (name == "android.hardware.telephony") {
1010 specTelephonyFeature = true;
1011 } else if (req && (name == "android.hardware.telephony.gsm" ||
1012 name == "android.hardware.telephony.cdma")) {
1013 // these have no corresponding permission to check for,
1014 // but should imply the foundational telephony permission
1015 reqTelephonySubFeature = true;
1016 } else if (name == "android.hardware.screen.portrait") {
1017 specScreenPortraitFeature = true;
1018 } else if (name == "android.hardware.screen.landscape") {
1019 specScreenLandscapeFeature = true;
1020 }
1021 printf("uses-feature%s:'%s'\n",
1022 req ? "" : "-not-required", name.string());
1023 } else {
1024 int vers = getIntegerAttribute(tree,
1025 GL_ES_VERSION_ATTR, &error);
1026 if (error == "") {
1027 printf("uses-gl-es:'0x%x'\n", vers);
1028 }
1029 }
1030 } else if (tag == "uses-permission") {
1031 String8 name = getAttribute(tree, NAME_ATTR, &error);
1032 if (name != "" && error == "") {
1033 if (name == "android.permission.CAMERA") {
1034 hasCameraPermission = true;
1035 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1036 hasGpsPermission = true;
1037 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1038 hasMockLocPermission = true;
1039 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1040 hasCoarseLocPermission = true;
1041 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1042 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1043 hasGeneralLocPermission = true;
1044 } else if (name == "android.permission.BLUETOOTH" ||
1045 name == "android.permission.BLUETOOTH_ADMIN") {
1046 hasBluetoothPermission = true;
1047 } else if (name == "android.permission.RECORD_AUDIO") {
1048 hasRecordAudioPermission = true;
1049 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1050 name == "android.permission.CHANGE_WIFI_STATE" ||
1051 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1052 hasWiFiPermission = true;
1053 } else if (name == "android.permission.CALL_PHONE" ||
1054 name == "android.permission.CALL_PRIVILEGED" ||
1055 name == "android.permission.MODIFY_PHONE_STATE" ||
1056 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1057 name == "android.permission.READ_SMS" ||
1058 name == "android.permission.RECEIVE_SMS" ||
1059 name == "android.permission.RECEIVE_MMS" ||
1060 name == "android.permission.RECEIVE_WAP_PUSH" ||
1061 name == "android.permission.SEND_SMS" ||
1062 name == "android.permission.WRITE_APN_SETTINGS" ||
1063 name == "android.permission.WRITE_SMS") {
1064 hasTelephonyPermission = true;
1065 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1066 hasWriteExternalStoragePermission = true;
1067 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1068 hasReadExternalStoragePermission = true;
1069 } else if (name == "android.permission.READ_PHONE_STATE") {
1070 hasReadPhoneStatePermission = true;
1071 } else if (name == "android.permission.READ_CONTACTS") {
1072 hasReadContactsPermission = true;
1073 } else if (name == "android.permission.WRITE_CONTACTS") {
1074 hasWriteContactsPermission = true;
1075 } else if (name == "android.permission.READ_CALL_LOG") {
1076 hasReadCallLogPermission = true;
1077 } else if (name == "android.permission.WRITE_CALL_LOG") {
1078 hasWriteCallLogPermission = true;
1079 }
1080 printf("uses-permission:'%s'\n", name.string());
1081 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1082 if (!req) {
1083 printf("optional-permission:'%s'\n", name.string());
1084 }
1085 } else {
1086 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1087 error.string());
1088 goto bail;
1089 }
1090 } else if (tag == "uses-package") {
1091 String8 name = getAttribute(tree, NAME_ATTR, &error);
1092 if (name != "" && error == "") {
1093 printf("uses-package:'%s'\n", name.string());
1094 } else {
1095 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1096 error.string());
1097 goto bail;
1098 }
1099 } else if (tag == "original-package") {
1100 String8 name = getAttribute(tree, NAME_ATTR, &error);
1101 if (name != "" && error == "") {
1102 printf("original-package:'%s'\n", name.string());
1103 } else {
1104 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1105 error.string());
1106 goto bail;
1107 }
1108 } else if (tag == "supports-gl-texture") {
1109 String8 name = getAttribute(tree, NAME_ATTR, &error);
1110 if (name != "" && error == "") {
1111 printf("supports-gl-texture:'%s'\n", name.string());
1112 } else {
1113 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1114 error.string());
1115 goto bail;
1116 }
1117 } else if (tag == "compatible-screens") {
1118 printCompatibleScreens(tree);
1119 depth--;
1120 } else if (tag == "package-verifier") {
1121 String8 name = getAttribute(tree, NAME_ATTR, &error);
1122 if (name != "" && error == "") {
1123 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1124 if (publicKey != "" && error == "") {
1125 printf("package-verifier: name='%s' publicKey='%s'\n",
1126 name.string(), publicKey.string());
1127 }
1128 }
1129 }
1130 } else if (depth == 3 && withinApplication) {
1131 withinActivity = false;
1132 withinReceiver = false;
1133 withinService = false;
1134 hasIntentFilter = false;
1135 if(tag == "activity") {
1136 withinActivity = true;
1137 activityName = getAttribute(tree, NAME_ATTR, &error);
1138 if (error != "") {
1139 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1140 goto bail;
1141 }
1142
1143 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1144 if (error != "") {
1145 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1146 goto bail;
1147 }
1148
1149 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1150 if (error != "") {
1151 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1152 goto bail;
1153 }
1154
1155 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1156 SCREEN_ORIENTATION_ATTR, &error);
1157 if (error == "") {
1158 if (orien == 0 || orien == 6 || orien == 8) {
1159 // Requests landscape, sensorLandscape, or reverseLandscape.
1160 reqScreenLandscapeFeature = true;
1161 } else if (orien == 1 || orien == 7 || orien == 9) {
1162 // Requests portrait, sensorPortrait, or reversePortrait.
1163 reqScreenPortraitFeature = true;
1164 }
1165 }
1166 } else if (tag == "uses-library") {
1167 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1168 if (error != "") {
1169 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1170 goto bail;
1171 }
1172 int req = getIntegerAttribute(tree,
1173 REQUIRED_ATTR, NULL, 1);
1174 printf("uses-library%s:'%s'\n",
1175 req ? "" : "-not-required", libraryName.string());
1176 } else if (tag == "receiver") {
1177 withinReceiver = true;
1178 receiverName = getAttribute(tree, NAME_ATTR, &error);
1179
1180 if (error != "") {
1181 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1182 "receiver:%s\n", error.string());
1183 goto bail;
1184 }
1185 } else if (tag == "service") {
1186 withinService = true;
1187 serviceName = getAttribute(tree, NAME_ATTR, &error);
1188
1189 if (error != "") {
1190 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1191 "service:%s\n", error.string());
1192 goto bail;
1193 }
1194 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1195 String8 metaDataName = getAttribute(tree, NAME_ATTR, &error);
1196 if (error != "") {
1197 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1198 "meta-data:%s\n", error.string());
1199 goto bail;
1200 }
1201 printf("meta-data: name='%s' ", metaDataName.string());
1202 Res_value value;
1203 getResolvedResourceAttribute(&value, &res, tree, VALUE_ATTR, &error);
1204 if (error != "") {
1205 fprintf(stderr, "ERROR getting 'android:value' attribute for "
1206 "meta-data:%s\n", error.string());
1207 goto bail;
1208 }
1209 if (value.dataType == Res_value::TYPE_STRING) {
1210 String8 metaDataValue = getAttribute(tree, value.data, &error);
1211 if (error != "") {
1212 fprintf(stderr, "ERROR getting 'android:value' attribute for "
1213 "meta-data: %s\n", error.string());
1214 goto bail;
1215 }
1216 printf("value='%s'\n", metaDataValue.string());
1217 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
1218 value.dataType <= Res_value::TYPE_LAST_INT) {
1219 printf("value='%d'\n", value.data);
1220 } else {
1221 printf("value=(type 0x%x)0x%x", (int)value.dataType, (int)value.data);
1222 }
1223 }
1224 } else if ((depth == 4) && (tag == "intent-filter")) {
1225 hasIntentFilter = true;
1226 withinIntentFilter = true;
1227 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService =
1228 false;
1229 } else if ((depth == 5) && withinIntentFilter) {
1230 String8 action;
1231 if (tag == "action") {
1232 action = getAttribute(tree, NAME_ATTR, &error);
1233 if (error != "") {
1234 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1235 error.string());
1236 goto bail;
1237 }
1238 if (withinActivity) {
1239 if (action == "android.intent.action.MAIN") {
1240 isMainActivity = true;
1241 actMainActivity = true;
1242 }
1243 } else if (withinReceiver) {
1244 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1245 actWidgetReceivers = true;
1246 }
1247 } else if (withinService) {
1248 if (action == "android.view.InputMethod") {
1249 actImeService = true;
1250 } else if (action == "android.service.wallpaper.WallpaperService") {
1251 actWallpaperService = true;
1252 }
1253 }
1254 if (action == "android.intent.action.SEARCH") {
1255 isSearchable = true;
1256 }
1257 }
1258
1259 if (tag == "category") {
1260 String8 category = getAttribute(tree, NAME_ATTR, &error);
1261 if (error != "") {
1262 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1263 goto bail;
1264 }
1265 if (withinActivity) {
1266 if (category == "android.intent.category.LAUNCHER") {
1267 isLauncherActivity = true;
1268 }
1269 }
1270 }
1271 }
1272 }
1273
1274 // Pre-1.6 implicitly granted permission compatibility logic
1275 if (targetSdk < 4) {
1276 if (!hasWriteExternalStoragePermission) {
1277 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1278 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1279 "'targetSdkVersion < 4'\n");
1280 hasWriteExternalStoragePermission = true;
1281 }
1282 if (!hasReadPhoneStatePermission) {
1283 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1284 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1285 "'targetSdkVersion < 4'\n");
1286 }
1287 }
1288
1289 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1290 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1291 // do this (regardless of target API version) because we can't have
1292 // an app with write permission but not read permission.
1293 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1294 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1295 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1296 "'requested WRITE_EXTERNAL_STORAGE'\n");
1297 }
1298
1299 // Pre-JellyBean call log permission compatibility.
1300 if (targetSdk < 16) {
1301 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1302 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1303 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1304 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1305 }
1306 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1307 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1308 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1309 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1310 }
1311 }
1312
1313 /* The following blocks handle printing "inferred" uses-features, based
1314 * on whether related features or permissions are used by the app.
1315 * Note that the various spec*Feature variables denote whether the
1316 * relevant tag was *present* in the AndroidManfest, not that it was
1317 * present and set to true.
1318 */
1319 // Camera-related back-compatibility logic
1320 if (!specCameraFeature) {
1321 if (reqCameraFlashFeature) {
1322 // if app requested a sub-feature (autofocus or flash) and didn't
1323 // request the base camera feature, we infer that it meant to
1324 printf("uses-feature:'android.hardware.camera'\n");
1325 printf("uses-implied-feature:'android.hardware.camera'," \
1326 "'requested android.hardware.camera.flash feature'\n");
1327 } else if (reqCameraAutofocusFeature) {
1328 // if app requested a sub-feature (autofocus or flash) and didn't
1329 // request the base camera feature, we infer that it meant to
1330 printf("uses-feature:'android.hardware.camera'\n");
1331 printf("uses-implied-feature:'android.hardware.camera'," \
1332 "'requested android.hardware.camera.autofocus feature'\n");
1333 } else if (hasCameraPermission) {
1334 // if app wants to use camera but didn't request the feature, we infer
1335 // that it meant to, and further that it wants autofocus
1336 // (which was the 1.0 - 1.5 behavior)
1337 printf("uses-feature:'android.hardware.camera'\n");
1338 if (!specCameraAutofocusFeature) {
1339 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1340 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1341 "'requested android.permission.CAMERA permission'\n");
1342 }
1343 }
1344 }
1345
1346 // Location-related back-compatibility logic
1347 if (!specLocationFeature &&
1348 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1349 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1350 // if app either takes a location-related permission or requests one of the
1351 // sub-features, we infer that it also meant to request the base location feature
1352 printf("uses-feature:'android.hardware.location'\n");
1353 printf("uses-implied-feature:'android.hardware.location'," \
1354 "'requested a location access permission'\n");
1355 }
1356 if (!specGpsFeature && hasGpsPermission) {
1357 // if app takes GPS (FINE location) perm but does not request the GPS
1358 // feature, we infer that it meant to
1359 printf("uses-feature:'android.hardware.location.gps'\n");
1360 printf("uses-implied-feature:'android.hardware.location.gps'," \
1361 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1362 }
1363 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1364 // if app takes Network location (COARSE location) perm but does not request the
1365 // network location feature, we infer that it meant to
1366 printf("uses-feature:'android.hardware.location.network'\n");
1367 printf("uses-implied-feature:'android.hardware.location.network'," \
1368 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1369 }
1370
1371 // Bluetooth-related compatibility logic
1372 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1373 // if app takes a Bluetooth permission but does not request the Bluetooth
1374 // feature, we infer that it meant to
1375 printf("uses-feature:'android.hardware.bluetooth'\n");
1376 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1377 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1378 "permission and targetSdkVersion > 4'\n");
1379 }
1380
1381 // Microphone-related compatibility logic
1382 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1383 // if app takes the record-audio permission but does not request the microphone
1384 // feature, we infer that it meant to
1385 printf("uses-feature:'android.hardware.microphone'\n");
1386 printf("uses-implied-feature:'android.hardware.microphone'," \
1387 "'requested android.permission.RECORD_AUDIO permission'\n");
1388 }
1389
1390 // WiFi-related compatibility logic
1391 if (!specWiFiFeature && hasWiFiPermission) {
1392 // if app takes one of the WiFi permissions but does not request the WiFi
1393 // feature, we infer that it meant to
1394 printf("uses-feature:'android.hardware.wifi'\n");
1395 printf("uses-implied-feature:'android.hardware.wifi'," \
1396 "'requested android.permission.ACCESS_WIFI_STATE, " \
1397 "android.permission.CHANGE_WIFI_STATE, or " \
1398 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1399 }
1400
1401 // Telephony-related compatibility logic
1402 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1403 // if app takes one of the telephony permissions or requests a sub-feature but
1404 // does not request the base telephony feature, we infer that it meant to
1405 printf("uses-feature:'android.hardware.telephony'\n");
1406 printf("uses-implied-feature:'android.hardware.telephony'," \
1407 "'requested a telephony-related permission or feature'\n");
1408 }
1409
1410 // Touchscreen-related back-compatibility logic
1411 if (!specTouchscreenFeature) { // not a typo!
1412 // all apps are presumed to require a touchscreen, unless they explicitly say
1413 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1414 // Note that specTouchscreenFeature is true if the tag is present, regardless
1415 // of whether its value is true or false, so this is safe
1416 printf("uses-feature:'android.hardware.touchscreen'\n");
1417 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1418 "'assumed you require a touch screen unless explicitly made optional'\n");
1419 }
1420 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1421 // if app takes one of the telephony permissions or requests a sub-feature but
1422 // does not request the base telephony feature, we infer that it meant to
1423 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1424 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1425 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1426 }
1427
1428 // Landscape/portrait-related compatibility logic
1429 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1430 // If the app has specified any activities in its manifest
1431 // that request a specific orientation, then assume that
1432 // orientation is required.
1433 if (reqScreenLandscapeFeature) {
1434 printf("uses-feature:'android.hardware.screen.landscape'\n");
1435 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1436 "'one or more activities have specified a landscape orientation'\n");
1437 }
1438 if (reqScreenPortraitFeature) {
1439 printf("uses-feature:'android.hardware.screen.portrait'\n");
1440 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1441 "'one or more activities have specified a portrait orientation'\n");
1442 }
1443 }
1444
1445 if (hasMainActivity) {
1446 printf("main\n");
1447 }
1448 if (hasWidgetReceivers) {
1449 printf("app-widget\n");
1450 }
1451 if (hasImeService) {
1452 printf("ime\n");
1453 }
1454 if (hasWallpaperService) {
1455 printf("wallpaper\n");
1456 }
1457 if (hasOtherActivities) {
1458 printf("other-activities\n");
1459 }
1460 if (isSearchable) {
1461 printf("search\n");
1462 }
1463 if (hasOtherReceivers) {
1464 printf("other-receivers\n");
1465 }
1466 if (hasOtherServices) {
1467 printf("other-services\n");
1468 }
1469
1470 // For modern apps, if screen size buckets haven't been specified
1471 // but the new width ranges have, then infer the buckets from them.
1472 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1473 && requiresSmallestWidthDp > 0) {
1474 int compatWidth = compatibleWidthLimitDp;
1475 if (compatWidth <= 0) {
1476 compatWidth = requiresSmallestWidthDp;
1477 }
1478 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1479 smallScreen = -1;
1480 } else {
1481 smallScreen = 0;
1482 }
1483 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1484 normalScreen = -1;
1485 } else {
1486 normalScreen = 0;
1487 }
1488 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1489 largeScreen = -1;
1490 } else {
1491 largeScreen = 0;
1492 }
1493 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1494 xlargeScreen = -1;
1495 } else {
1496 xlargeScreen = 0;
1497 }
1498 }
1499
1500 // Determine default values for any unspecified screen sizes,
1501 // based on the target SDK of the package. As of 4 (donut)
1502 // the screen size support was introduced, so all default to
1503 // enabled.
1504 if (smallScreen > 0) {
1505 smallScreen = targetSdk >= 4 ? -1 : 0;
1506 }
1507 if (normalScreen > 0) {
1508 normalScreen = -1;
1509 }
1510 if (largeScreen > 0) {
1511 largeScreen = targetSdk >= 4 ? -1 : 0;
1512 }
1513 if (xlargeScreen > 0) {
1514 // Introduced in Gingerbread.
1515 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1516 }
1517 if (anyDensity > 0) {
1518 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1519 || compatibleWidthLimitDp > 0) ? -1 : 0;
1520 }
1521 printf("supports-screens:");
1522 if (smallScreen != 0) {
1523 printf(" 'small'");
1524 }
1525 if (normalScreen != 0) {
1526 printf(" 'normal'");
1527 }
1528 if (largeScreen != 0) {
1529 printf(" 'large'");
1530 }
1531 if (xlargeScreen != 0) {
1532 printf(" 'xlarge'");
1533 }
1534 printf("\n");
1535 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1536 if (requiresSmallestWidthDp > 0) {
1537 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1538 }
1539 if (compatibleWidthLimitDp > 0) {
1540 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1541 }
1542 if (largestWidthLimitDp > 0) {
1543 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1544 }
1545
1546 printf("locales:");
1547 const size_t NL = locales.size();
1548 for (size_t i=0; i<NL; i++) {
1549 const char* localeStr = locales[i].string();
1550 if (localeStr == NULL || strlen(localeStr) == 0) {
1551 localeStr = "--_--";
1552 }
1553 printf(" '%s'", localeStr);
1554 }
1555 printf("\n");
1556
1557 printf("densities:");
1558 const size_t ND = densities.size();
1559 for (size_t i=0; i<ND; i++) {
1560 printf(" '%d'", densities[i]);
1561 }
1562 printf("\n");
1563
1564 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1565 if (dir != NULL) {
1566 if (dir->getFileCount() > 0) {
1567 printf("native-code:");
1568 for (size_t i=0; i<dir->getFileCount(); i++) {
1569 printf(" '%s'", dir->getFileName(i).string());
1570 }
1571 printf("\n");
1572 }
1573 delete dir;
1574 }
1575 } else if (strcmp("badger", option) == 0) {
1576 printf("%s", CONSOLE_DATA);
1577 } else if (strcmp("configurations", option) == 0) {
1578 Vector<ResTable_config> configs;
1579 res.getConfigurations(&configs);
1580 const size_t N = configs.size();
1581 for (size_t i=0; i<N; i++) {
1582 printf("%s\n", configs[i].toString().string());
1583 }
1584 } else {
1585 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1586 goto bail;
1587 }
1588 }
1589
1590 result = NO_ERROR;
1591
1592bail:
1593 if (asset) {
1594 delete asset;
1595 }
1596 return (result != NO_ERROR);
1597}
1598
1599
1600/*
1601 * Handle the "add" command, which wants to add files to a new or
1602 * pre-existing archive.
1603 */
1604int doAdd(Bundle* bundle)
1605{
1606 ZipFile* zip = NULL;
1607 status_t result = UNKNOWN_ERROR;
1608 const char* zipFileName;
1609
1610 if (bundle->getUpdate()) {
1611 /* avoid confusion */
1612 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1613 goto bail;
1614 }
1615
1616 if (bundle->getFileSpecCount() < 1) {
1617 fprintf(stderr, "ERROR: must specify zip file name\n");
1618 goto bail;
1619 }
1620 zipFileName = bundle->getFileSpecEntry(0);
1621
1622 if (bundle->getFileSpecCount() < 2) {
1623 fprintf(stderr, "NOTE: nothing to do\n");
1624 goto bail;
1625 }
1626
1627 zip = openReadWrite(zipFileName, true);
1628 if (zip == NULL) {
1629 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1630 goto bail;
1631 }
1632
1633 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1634 const char* fileName = bundle->getFileSpecEntry(i);
1635
1636 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1637 printf(" '%s'... (from gzip)\n", fileName);
1638 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1639 } else {
1640 if (bundle->getJunkPath()) {
1641 String8 storageName = String8(fileName).getPathLeaf();
1642 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1643 result = zip->add(fileName, storageName.string(),
1644 bundle->getCompressionMethod(), NULL);
1645 } else {
1646 printf(" '%s'...\n", fileName);
1647 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1648 }
1649 }
1650 if (result != NO_ERROR) {
1651 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1652 if (result == NAME_NOT_FOUND) {
1653 fprintf(stderr, ": file not found\n");
1654 } else if (result == ALREADY_EXISTS) {
1655 fprintf(stderr, ": already exists in archive\n");
1656 } else {
1657 fprintf(stderr, "\n");
1658 }
1659 goto bail;
1660 }
1661 }
1662
1663 result = NO_ERROR;
1664
1665bail:
1666 delete zip;
1667 return (result != NO_ERROR);
1668}
1669
1670
1671/*
1672 * Delete files from an existing archive.
1673 */
1674int doRemove(Bundle* bundle)
1675{
1676 ZipFile* zip = NULL;
1677 status_t result = UNKNOWN_ERROR;
1678 const char* zipFileName;
1679
1680 if (bundle->getFileSpecCount() < 1) {
1681 fprintf(stderr, "ERROR: must specify zip file name\n");
1682 goto bail;
1683 }
1684 zipFileName = bundle->getFileSpecEntry(0);
1685
1686 if (bundle->getFileSpecCount() < 2) {
1687 fprintf(stderr, "NOTE: nothing to do\n");
1688 goto bail;
1689 }
1690
1691 zip = openReadWrite(zipFileName, false);
1692 if (zip == NULL) {
1693 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1694 zipFileName);
1695 goto bail;
1696 }
1697
1698 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1699 const char* fileName = bundle->getFileSpecEntry(i);
1700 ZipEntry* entry;
1701
1702 entry = zip->getEntryByName(fileName);
1703 if (entry == NULL) {
1704 printf(" '%s' NOT FOUND\n", fileName);
1705 continue;
1706 }
1707
1708 result = zip->remove(entry);
1709
1710 if (result != NO_ERROR) {
1711 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1712 bundle->getFileSpecEntry(i), zipFileName);
1713 goto bail;
1714 }
1715 }
1716
1717 /* update the archive */
1718 zip->flush();
1719
1720bail:
1721 delete zip;
1722 return (result != NO_ERROR);
1723}
1724
1725
1726/*
1727 * Package up an asset directory and associated application files.
1728 */
1729int doPackage(Bundle* bundle)
1730{
1731 const char* outputAPKFile;
1732 int retVal = 1;
1733 status_t err;
1734 sp<AaptAssets> assets;
1735 int N;
1736 FILE* fp;
1737 String8 dependencyFile;
1738
1739 // -c zz_ZZ means do pseudolocalization
1740 ResourceFilter filter;
1741 err = filter.parse(bundle->getConfigurations());
1742 if (err != NO_ERROR) {
1743 goto bail;
1744 }
1745 if (filter.containsPseudo()) {
1746 bundle->setPseudolocalize(true);
1747 }
1748
1749 N = bundle->getFileSpecCount();
1750 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1751 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1752 fprintf(stderr, "ERROR: no input files\n");
1753 goto bail;
1754 }
1755
1756 outputAPKFile = bundle->getOutputAPKFile();
1757
1758 // Make sure the filenames provided exist and are of the appropriate type.
1759 if (outputAPKFile) {
1760 FileType type;
1761 type = getFileType(outputAPKFile);
1762 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1763 fprintf(stderr,
1764 "ERROR: output file '%s' exists but is not regular file\n",
1765 outputAPKFile);
1766 goto bail;
1767 }
1768 }
1769
1770 // Load the assets.
1771 assets = new AaptAssets();
1772
1773 // Set up the resource gathering in assets if we're going to generate
1774 // dependency files. Every time we encounter a resource while slurping
1775 // the tree, we'll add it to these stores so we have full resource paths
1776 // to write to a dependency file.
1777 if (bundle->getGenDependencies()) {
1778 sp<FilePathStore> resPathStore = new FilePathStore;
1779 assets->setFullResPaths(resPathStore);
1780 sp<FilePathStore> assetPathStore = new FilePathStore;
1781 assets->setFullAssetPaths(assetPathStore);
1782 }
1783
1784 err = assets->slurpFromArgs(bundle);
1785 if (err < 0) {
1786 goto bail;
1787 }
1788
1789 if (bundle->getVerbose()) {
1790 assets->print(String8());
1791 }
1792
1793 // If they asked for any fileAs that need to be compiled, do so.
1794 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1795 err = buildResources(bundle, assets);
1796 if (err != 0) {
1797 goto bail;
1798 }
1799 }
1800
1801 // At this point we've read everything and processed everything. From here
1802 // on out it's just writing output files.
1803 if (SourcePos::hasErrors()) {
1804 goto bail;
1805 }
1806
1807 // Update symbols with information about which ones are needed as Java symbols.
1808 assets->applyJavaSymbols();
1809 if (SourcePos::hasErrors()) {
1810 goto bail;
1811 }
1812
1813 // If we've been asked to generate a dependency file, do that here
1814 if (bundle->getGenDependencies()) {
1815 // If this is the packaging step, generate the dependency file next to
1816 // the output apk (e.g. bin/resources.ap_.d)
1817 if (outputAPKFile) {
1818 dependencyFile = String8(outputAPKFile);
1819 // Add the .d extension to the dependency file.
1820 dependencyFile.append(".d");
1821 } else {
1822 // Else if this is the R.java dependency generation step,
1823 // generate the dependency file in the R.java package subdirectory
1824 // e.g. gen/com/foo/app/R.java.d
1825 dependencyFile = String8(bundle->getRClassDir());
1826 dependencyFile.appendPath("R.java.d");
1827 }
1828 // Make sure we have a clean dependency file to start with
1829 fp = fopen(dependencyFile, "w");
1830 fclose(fp);
1831 }
1832
1833 // Write out R.java constants
1834 if (!assets->havePrivateSymbols()) {
1835 if (bundle->getCustomPackage() == NULL) {
1836 // Write the R.java file into the appropriate class directory
1837 // e.g. gen/com/foo/app/R.java
1838 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1839 } else {
1840 const String8 customPkg(bundle->getCustomPackage());
1841 err = writeResourceSymbols(bundle, assets, customPkg, true);
1842 }
1843 if (err < 0) {
1844 goto bail;
1845 }
1846 // If we have library files, we're going to write our R.java file into
1847 // the appropriate class directory for those libraries as well.
1848 // e.g. gen/com/foo/app/lib/R.java
1849 if (bundle->getExtraPackages() != NULL) {
1850 // Split on colon
1851 String8 libs(bundle->getExtraPackages());
1852 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1853 while (packageString != NULL) {
1854 // Write the R.java file out with the correct package name
1855 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1856 if (err < 0) {
1857 goto bail;
1858 }
1859 packageString = strtok(NULL, ":");
1860 }
1861 libs.unlockBuffer();
1862 }
1863 } else {
1864 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1865 if (err < 0) {
1866 goto bail;
1867 }
1868 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1869 if (err < 0) {
1870 goto bail;
1871 }
1872 }
1873
1874 // Write out the ProGuard file
1875 err = writeProguardFile(bundle, assets);
1876 if (err < 0) {
1877 goto bail;
1878 }
1879
1880 // Write the apk
1881 if (outputAPKFile) {
1882 err = writeAPK(bundle, assets, String8(outputAPKFile));
1883 if (err != NO_ERROR) {
1884 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1885 goto bail;
1886 }
1887 }
1888
1889 // If we've been asked to generate a dependency file, we need to finish up here.
1890 // the writeResourceSymbols and writeAPK functions have already written the target
1891 // half of the dependency file, now we need to write the prerequisites. (files that
1892 // the R.java file or .ap_ file depend on)
1893 if (bundle->getGenDependencies()) {
1894 // Now that writeResourceSymbols or writeAPK has taken care of writing
1895 // the targets to our dependency file, we'll write the prereqs
1896 fp = fopen(dependencyFile, "a+");
1897 fprintf(fp, " : ");
1898 bool includeRaw = (outputAPKFile != NULL);
1899 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1900 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1901 // and therefore was not added to our pathstores during slurping
1902 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1903 fclose(fp);
1904 }
1905
1906 retVal = 0;
1907bail:
1908 if (SourcePos::hasErrors()) {
1909 SourcePos::printErrors(stderr);
1910 }
1911 return retVal;
1912}
1913
1914/*
1915 * Do PNG Crunching
1916 * PRECONDITIONS
1917 * -S flag points to a source directory containing drawable* folders
1918 * -C flag points to destination directory. The folder structure in the
1919 * source directory will be mirrored to the destination (cache) directory
1920 *
1921 * POSTCONDITIONS
1922 * Destination directory will be updated to match the PNG files in
1923 * the source directory.
1924 */
1925int doCrunch(Bundle* bundle)
1926{
1927 fprintf(stdout, "Crunching PNG Files in ");
1928 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1929 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1930
1931 updatePreProcessedCache(bundle);
1932
1933 return NO_ERROR;
1934}
1935
1936/*
1937 * Do PNG Crunching on a single flag
1938 * -i points to a single png file
1939 * -o points to a single png output file
1940 */
1941int doSingleCrunch(Bundle* bundle)
1942{
1943 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
1944 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
1945
1946 String8 input(bundle->getSingleCrunchInputFile());
1947 String8 output(bundle->getSingleCrunchOutputFile());
1948
1949 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
1950 // we can't return the status_t as it gets truncate to the lower 8 bits.
1951 return 42;
1952 }
1953
1954 return NO_ERROR;
1955}
1956
1957char CONSOLE_DATA[2925] = {
1958 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1959 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1960 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1961 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1962 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1963 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1964 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1965 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1966 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1967 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1969 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1970 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1971 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1972 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1973 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1974 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1976 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1977 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1978 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1979 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1980 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1981 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1982 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1983 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1984 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1985 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1986 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1987 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1988 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1989 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1990 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1991 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1992 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1993 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1994 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1995 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1996 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1997 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1998 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1999 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2000 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2001 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2002 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2003 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2004 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2005 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2006 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2007 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2008 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2009 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2010 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2011 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2012 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2013 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2014 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2015 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2016 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2017 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2018 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2019 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2020 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2021 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2022 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2023 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2024 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2025 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2026 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2027 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2028 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2029 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2030 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2031 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2032 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2033 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2034 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2035 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2036 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2037 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2038 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2039 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2040 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2041 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2042 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2043 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2044 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2045 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2046 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2047 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2048 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2049 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2050 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2051 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2052 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2053 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2054 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2055 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2056 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2057 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2058 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2059 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2060 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2061 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2062 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2063 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2064 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2065 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2066 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2067 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2068 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2069 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2070 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2071 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2072 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2073 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2074 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2075 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2076 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2077 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2078 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2079 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2080 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2081 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2082 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2083 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2084 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2085 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2086 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2087 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2088 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2089 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2090 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2091 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2092 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2093 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2094 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2095 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2096 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2097 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2098 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2099 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2100 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2101 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2102 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2103 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2104 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2105 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2106 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2107 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2108 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2109 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2110 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2111 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2112 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2113 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2114 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2115 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2116 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2117 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2118 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2119 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2120 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2121 };