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