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