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