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