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 = 10000; // 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 size_t len;
973 ResXMLTree::event_code_t code;
974 int depth = 0;
975 String8 error;
976 bool withinActivity = false;
977 bool isMainActivity = false;
978 bool isLauncherActivity = false;
979 bool isLeanbackLauncherActivity = false;
980 bool isSearchable = false;
981 bool withinApplication = false;
982 bool withinSupportsInput = false;
983 bool withinFeatureGroup = false;
984 bool withinReceiver = false;
985 bool withinService = false;
986 bool withinProvider = false;
987 bool withinIntentFilter = false;
988 bool hasMainActivity = false;
989 bool hasOtherActivities = false;
990 bool hasOtherReceivers = false;
991 bool hasOtherServices = false;
992 bool hasIntentFilter = false;
993
994 bool hasWallpaperService = false;
995 bool hasImeService = false;
996 bool hasAccessibilityService = false;
997 bool hasPrintService = false;
998 bool hasWidgetReceivers = false;
999 bool hasDeviceAdminReceiver = false;
1000 bool hasPaymentService = false;
1001 bool hasDocumentsProvider = false;
1002 bool hasCameraActivity = false;
1003 bool hasCameraSecureActivity = false;
1004 bool hasLauncher = false;
1005 bool hasNotificationListenerService = false;
1006 bool hasDreamService = false;
1007
1008 bool actMainActivity = false;
1009 bool actWidgetReceivers = false;
1010 bool actDeviceAdminEnabled = false;
1011 bool actImeService = false;
1012 bool actWallpaperService = false;
1013 bool actAccessibilityService = false;
1014 bool actPrintService = false;
1015 bool actHostApduService = false;
1016 bool actOffHostApduService = false;
1017 bool actDocumentsProvider = false;
1018 bool actNotificationListenerService = false;
1019 bool actDreamService = false;
1020 bool actCamera = false;
1021 bool actCameraSecure = false;
1022 bool catLauncher = false;
1023 bool hasMetaHostPaymentCategory = false;
1024 bool hasMetaOffHostPaymentCategory = false;
1025
1026 // These permissions are required by services implementing services
1027 // the system binds to (IME, Accessibility, PrintServices, etc.)
1028 bool hasBindDeviceAdminPermission = false;
1029 bool hasBindInputMethodPermission = false;
1030 bool hasBindAccessibilityServicePermission = false;
1031 bool hasBindPrintServicePermission = false;
1032 bool hasBindNfcServicePermission = false;
1033 bool hasRequiredSafAttributes = false;
1034 bool hasBindNotificationListenerServicePermission = false;
1035 bool hasBindDreamServicePermission = false;
1036
1037 // These two implement the implicit permissions that are granted
1038 // to pre-1.6 applications.
1039 bool hasWriteExternalStoragePermission = false;
1040 int32_t writeExternalStoragePermissionMaxSdkVersion = -1;
1041 bool hasReadPhoneStatePermission = false;
1042
1043 // If an app requests write storage, they will also get read storage.
1044 bool hasReadExternalStoragePermission = false;
1045
1046 // Implement transition to read and write call log.
1047 bool hasReadContactsPermission = false;
1048 bool hasWriteContactsPermission = false;
1049 bool hasReadCallLogPermission = false;
1050 bool hasWriteCallLogPermission = false;
1051
1052 // If an app declares itself as multiArch, we report the
1053 // native libraries differently.
1054 bool hasMultiArch = false;
1055
1056 // This next group of variables is used to implement a group of
1057 // backward-compatibility heuristics necessitated by the addition of
1058 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1059 // heuristic is "if an app requests a permission but doesn't explicitly
1060 // request the corresponding <uses-feature>, presume it's there anyway".
1061
1062 // 2.2 also added some other features that apps can request, but that
1063 // have no corresponding permission, so we cannot implement any
1064 // back-compatibility heuristic for them. The below are thus unnecessary
1065 // (but are retained here for documentary purposes.)
1066 //bool specCompassFeature = false;
1067 //bool specAccelerometerFeature = false;
1068 //bool specProximityFeature = false;
1069 //bool specAmbientLightFeature = false;
1070 //bool specLiveWallpaperFeature = false;
1071
1072 int targetSdk = 0;
1073 int smallScreen = 1;
1074 int normalScreen = 1;
1075 int largeScreen = 1;
1076 int xlargeScreen = 1;
1077 int anyDensity = 1;
1078 int requiresSmallestWidthDp = 0;
1079 int compatibleWidthLimitDp = 0;
1080 int largestWidthLimitDp = 0;
1081 String8 pkg;
1082 String8 activityName;
1083 String8 activityLabel;
1084 String8 activityIcon;
1085 String8 activityBanner;
1086 String8 receiverName;
1087 String8 serviceName;
1088 Vector<String8> supportedInput;
1089
1090 FeatureGroup commonFeatures;
1091 Vector<FeatureGroup> featureGroups;
1092 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1093
1094 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
1095 code != ResXMLTree::BAD_DOCUMENT) {
1096 if (code == ResXMLTree::END_TAG) {
1097 depth--;
1098 if (depth < 2) {
1099 if (withinSupportsInput && !supportedInput.isEmpty()) {
1100 printf("supports-input: '");
1101 const size_t N = supportedInput.size();
1102 for (size_t i=0; i<N; i++) {
1103 printf("%s", ResTable::normalizeForOutput(
1104 supportedInput[i].string()).string());
1105 if (i != N - 1) {
1106 printf("' '");
1107 } else {
1108 printf("'\n");
1109 }
1110 }
1111 supportedInput.clear();
1112 }
1113 withinApplication = false;
1114 withinSupportsInput = false;
1115 withinFeatureGroup = false;
1116 } else if (depth < 3) {
1117 if (withinActivity && isMainActivity) {
1118 String8 aName(getComponentName(pkg, activityName));
1119 if (isLauncherActivity) {
1120 printf("launchable-activity:");
1121 if (aName.length() > 0) {
1122 printf(" name='%s' ",
1123 ResTable::normalizeForOutput(aName.string()).string());
1124 }
1125 printf(" label='%s' icon='%s'\n",
1126 ResTable::normalizeForOutput(activityLabel.string())
1127 .string(),
1128 ResTable::normalizeForOutput(activityIcon.string())
1129 .string());
1130 }
1131 if (isLeanbackLauncherActivity) {
1132 printf("leanback-launchable-activity:");
1133 if (aName.length() > 0) {
1134 printf(" name='%s' ",
1135 ResTable::normalizeForOutput(aName.string()).string());
1136 }
1137 printf(" label='%s' icon='%s' banner='%s'\n",
1138 ResTable::normalizeForOutput(activityLabel.string())
1139 .string(),
1140 ResTable::normalizeForOutput(activityIcon.string())
1141 .string(),
1142 ResTable::normalizeForOutput(activityBanner.string())
1143 .string());
1144 }
1145 }
1146 if (!hasIntentFilter) {
1147 hasOtherActivities |= withinActivity;
1148 hasOtherReceivers |= withinReceiver;
1149 hasOtherServices |= withinService;
1150 } else {
1151 if (withinService) {
1152 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1153 hasBindNfcServicePermission);
1154 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1155 hasBindNfcServicePermission);
1156 }
1157 }
1158 withinActivity = false;
1159 withinService = false;
1160 withinReceiver = false;
1161 withinProvider = false;
1162 hasIntentFilter = false;
1163 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
1164 } else if (depth < 4) {
1165 if (withinIntentFilter) {
1166 if (withinActivity) {
1167 hasMainActivity |= actMainActivity;
1168 hasLauncher |= catLauncher;
1169 hasCameraActivity |= actCamera;
1170 hasCameraSecureActivity |= actCameraSecure;
1171 hasOtherActivities |=
1172 !actMainActivity && !actCamera && !actCameraSecure;
1173 } else if (withinReceiver) {
1174 hasWidgetReceivers |= actWidgetReceivers;
1175 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1176 hasBindDeviceAdminPermission);
1177 hasOtherReceivers |=
1178 (!actWidgetReceivers && !actDeviceAdminEnabled);
1179 } else if (withinService) {
1180 hasImeService |= actImeService;
1181 hasWallpaperService |= actWallpaperService;
1182 hasAccessibilityService |= (actAccessibilityService &&
1183 hasBindAccessibilityServicePermission);
1184 hasPrintService |=
1185 (actPrintService && hasBindPrintServicePermission);
1186 hasNotificationListenerService |= actNotificationListenerService &&
1187 hasBindNotificationListenerServicePermission;
1188 hasDreamService |= actDreamService && hasBindDreamServicePermission;
1189 hasOtherServices |= (!actImeService && !actWallpaperService &&
1190 !actAccessibilityService && !actPrintService &&
1191 !actHostApduService && !actOffHostApduService &&
1192 !actNotificationListenerService);
1193 } else if (withinProvider) {
1194 hasDocumentsProvider |=
1195 actDocumentsProvider && hasRequiredSafAttributes;
1196 }
1197 }
1198 withinIntentFilter = false;
1199 }
1200 continue;
1201 }
1202 if (code != ResXMLTree::START_TAG) {
1203 continue;
1204 }
1205 depth++;
1206
1207 const char16_t* ctag16 = tree.getElementName(&len);
1208 if (ctag16 == NULL) {
1209 SourcePos(manifestFile, tree.getLineNumber()).error(
1210 "ERROR: failed to get XML element name (bad string pool)");
1211 goto bail;
1212 }
1213 String8 tag(ctag16);
1214 //printf("Depth %d, %s\n", depth, tag.string());
1215 if (depth == 1) {
1216 if (tag != "manifest") {
1217 SourcePos(manifestFile, tree.getLineNumber()).error(
1218 "ERROR: manifest does not start with <manifest> tag");
1219 goto bail;
1220 }
1221 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
1222 printf("package: name='%s' ",
1223 ResTable::normalizeForOutput(pkg.string()).string());
1224 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
1225 &error);
1226 if (error != "") {
1227 SourcePos(manifestFile, tree.getLineNumber()).error(
1228 "ERROR getting 'android:versionCode' attribute: %s",
1229 error.string());
1230 goto bail;
1231 }
1232 if (versionCode > 0) {
1233 printf("versionCode='%d' ", versionCode);
1234 } else {
1235 printf("versionCode='' ");
1236 }
1237 String8 versionName = AaptXml::getResolvedAttribute(res, tree,
1238 VERSION_NAME_ATTR, &error);
1239 if (error != "") {
1240 SourcePos(manifestFile, tree.getLineNumber()).error(
1241 "ERROR getting 'android:versionName' attribute: %s",
1242 error.string());
1243 goto bail;
1244 }
1245 printf("versionName='%s'",
1246 ResTable::normalizeForOutput(versionName.string()).string());
1247
1248 String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
1249 if (!splitName.isEmpty()) {
1250 printf(" split='%s'", ResTable::normalizeForOutput(
1251 splitName.string()).string());
1252 }
1253
1254 String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
1255 "platformBuildVersionName");
1256 if (platformBuildVersionName != "") {
1257 printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
1258 }
1259
1260 String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
1261 "platformBuildVersionCode");
1262 if (platformBuildVersionCode != "") {
1263 printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
1264 }
1265
1266 int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
1267 COMPILE_SDK_VERSION_ATTR, &error);
1268 if (error != "") {
1269 SourcePos(manifestFile, tree.getLineNumber()).error(
1270 "ERROR getting 'android:compileSdkVersion' attribute: %s",
1271 error.string());
1272 goto bail;
1273 }
1274 if (compileSdkVersion > 0) {
1275 printf(" compileSdkVersion='%d'", compileSdkVersion);
1276 }
1277
1278 String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
1279 COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
1280 if (compileSdkVersionCodename != "") {
1281 printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
1282 compileSdkVersionCodename.string()).string());
1283 }
1284
1285 printf("\n");
1286
1287 int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
1288 INSTALL_LOCATION_ATTR, &error);
1289 if (error != "") {
1290 SourcePos(manifestFile, tree.getLineNumber()).error(
1291 "ERROR getting 'android:installLocation' attribute: %s",
1292 error.string());
1293 goto bail;
1294 }
1295
1296 if (installLocation >= 0) {
1297 printf("install-location:'");
1298 switch (installLocation) {
1299 case 0:
1300 printf("auto");
1301 break;
1302 case 1:
1303 printf("internalOnly");
1304 break;
1305 case 2:
1306 printf("preferExternal");
1307 break;
1308 default:
1309 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1310 goto bail;
1311 }
1312 printf("'\n");
1313 }
1314 } else if (depth == 2) {
1315 withinApplication = false;
1316 if (tag == "application") {
1317 withinApplication = true;
1318
1319 String8 label;
1320 const size_t NL = locales.size();
1321 for (size_t i=0; i<NL; i++) {
1322 const char* localeStr = locales[i].string();
1323 assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
1324 String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1325 &error);
1326 if (llabel != "") {
1327 if (localeStr == NULL || strlen(localeStr) == 0) {
1328 label = llabel;
1329 printf("application-label:'%s'\n",
1330 ResTable::normalizeForOutput(llabel.string()).string());
1331 } else {
1332 if (label == "") {
1333 label = llabel;
1334 }
1335 printf("application-label-%s:'%s'\n", localeStr,
1336 ResTable::normalizeForOutput(llabel.string()).string());
1337 }
1338 }
1339 }
1340
1341 ResTable_config tmpConfig = config;
1342 const size_t ND = densities.size();
1343 for (size_t i=0; i<ND; i++) {
1344 tmpConfig.density = densities[i];
1345 assets.setConfiguration(tmpConfig);
1346 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1347 &error);
1348 if (icon != "") {
1349 printf("application-icon-%d:'%s'\n", densities[i],
1350 ResTable::normalizeForOutput(icon.string()).string());
1351 }
1352 }
1353 assets.setConfiguration(config);
1354
1355 String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
1356 if (error != "") {
1357 SourcePos(manifestFile, tree.getLineNumber()).error(
1358 "ERROR getting 'android:icon' attribute: %s", error.string());
1359 goto bail;
1360 }
1361 int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
1362 &error);
1363 if (error != "") {
1364 SourcePos(manifestFile, tree.getLineNumber()).error(
1365 "ERROR getting 'android:testOnly' attribute: %s",
1366 error.string());
1367 goto bail;
1368 }
1369
1370 String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1371 &error);
1372 if (error != "") {
1373 SourcePos(manifestFile, tree.getLineNumber()).error(
1374 "ERROR getting 'android:banner' attribute: %s", error.string());
1375 goto bail;
1376 }
1377 printf("application: label='%s' ",
1378 ResTable::normalizeForOutput(label.string()).string());
1379 printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
1380 if (banner != "") {
1381 printf(" banner='%s'",
1382 ResTable::normalizeForOutput(banner.string()).string());
1383 }
1384 printf("\n");
1385 if (testOnly != 0) {
1386 printf("testOnly='%d'\n", testOnly);
1387 }
1388
1389 int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree,
1390 ISGAME_ATTR, 0, &error);
1391 if (error != "") {
1392 SourcePos(manifestFile, tree.getLineNumber()).error(
1393 "ERROR getting 'android:isGame' attribute: %s", error.string());
1394 goto bail;
1395 }
1396 if (isGame != 0) {
1397 printf("application-isGame\n");
1398 }
1399
1400 int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
1401 DEBUGGABLE_ATTR, 0, &error);
1402 if (error != "") {
1403 SourcePos(manifestFile, tree.getLineNumber()).error(
1404 "ERROR getting 'android:debuggable' attribute: %s",
1405 error.string());
1406 goto bail;
1407 }
1408 if (debuggable != 0) {
1409 printf("application-debuggable\n");
1410 }
1411
1412 // We must search by name because the multiArch flag hasn't been API
1413 // frozen yet.
1414 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1415 "multiArch");
1416 if (multiArchIndex >= 0) {
1417 Res_value value;
1418 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1419 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1420 value.dataType <= Res_value::TYPE_LAST_INT) {
1421 hasMultiArch = value.data;
1422 }
1423 }
1424 }
1425 } else if (tag == "uses-sdk") {
1426 int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
1427 &error);
1428 if (error != "") {
1429 error = "";
1430 String8 name = AaptXml::getResolvedAttribute(res, tree,
1431 MIN_SDK_VERSION_ATTR, &error);
1432 if (error != "") {
1433 SourcePos(manifestFile, tree.getLineNumber()).error(
1434 "ERROR getting 'android:minSdkVersion' attribute: %s",
1435 error.string());
1436 goto bail;
1437 }
1438 if (name == "Donut") targetSdk = 4;
1439 printf("sdkVersion:'%s'\n",
1440 ResTable::normalizeForOutput(name.string()).string());
1441 } else if (code != -1) {
1442 targetSdk = code;
1443 printf("sdkVersion:'%d'\n", code);
1444 }
1445 code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
1446 if (code != -1) {
1447 printf("maxSdkVersion:'%d'\n", code);
1448 }
1449 code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1450 if (error != "") {
1451 error = "";
1452 String8 name = AaptXml::getResolvedAttribute(res, tree,
1453 TARGET_SDK_VERSION_ATTR, &error);
1454 if (error != "") {
1455 SourcePos(manifestFile, tree.getLineNumber()).error(
1456 "ERROR getting 'android:targetSdkVersion' attribute: %s",
1457 error.string());
1458 goto bail;
1459 }
1460 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
1461 printf("targetSdkVersion:'%s'\n",
1462 ResTable::normalizeForOutput(name.string()).string());
1463 } else if (code != -1) {
1464 if (targetSdk < code) {
1465 targetSdk = code;
1466 }
1467 printf("targetSdkVersion:'%d'\n", code);
1468 }
1469 } else if (tag == "uses-configuration") {
1470 int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
1471 REQ_TOUCH_SCREEN_ATTR, 0);
1472 int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
1473 REQ_KEYBOARD_TYPE_ATTR, 0);
1474 int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
1475 REQ_HARD_KEYBOARD_ATTR, 0);
1476 int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
1477 REQ_NAVIGATION_ATTR, 0);
1478 int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
1479 REQ_FIVE_WAY_NAV_ATTR, 0);
1480 printf("uses-configuration:");
1481 if (reqTouchScreen != 0) {
1482 printf(" reqTouchScreen='%d'", reqTouchScreen);
1483 }
1484 if (reqKeyboardType != 0) {
1485 printf(" reqKeyboardType='%d'", reqKeyboardType);
1486 }
1487 if (reqHardKeyboard != 0) {
1488 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1489 }
1490 if (reqNavigation != 0) {
1491 printf(" reqNavigation='%d'", reqNavigation);
1492 }
1493 if (reqFiveWayNav != 0) {
1494 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1495 }
1496 printf("\n");
1497 } else if (tag == "supports-input") {
1498 withinSupportsInput = true;
1499 } else if (tag == "supports-screens") {
1500 smallScreen = AaptXml::getIntegerAttribute(tree,
1501 SMALL_SCREEN_ATTR, 1);
1502 normalScreen = AaptXml::getIntegerAttribute(tree,
1503 NORMAL_SCREEN_ATTR, 1);
1504 largeScreen = AaptXml::getIntegerAttribute(tree,
1505 LARGE_SCREEN_ATTR, 1);
1506 xlargeScreen = AaptXml::getIntegerAttribute(tree,
1507 XLARGE_SCREEN_ATTR, 1);
1508 anyDensity = AaptXml::getIntegerAttribute(tree,
1509 ANY_DENSITY_ATTR, 1);
1510 requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
1511 REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
1512 compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1513 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
1514 largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
1515 LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
1516 } else if (tag == "feature-group") {
1517 withinFeatureGroup = true;
1518 FeatureGroup group;
1519 group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
1520 if (error != "") {
1521 SourcePos(manifestFile, tree.getLineNumber()).error(
1522 "ERROR getting 'android:label' attribute: %s", error.string());
1523 goto bail;
1524 }
1525 featureGroups.add(group);
1526
1527 } else if (tag == "uses-feature") {
1528 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1529 if (name != "" && error == "") {
1530 const char* androidSchema =
1531 "http://schemas.android.com/apk/res/android";
1532
1533 int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
1534 &error);
1535 if (error != "") {
1536 SourcePos(manifestFile, tree.getLineNumber()).error(
1537 "failed to read attribute 'android:required': %s",
1538 error.string());
1539 goto bail;
1540 }
1541
1542 int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
1543 "version", 0, &error);
1544 if (error != "") {
1545 SourcePos(manifestFile, tree.getLineNumber()).error(
1546 "failed to read attribute 'android:version': %s",
1547 error.string());
1548 goto bail;
1549 }
1550
1551 commonFeatures.features.add(name, Feature(req != 0, version));
1552 if (req) {
1553 addParentFeatures(&commonFeatures, name);
1554 }
1555 } else {
1556 int vers = AaptXml::getIntegerAttribute(tree,
1557 GL_ES_VERSION_ATTR, &error);
1558 if (error == "") {
1559 if (vers > commonFeatures.openGLESVersion) {
1560 commonFeatures.openGLESVersion = vers;
1561 }
1562 }
1563 }
1564 } else if (tag == "uses-permission") {
1565 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1566 if (error != "") {
1567 SourcePos(manifestFile, tree.getLineNumber()).error(
1568 "ERROR getting 'android:name' attribute: %s", error.string());
1569 goto bail;
1570 }
1571
1572 if (name == "") {
1573 SourcePos(manifestFile, tree.getLineNumber()).error(
1574 "ERROR: missing 'android:name' for uses-permission");
1575 goto bail;
1576 }
1577
1578 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
1579
1580 const int32_t maxSdkVersion =
1581 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
1582 const String8 requiredFeature = AaptXml::getAttribute(tree,
1583 REQUIRED_FEATURE_ATTR, &error);
1584 const String8 requiredNotFeature = AaptXml::getAttribute(tree,
1585 REQUIRED_NOT_FEATURE_ATTR, &error);
1586
1587 if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1588 hasWriteExternalStoragePermission = true;
1589 writeExternalStoragePermissionMaxSdkVersion = maxSdkVersion;
1590 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1591 hasReadExternalStoragePermission = true;
1592 } else if (name == "android.permission.READ_PHONE_STATE") {
1593 hasReadPhoneStatePermission = true;
1594 } else if (name == "android.permission.READ_CONTACTS") {
1595 hasReadContactsPermission = true;
1596 } else if (name == "android.permission.WRITE_CONTACTS") {
1597 hasWriteContactsPermission = true;
1598 } else if (name == "android.permission.READ_CALL_LOG") {
1599 hasReadCallLogPermission = true;
1600 } else if (name == "android.permission.WRITE_CALL_LOG") {
1601 hasWriteCallLogPermission = true;
1602 }
1603
1604 printUsesPermission(name,
1605 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
1606 maxSdkVersion, requiredFeature, requiredNotFeature);
1607
1608 } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
1609 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1610 if (error != "") {
1611 SourcePos(manifestFile, tree.getLineNumber()).error(
1612 "ERROR getting 'android:name' attribute: %s", error.string());
1613 goto bail;
1614 }
1615
1616 if (name == "") {
1617 SourcePos(manifestFile, tree.getLineNumber()).error(
1618 "ERROR: missing 'android:name' for uses-permission-sdk-23");
1619 goto bail;
1620 }
1621
1622 addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
1623
1624 printUsesPermissionSdk23(
1625 name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
1626
1627 } else if (tag == "uses-package") {
1628 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1629 if (name != "" && error == "") {
1630 printf("uses-package:'%s'\n",
1631 ResTable::normalizeForOutput(name.string()).string());
1632 } else {
1633 SourcePos(manifestFile, tree.getLineNumber()).error(
1634 "ERROR getting 'android:name' attribute: %s", error.string());
1635 goto bail;
1636 }
1637 } else if (tag == "original-package") {
1638 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1639 if (name != "" && error == "") {
1640 printf("original-package:'%s'\n",
1641 ResTable::normalizeForOutput(name.string()).string());
1642 } else {
1643 SourcePos(manifestFile, tree.getLineNumber()).error(
1644 "ERROR getting 'android:name' attribute: %s", error.string());
1645 goto bail;
1646 }
1647 } else if (tag == "supports-gl-texture") {
1648 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1649 if (name != "" && error == "") {
1650 printf("supports-gl-texture:'%s'\n",
1651 ResTable::normalizeForOutput(name.string()).string());
1652 } else {
1653 SourcePos(manifestFile, tree.getLineNumber()).error(
1654 "ERROR getting 'android:name' attribute: %s", error.string());
1655 goto bail;
1656 }
1657 } else if (tag == "compatible-screens") {
1658 printCompatibleScreens(tree, &error);
1659 if (error != "") {
1660 SourcePos(manifestFile, tree.getLineNumber()).error(
1661 "ERROR getting compatible screens: %s", error.string());
1662 goto bail;
1663 }
1664 depth--;
1665 } else if (tag == "package-verifier") {
1666 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1667 if (name != "" && error == "") {
1668 String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
1669 &error);
1670 if (publicKey != "" && error == "") {
1671 printf("package-verifier: name='%s' publicKey='%s'\n",
1672 ResTable::normalizeForOutput(name.string()).string(),
1673 ResTable::normalizeForOutput(publicKey.string()).string());
1674 }
1675 }
1676 }
1677 } else if (depth == 3) {
1678 withinActivity = false;
1679 withinReceiver = false;
1680 withinService = false;
1681 withinProvider = false;
1682 hasIntentFilter = false;
1683 hasMetaHostPaymentCategory = false;
1684 hasMetaOffHostPaymentCategory = false;
1685 hasBindDeviceAdminPermission = false;
1686 hasBindInputMethodPermission = false;
1687 hasBindAccessibilityServicePermission = false;
1688 hasBindPrintServicePermission = false;
1689 hasBindNfcServicePermission = false;
1690 hasRequiredSafAttributes = false;
1691 hasBindNotificationListenerServicePermission = false;
1692 hasBindDreamServicePermission = false;
1693 if (withinApplication) {
1694 if(tag == "activity") {
1695 withinActivity = true;
1696 activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1697 if (error != "") {
1698 SourcePos(manifestFile, tree.getLineNumber()).error(
1699 "ERROR getting 'android:name' attribute: %s",
1700 error.string());
1701 goto bail;
1702 }
1703
1704 activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
1705 &error);
1706 if (error != "") {
1707 SourcePos(manifestFile, tree.getLineNumber()).error(
1708 "ERROR getting 'android:label' attribute: %s",
1709 error.string());
1710 goto bail;
1711 }
1712
1713 activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
1714 &error);
1715 if (error != "") {
1716 SourcePos(manifestFile, tree.getLineNumber()).error(
1717 "ERROR getting 'android:icon' attribute: %s",
1718 error.string());
1719 goto bail;
1720 }
1721
1722 activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
1723 &error);
1724 if (error != "") {
1725 SourcePos(manifestFile, tree.getLineNumber()).error(
1726 "ERROR getting 'android:banner' attribute: %s",
1727 error.string());
1728 goto bail;
1729 }
1730
1731 int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
1732 SCREEN_ORIENTATION_ATTR, &error);
1733 if (error == "") {
1734 if (orien == 0 || orien == 6 || orien == 8) {
1735 // Requests landscape, sensorLandscape, or reverseLandscape.
1736 addImpliedFeature(
1737 &impliedFeatures, "android.hardware.screen.landscape",
1738 String8("one or more activities have specified a "
1739 "landscape orientation"),
1740 false);
1741 } else if (orien == 1 || orien == 7 || orien == 9) {
1742 // Requests portrait, sensorPortrait, or reversePortrait.
1743 addImpliedFeature(
1744 &impliedFeatures, "android.hardware.screen.portrait",
1745 String8("one or more activities have specified a "
1746 "portrait orientation"),
1747 false);
1748 }
1749 }
1750 } else if (tag == "uses-library") {
1751 String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1752 if (error != "") {
1753 SourcePos(manifestFile, tree.getLineNumber()).error(
1754 "ERROR getting 'android:name' attribute for uses-library"
1755 " %s", error.string());
1756 goto bail;
1757 }
1758 int req = AaptXml::getIntegerAttribute(tree,
1759 REQUIRED_ATTR, 1);
1760 printf("uses-library%s:'%s'\n",
1761 req ? "" : "-not-required", ResTable::normalizeForOutput(
1762 libraryName.string()).string());
1763 } else if (tag == "receiver") {
1764 withinReceiver = true;
1765 receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1766
1767 if (error != "") {
1768 SourcePos(manifestFile, tree.getLineNumber()).error(
1769 "ERROR getting 'android:name' attribute for receiver:"
1770 " %s", error.string());
1771 goto bail;
1772 }
1773
1774 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1775 &error);
1776 if (error == "") {
1777 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1778 hasBindDeviceAdminPermission = true;
1779 }
1780 } else {
1781 SourcePos(manifestFile, tree.getLineNumber()).error(
1782 "ERROR getting 'android:permission' attribute for"
1783 " receiver '%s': %s",
1784 receiverName.string(), error.string());
1785 }
1786 } else if (tag == "service") {
1787 withinService = true;
1788 serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1789
1790 if (error != "") {
1791 SourcePos(manifestFile, tree.getLineNumber()).error(
1792 "ERROR getting 'android:name' attribute for "
1793 "service:%s", error.string());
1794 goto bail;
1795 }
1796
1797 String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
1798 &error);
1799 if (error == "") {
1800 if (permission == "android.permission.BIND_INPUT_METHOD") {
1801 hasBindInputMethodPermission = true;
1802 } else if (permission ==
1803 "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1804 hasBindAccessibilityServicePermission = true;
1805 } else if (permission ==
1806 "android.permission.BIND_PRINT_SERVICE") {
1807 hasBindPrintServicePermission = true;
1808 } else if (permission ==
1809 "android.permission.BIND_NFC_SERVICE") {
1810 hasBindNfcServicePermission = true;
1811 } else if (permission ==
1812 "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1813 hasBindNotificationListenerServicePermission = true;
1814 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1815 hasBindDreamServicePermission = true;
1816 }
1817 } else {
1818 SourcePos(manifestFile, tree.getLineNumber()).error(
1819 "ERROR getting 'android:permission' attribute for "
1820 "service '%s': %s", serviceName.string(), error.string());
1821 }
1822 } else if (tag == "provider") {
1823 withinProvider = true;
1824
1825 bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
1826 EXPORTED_ATTR, &error);
1827 if (error != "") {
1828 SourcePos(manifestFile, tree.getLineNumber()).error(
1829 "ERROR getting 'android:exported' attribute for provider:"
1830 " %s", error.string());
1831 goto bail;
1832 }
1833
1834 bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
1835 res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
1836 if (error != "") {
1837 SourcePos(manifestFile, tree.getLineNumber()).error(
1838 "ERROR getting 'android:grantUriPermissions' attribute for "
1839 "provider: %s", error.string());
1840 goto bail;
1841 }
1842
1843 String8 permission = AaptXml::getResolvedAttribute(res, tree,
1844 PERMISSION_ATTR, &error);
1845 if (error != "") {
1846 SourcePos(manifestFile, tree.getLineNumber()).error(
1847 "ERROR getting 'android:permission' attribute for "
1848 "provider: %s", error.string());
1849 goto bail;
1850 }
1851
1852 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1853 permission == "android.permission.MANAGE_DOCUMENTS";
1854
1855 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1856 String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
1857 NAME_ATTR, &error);
1858 if (error != "") {
1859 SourcePos(manifestFile, tree.getLineNumber()).error(
1860 "ERROR getting 'android:name' attribute for "
1861 "meta-data: %s", error.string());
1862 goto bail;
1863 }
1864 printf("meta-data: name='%s' ",
1865 ResTable::normalizeForOutput(metaDataName.string()).string());
1866 printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
1867 &error);
1868 if (error != "") {
1869 // Try looking for a RESOURCE_ATTR
1870 error = "";
1871 printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
1872 String8("resource"), &error);
1873 if (error != "") {
1874 SourcePos(manifestFile, tree.getLineNumber()).error(
1875 "ERROR getting 'android:value' or "
1876 "'android:resource' attribute for "
1877 "meta-data: %s", error.string());
1878 goto bail;
1879 }
1880 }
1881 printf("\n");
1882 } else if (withinSupportsInput && tag == "input-type") {
1883 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1884 if (name != "" && error == "") {
1885 supportedInput.add(name);
1886 } else {
1887 SourcePos(manifestFile, tree.getLineNumber()).error(
1888 "ERROR getting 'android:name' attribute: %s",
1889 error.string());
1890 goto bail;
1891 }
1892 }
1893 } else if (withinFeatureGroup && tag == "uses-feature") {
1894 const String8 androidSchema("http://schemas.android.com/apk/res/android");
1895 FeatureGroup& top = featureGroups.editTop();
1896
1897 String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
1898 if (name != "" && error == "") {
1899 Feature feature(true);
1900
1901 int32_t featureVers = AaptXml::getIntegerAttribute(
1902 tree, androidSchema.string(), "version", 0, &error);
1903 if (error == "") {
1904 feature.version = featureVers;
1905 } else {
1906 SourcePos(manifestFile, tree.getLineNumber()).error(
1907 "failed to read attribute 'android:version': %s",
1908 error.string());
1909 goto bail;
1910 }
1911
1912 top.features.add(name, feature);
1913 addParentFeatures(&top, name);
1914
1915 } else {
1916 int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
1917 &error);
1918 if (error == "") {
1919 if (vers > top.openGLESVersion) {
1920 top.openGLESVersion = vers;
1921 }
1922 }
1923 }
1924 }
1925 } else if (depth == 4) {
1926 if (tag == "intent-filter") {
1927 hasIntentFilter = true;
1928 withinIntentFilter = true;
1929 actMainActivity = false;
1930 actWidgetReceivers = false;
1931 actImeService = false;
1932 actWallpaperService = false;
1933 actAccessibilityService = false;
1934 actPrintService = false;
1935 actDeviceAdminEnabled = false;
1936 actHostApduService = false;
1937 actOffHostApduService = false;
1938 actDocumentsProvider = false;
1939 actNotificationListenerService = false;
1940 actDreamService = false;
1941 actCamera = false;
1942 actCameraSecure = false;
1943 catLauncher = false;
1944 } else if (withinService && tag == "meta-data") {
1945 String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1946 if (error != "") {
1947 SourcePos(manifestFile, tree.getLineNumber()).error(
1948 "ERROR getting 'android:name' attribute for "
1949 "meta-data tag in service '%s': %s", serviceName.string(),
1950 error.string());
1951 goto bail;
1952 }
1953
1954 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1955 name == "android.nfc.cardemulation.off_host_apdu_service") {
1956 bool offHost = true;
1957 if (name == "android.nfc.cardemulation.host_apdu_service") {
1958 offHost = false;
1959 }
1960
1961 String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
1962 RESOURCE_ATTR, &error);
1963 if (error != "") {
1964 SourcePos(manifestFile, tree.getLineNumber()).error(
1965 "ERROR getting 'android:resource' attribute for "
1966 "meta-data tag in service '%s': %s",
1967 serviceName.string(), error.string());
1968 goto bail;
1969 }
1970
1971 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1972 offHost, &error);
1973 if (error != "") {
1974 SourcePos(manifestFile, tree.getLineNumber()).error(
1975 "ERROR getting AID category for service '%s'",
1976 serviceName.string());
1977 goto bail;
1978 }
1979
1980 const size_t catLen = categories.size();
1981 for (size_t i = 0; i < catLen; i++) {
1982 bool paymentCategory = (categories[i] == "payment");
1983 if (offHost) {
1984 hasMetaOffHostPaymentCategory |= paymentCategory;
1985 } else {
1986 hasMetaHostPaymentCategory |= paymentCategory;
1987 }
1988 }
1989 }
1990 }
1991 } else if ((depth == 5) && withinIntentFilter) {
1992 String8 action;
1993 if (tag == "action") {
1994 action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
1995 if (error != "") {
1996 SourcePos(manifestFile, tree.getLineNumber()).error(
1997 "ERROR getting 'android:name' attribute: %s", error.string());
1998 goto bail;
1999 }
2000
2001 if (withinActivity) {
2002 if (action == "android.intent.action.MAIN") {
2003 isMainActivity = true;
2004 actMainActivity = true;
2005 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
2006 action == "android.media.action.VIDEO_CAMERA") {
2007 actCamera = true;
2008 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
2009 actCameraSecure = true;
2010 }
2011 } else if (withinReceiver) {
2012 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
2013 actWidgetReceivers = true;
2014 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
2015 actDeviceAdminEnabled = true;
2016 }
2017 } else if (withinService) {
2018 if (action == "android.view.InputMethod") {
2019 actImeService = true;
2020 } else if (action == "android.service.wallpaper.WallpaperService") {
2021 actWallpaperService = true;
2022 } else if (action ==
2023 "android.accessibilityservice.AccessibilityService") {
2024 actAccessibilityService = true;
2025 } else if (action =="android.printservice.PrintService") {
2026 actPrintService = true;
2027 } else if (action ==
2028 "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
2029 actHostApduService = true;
2030 } else if (action ==
2031 "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
2032 actOffHostApduService = true;
2033 } else if (action ==
2034 "android.service.notification.NotificationListenerService") {
2035 actNotificationListenerService = true;
2036 } else if (action == "android.service.dreams.DreamService") {
2037 actDreamService = true;
2038 }
2039 } else if (withinProvider) {
2040 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
2041 actDocumentsProvider = true;
2042 }
2043 }
2044 if (action == "android.intent.action.SEARCH") {
2045 isSearchable = true;
2046 }
2047 }
2048
2049 if (tag == "category") {
2050 String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
2051 if (error != "") {
2052 SourcePos(manifestFile, tree.getLineNumber()).error(
2053 "ERROR getting 'name' attribute: %s", error.string());
2054 goto bail;
2055 }
2056 if (withinActivity) {
2057 if (category == "android.intent.category.LAUNCHER") {
2058 isLauncherActivity = true;
2059 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
2060 isLeanbackLauncherActivity = true;
2061 } else if (category == "android.intent.category.HOME") {
2062 catLauncher = true;
2063 }
2064 }
2065 }
2066 }
2067 }
2068
2069 // Pre-1.6 implicitly granted permission compatibility logic
2070 if (targetSdk < 4) {
2071 if (!hasWriteExternalStoragePermission) {
2072 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
2073 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
2074 String8("targetSdkVersion < 4"));
2075 hasWriteExternalStoragePermission = true;
2076 }
2077 if (!hasReadPhoneStatePermission) {
2078 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
2079 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
2080 String8("targetSdkVersion < 4"));
2081 }
2082 }
2083
2084 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
2085 // force them to always take READ_EXTERNAL_STORAGE as well. We always
2086 // do this (regardless of target API version) because we can't have
2087 // an app with write permission but not read permission.
2088 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
2089 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2090 false /* optional */, writeExternalStoragePermissionMaxSdkVersion);
2091 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
2092 String8("requested WRITE_EXTERNAL_STORAGE"),
2093 writeExternalStoragePermissionMaxSdkVersion);
2094 }
2095
2096 // Pre-JellyBean call log permission compatibility.
2097 if (targetSdk < 16) {
2098 if (!hasReadCallLogPermission && hasReadContactsPermission) {
2099 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
2100 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
2101 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
2102 }
2103 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
2104 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
2105 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
2106 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
2107 }
2108 }
2109
2110 // If the app hasn't declared the touchscreen as a feature requirement (either
2111 // directly or implied, required or not), then the faketouch feature is implied.
2112 if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) {
2113 addImpliedFeature(&impliedFeatures, "android.hardware.faketouch",
2114 String8("default feature for all apps"), false);
2115 }
2116
2117 const size_t numFeatureGroups = featureGroups.size();
2118 if (numFeatureGroups == 0) {
2119 // If no <feature-group> tags were defined, apply auto-implied features.
2120 printDefaultFeatureGroup(commonFeatures, impliedFeatures);
2121
2122 } else {
2123 // <feature-group> tags are defined, so we ignore implied features and
2124 for (size_t i = 0; i < numFeatureGroups; i++) {
2125 FeatureGroup& grp = featureGroups.editItemAt(i);
2126
2127 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
2128 grp.openGLESVersion = commonFeatures.openGLESVersion;
2129 }
2130
2131 // Merge the features defined in the top level (not inside a <feature-group>)
2132 // with this feature group.
2133 const size_t numCommonFeatures = commonFeatures.features.size();
2134 for (size_t j = 0; j < numCommonFeatures; j++) {
2135 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
2136 grp.features.add(commonFeatures.features.keyAt(j),
2137 commonFeatures.features[j]);
2138 }
2139 }
2140
2141 if (!grp.features.isEmpty()) {
2142 printFeatureGroup(grp);
2143 }
2144 }
2145 }
2146
2147
2148 if (hasWidgetReceivers) {
2149 printComponentPresence("app-widget");
2150 }
2151 if (hasDeviceAdminReceiver) {
2152 printComponentPresence("device-admin");
2153 }
2154 if (hasImeService) {
2155 printComponentPresence("ime");
2156 }
2157 if (hasWallpaperService) {
2158 printComponentPresence("wallpaper");
2159 }
2160 if (hasAccessibilityService) {
2161 printComponentPresence("accessibility");
2162 }
2163 if (hasPrintService) {
2164 printComponentPresence("print-service");
2165 }
2166 if (hasPaymentService) {
2167 printComponentPresence("payment");
2168 }
2169 if (isSearchable) {
2170 printComponentPresence("search");
2171 }
2172 if (hasDocumentsProvider) {
2173 printComponentPresence("document-provider");
2174 }
2175 if (hasLauncher) {
2176 printComponentPresence("launcher");
2177 }
2178 if (hasNotificationListenerService) {
2179 printComponentPresence("notification-listener");
2180 }
2181 if (hasDreamService) {
2182 printComponentPresence("dream");
2183 }
2184 if (hasCameraActivity) {
2185 printComponentPresence("camera");
2186 }
2187 if (hasCameraSecureActivity) {
2188 printComponentPresence("camera-secure");
2189 }
2190
2191 if (hasMainActivity) {
2192 printf("main\n");
2193 }
2194 if (hasOtherActivities) {
2195 printf("other-activities\n");
2196 }
2197 if (hasOtherReceivers) {
2198 printf("other-receivers\n");
2199 }
2200 if (hasOtherServices) {
2201 printf("other-services\n");
2202 }
2203
2204 // For modern apps, if screen size buckets haven't been specified
2205 // but the new width ranges have, then infer the buckets from them.
2206 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2207 && requiresSmallestWidthDp > 0) {
2208 int compatWidth = compatibleWidthLimitDp;
2209 if (compatWidth <= 0) {
2210 compatWidth = requiresSmallestWidthDp;
2211 }
2212 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2213 smallScreen = -1;
2214 } else {
2215 smallScreen = 0;
2216 }
2217 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2218 normalScreen = -1;
2219 } else {
2220 normalScreen = 0;
2221 }
2222 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2223 largeScreen = -1;
2224 } else {
2225 largeScreen = 0;
2226 }
2227 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2228 xlargeScreen = -1;
2229 } else {
2230 xlargeScreen = 0;
2231 }
2232 }
2233
2234 // Determine default values for any unspecified screen sizes,
2235 // based on the target SDK of the package. As of 4 (donut)
2236 // the screen size support was introduced, so all default to
2237 // enabled.
2238 if (smallScreen > 0) {
2239 smallScreen = targetSdk >= 4 ? -1 : 0;
2240 }
2241 if (normalScreen > 0) {
2242 normalScreen = -1;
2243 }
2244 if (largeScreen > 0) {
2245 largeScreen = targetSdk >= 4 ? -1 : 0;
2246 }
2247 if (xlargeScreen > 0) {
2248 // Introduced in Gingerbread.
2249 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2250 }
2251 if (anyDensity > 0) {
2252 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2253 || compatibleWidthLimitDp > 0) ? -1 : 0;
2254 }
2255 printf("supports-screens:");
2256 if (smallScreen != 0) {
2257 printf(" 'small'");
2258 }
2259 if (normalScreen != 0) {
2260 printf(" 'normal'");
2261 }
2262 if (largeScreen != 0) {
2263 printf(" 'large'");
2264 }
2265 if (xlargeScreen != 0) {
2266 printf(" 'xlarge'");
2267 }
2268 printf("\n");
2269 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2270 if (requiresSmallestWidthDp > 0) {
2271 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2272 }
2273 if (compatibleWidthLimitDp > 0) {
2274 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2275 }
2276 if (largestWidthLimitDp > 0) {
2277 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2278 }
2279
2280 printf("locales:");
2281 const size_t NL = locales.size();
2282 for (size_t i=0; i<NL; i++) {
2283 const char* localeStr = locales[i].string();
2284 if (localeStr == NULL || strlen(localeStr) == 0) {
2285 localeStr = "--_--";
2286 }
2287 printf(" '%s'", localeStr);
2288 }
2289 printf("\n");
2290
2291 printf("densities:");
2292 const size_t ND = densities.size();
2293 for (size_t i=0; i<ND; i++) {
2294 printf(" '%d'", densities[i]);
2295 }
2296 printf("\n");
2297
2298 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2299 if (dir != NULL) {
2300 if (dir->getFileCount() > 0) {
2301 SortedVector<String8> architectures;
2302 for (size_t i=0; i<dir->getFileCount(); i++) {
2303 architectures.add(ResTable::normalizeForOutput(
2304 dir->getFileName(i).string()));
2305 }
2306
2307 bool outputAltNativeCode = false;
2308 // A multiArch package is one that contains 64-bit and
2309 // 32-bit versions of native code and expects 3rd-party
2310 // apps to load these native code libraries. Since most
2311 // 64-bit systems also support 32-bit apps, the apps
2312 // loading this multiArch package's code may be either
2313 // 32-bit or 64-bit.
2314 if (hasMultiArch) {
2315 // If this is a multiArch package, report the 64-bit
2316 // version only. Then as a separate entry, report the
2317 // rest.
2318 //
2319 // If we report the 32-bit architecture, this APK will
2320 // be installed on a 32-bit device, causing a large waste
2321 // of bandwidth and disk space. This assumes that
2322 // the developer of the multiArch package has also
2323 // made a version that is 32-bit only.
2324 String8 intel64("x86_64");
2325 String8 arm64("arm64-v8a");
2326 ssize_t index = architectures.indexOf(intel64);
2327 if (index < 0) {
2328 index = architectures.indexOf(arm64);
2329 }
2330
2331 if (index >= 0) {
2332 printf("native-code: '%s'\n", architectures[index].string());
2333 architectures.removeAt(index);
2334 outputAltNativeCode = true;
2335 }
2336 }
2337
2338 const size_t archCount = architectures.size();
2339 if (archCount > 0) {
2340 if (outputAltNativeCode) {
2341 printf("alt-");
2342 }
2343 printf("native-code:");
2344 for (size_t i = 0; i < archCount; i++) {
2345 printf(" '%s'", architectures[i].string());
2346 }
2347 printf("\n");
2348 }
2349 }
2350 delete dir;
2351 }
2352 } else if (strcmp("badger", option) == 0) {
2353 printf("%s", CONSOLE_DATA);
2354 } else if (strcmp("configurations", option) == 0) {
2355 Vector<ResTable_config> configs;
2356 res.getConfigurations(&configs);
2357 const size_t N = configs.size();
2358 for (size_t i=0; i<N; i++) {
2359 printf("%s\n", configs[i].toString().string());
2360 }
2361 } else {
2362 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2363 goto bail;
2364 }
2365 }
2366
2367 result = NO_ERROR;
2368
2369 bail:
2370 if (SourcePos::hasErrors()) {
2371 SourcePos::printErrors(stderr);
2372 }
2373
2374 if (asset) {
2375 delete asset;
2376 }
2377 return (result != NO_ERROR);
2378 }
2379
2380
2381 /*
2382 * Handle the "add" command, which wants to add files to a new or
2383 * pre-existing archive.
2384 */
doAdd(Bundle * bundle)2385 int doAdd(Bundle* bundle)
2386 {
2387 ZipFile* zip = NULL;
2388 status_t result = UNKNOWN_ERROR;
2389 const char* zipFileName;
2390
2391 if (bundle->getUpdate()) {
2392 /* avoid confusion */
2393 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2394 goto bail;
2395 }
2396
2397 if (bundle->getFileSpecCount() < 1) {
2398 fprintf(stderr, "ERROR: must specify zip file name\n");
2399 goto bail;
2400 }
2401 zipFileName = bundle->getFileSpecEntry(0);
2402
2403 if (bundle->getFileSpecCount() < 2) {
2404 fprintf(stderr, "NOTE: nothing to do\n");
2405 goto bail;
2406 }
2407
2408 zip = openReadWrite(zipFileName, true);
2409 if (zip == NULL) {
2410 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2411 goto bail;
2412 }
2413
2414 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2415 const char* fileName = bundle->getFileSpecEntry(i);
2416
2417 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2418 printf(" '%s'... (from gzip)\n", fileName);
2419 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2420 } else {
2421 if (bundle->getJunkPath()) {
2422 String8 storageName = String8(fileName).getPathLeaf();
2423 printf(" '%s' as '%s'...\n", fileName,
2424 ResTable::normalizeForOutput(storageName.string()).string());
2425 result = zip->add(fileName, storageName.string(),
2426 bundle->getCompressionMethod(), NULL);
2427 } else {
2428 printf(" '%s'...\n", fileName);
2429 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2430 }
2431 }
2432 if (result != NO_ERROR) {
2433 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2434 if (result == NAME_NOT_FOUND) {
2435 fprintf(stderr, ": file not found\n");
2436 } else if (result == ALREADY_EXISTS) {
2437 fprintf(stderr, ": already exists in archive\n");
2438 } else {
2439 fprintf(stderr, "\n");
2440 }
2441 goto bail;
2442 }
2443 }
2444
2445 result = NO_ERROR;
2446
2447 bail:
2448 delete zip;
2449 return (result != NO_ERROR);
2450 }
2451
2452
2453 /*
2454 * Delete files from an existing archive.
2455 */
doRemove(Bundle * bundle)2456 int doRemove(Bundle* bundle)
2457 {
2458 ZipFile* zip = NULL;
2459 status_t result = UNKNOWN_ERROR;
2460 const char* zipFileName;
2461
2462 if (bundle->getFileSpecCount() < 1) {
2463 fprintf(stderr, "ERROR: must specify zip file name\n");
2464 goto bail;
2465 }
2466 zipFileName = bundle->getFileSpecEntry(0);
2467
2468 if (bundle->getFileSpecCount() < 2) {
2469 fprintf(stderr, "NOTE: nothing to do\n");
2470 goto bail;
2471 }
2472
2473 zip = openReadWrite(zipFileName, false);
2474 if (zip == NULL) {
2475 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2476 zipFileName);
2477 goto bail;
2478 }
2479
2480 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2481 const char* fileName = bundle->getFileSpecEntry(i);
2482 ZipEntry* entry;
2483
2484 entry = zip->getEntryByName(fileName);
2485 if (entry == NULL) {
2486 printf(" '%s' NOT FOUND\n", fileName);
2487 continue;
2488 }
2489
2490 result = zip->remove(entry);
2491
2492 if (result != NO_ERROR) {
2493 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2494 bundle->getFileSpecEntry(i), zipFileName);
2495 goto bail;
2496 }
2497 }
2498
2499 /* update the archive */
2500 zip->flush();
2501
2502 bail:
2503 delete zip;
2504 return (result != NO_ERROR);
2505 }
2506
addResourcesToBuilder(const sp<AaptDir> & dir,const sp<ApkBuilder> & builder,bool ignoreConfig=false)2507 static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
2508 const size_t numDirs = dir->getDirs().size();
2509 for (size_t i = 0; i < numDirs; i++) {
2510 bool ignore = ignoreConfig;
2511 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2512 const char* dirStr = subDir->getLeaf().string();
2513 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2514 ignore = true;
2515 }
2516 status_t err = addResourcesToBuilder(subDir, builder, ignore);
2517 if (err != NO_ERROR) {
2518 return err;
2519 }
2520 }
2521
2522 const size_t numFiles = dir->getFiles().size();
2523 for (size_t i = 0; i < numFiles; i++) {
2524 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2525 const size_t numConfigs = gp->getFiles().size();
2526 for (size_t j = 0; j < numConfigs; j++) {
2527 status_t err = NO_ERROR;
2528 if (ignoreConfig) {
2529 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2530 } else {
2531 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2532 }
2533 if (err != NO_ERROR) {
2534 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2535 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2536 return err;
2537 }
2538 }
2539 }
2540 return NO_ERROR;
2541 }
2542
buildApkName(const String8 & original,const sp<ApkSplit> & split)2543 static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2544 if (split->isBase()) {
2545 return original;
2546 }
2547
2548 String8 ext(original.getPathExtension());
2549 if (ext == String8(".apk")) {
2550 return String8::format("%s_%s%s",
2551 original.getBasePath().string(),
2552 split->getDirectorySafeName().string(),
2553 ext.string());
2554 }
2555
2556 return String8::format("%s_%s", original.string(),
2557 split->getDirectorySafeName().string());
2558 }
2559
2560 /*
2561 * Package up an asset directory and associated application files.
2562 */
doPackage(Bundle * bundle)2563 int doPackage(Bundle* bundle)
2564 {
2565 const char* outputAPKFile;
2566 int retVal = 1;
2567 status_t err;
2568 sp<AaptAssets> assets;
2569 int N;
2570 FILE* fp;
2571 String8 dependencyFile;
2572 sp<ApkBuilder> builder;
2573
2574 // -c en_XA or/and ar_XB means do pseudolocalization
2575 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2576 err = configFilter->parse(bundle->getConfigurations());
2577 if (err != NO_ERROR) {
2578 goto bail;
2579 }
2580 if (configFilter->containsPseudo()) {
2581 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2582 }
2583 if (configFilter->containsPseudoBidi()) {
2584 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
2585 }
2586
2587 N = bundle->getFileSpecCount();
2588 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
2589 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
2590 fprintf(stderr, "ERROR: no input files\n");
2591 goto bail;
2592 }
2593
2594 outputAPKFile = bundle->getOutputAPKFile();
2595
2596 // Make sure the filenames provided exist and are of the appropriate type.
2597 if (outputAPKFile) {
2598 FileType type;
2599 type = getFileType(outputAPKFile);
2600 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2601 fprintf(stderr,
2602 "ERROR: output file '%s' exists but is not regular file\n",
2603 outputAPKFile);
2604 goto bail;
2605 }
2606 }
2607
2608 // Load the assets.
2609 assets = new AaptAssets();
2610
2611 // Set up the resource gathering in assets if we're going to generate
2612 // dependency files. Every time we encounter a resource while slurping
2613 // the tree, we'll add it to these stores so we have full resource paths
2614 // to write to a dependency file.
2615 if (bundle->getGenDependencies()) {
2616 sp<FilePathStore> resPathStore = new FilePathStore;
2617 assets->setFullResPaths(resPathStore);
2618 sp<FilePathStore> assetPathStore = new FilePathStore;
2619 assets->setFullAssetPaths(assetPathStore);
2620 }
2621
2622 err = assets->slurpFromArgs(bundle);
2623 if (err < 0) {
2624 goto bail;
2625 }
2626
2627 if (bundle->getVerbose()) {
2628 assets->print(String8());
2629 }
2630
2631 // Create the ApkBuilder, which will collect the compiled files
2632 // to write to the final APK (or sets of APKs if we are building
2633 // a Split APK.
2634 builder = new ApkBuilder(configFilter);
2635
2636 // If we are generating a Split APK, find out which configurations to split on.
2637 if (bundle->getSplitConfigurations().size() > 0) {
2638 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2639 const size_t numSplits = splitStrs.size();
2640 for (size_t i = 0; i < numSplits; i++) {
2641 std::set<ConfigDescription> configs;
2642 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2643 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2644 goto bail;
2645 }
2646
2647 err = builder->createSplitForConfigs(configs);
2648 if (err != NO_ERROR) {
2649 goto bail;
2650 }
2651 }
2652 }
2653
2654 // If they asked for any fileAs that need to be compiled, do so.
2655 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
2656 err = buildResources(bundle, assets, builder);
2657 if (err != 0) {
2658 goto bail;
2659 }
2660 }
2661
2662 // At this point we've read everything and processed everything. From here
2663 // on out it's just writing output files.
2664 if (SourcePos::hasErrors()) {
2665 goto bail;
2666 }
2667
2668 // Update symbols with information about which ones are needed as Java symbols.
2669 assets->applyJavaSymbols();
2670 if (SourcePos::hasErrors()) {
2671 goto bail;
2672 }
2673
2674 // If we've been asked to generate a dependency file, do that here
2675 if (bundle->getGenDependencies()) {
2676 // If this is the packaging step, generate the dependency file next to
2677 // the output apk (e.g. bin/resources.ap_.d)
2678 if (outputAPKFile) {
2679 dependencyFile = String8(outputAPKFile);
2680 // Add the .d extension to the dependency file.
2681 dependencyFile.append(".d");
2682 } else {
2683 // Else if this is the R.java dependency generation step,
2684 // generate the dependency file in the R.java package subdirectory
2685 // e.g. gen/com/foo/app/R.java.d
2686 dependencyFile = String8(bundle->getRClassDir());
2687 dependencyFile.appendPath("R.java.d");
2688 }
2689 // Make sure we have a clean dependency file to start with
2690 fp = fopen(dependencyFile, "w");
2691 fclose(fp);
2692 }
2693
2694 // Write out R.java constants
2695 if (!assets->havePrivateSymbols()) {
2696 if (bundle->getCustomPackage() == NULL) {
2697 // Write the R.java file into the appropriate class directory
2698 // e.g. gen/com/foo/app/R.java
2699 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2700 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
2701 } else {
2702 const String8 customPkg(bundle->getCustomPackage());
2703 err = writeResourceSymbols(bundle, assets, customPkg, true,
2704 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
2705 }
2706 if (err < 0) {
2707 goto bail;
2708 }
2709 // If we have library files, we're going to write our R.java file into
2710 // the appropriate class directory for those libraries as well.
2711 // e.g. gen/com/foo/app/lib/R.java
2712 if (bundle->getExtraPackages() != NULL) {
2713 // Split on colon
2714 String8 libs(bundle->getExtraPackages());
2715 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2716 while (packageString != NULL) {
2717 // Write the R.java file out with the correct package name
2718 err = writeResourceSymbols(bundle, assets, String8(packageString), true,
2719 bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
2720 if (err < 0) {
2721 goto bail;
2722 }
2723 packageString = strtok(NULL, ":");
2724 }
2725 libs.unlockBuffer();
2726 }
2727 } else {
2728 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
2729 if (err < 0) {
2730 goto bail;
2731 }
2732 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
2733 if (err < 0) {
2734 goto bail;
2735 }
2736 }
2737
2738 // Write out the ProGuard file
2739 err = writeProguardFile(bundle, assets);
2740 if (err < 0) {
2741 goto bail;
2742 }
2743
2744 // Write out the Main Dex ProGuard file
2745 err = writeMainDexProguardFile(bundle, assets);
2746 if (err < 0) {
2747 goto bail;
2748 }
2749
2750 // Write the apk
2751 if (outputAPKFile) {
2752 // Gather all resources and add them to the APK Builder. The builder will then
2753 // figure out which Split they belong in.
2754 err = addResourcesToBuilder(assets, builder);
2755 if (err != NO_ERROR) {
2756 goto bail;
2757 }
2758
2759 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2760 const size_t numSplits = splits.size();
2761 for (size_t i = 0; i < numSplits; i++) {
2762 const sp<ApkSplit>& split = splits[i];
2763 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2764 err = writeAPK(bundle, outputPath, split);
2765 if (err != NO_ERROR) {
2766 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2767 goto bail;
2768 }
2769 }
2770 }
2771
2772 // If we've been asked to generate a dependency file, we need to finish up here.
2773 // the writeResourceSymbols and writeAPK functions have already written the target
2774 // half of the dependency file, now we need to write the prerequisites. (files that
2775 // the R.java file or .ap_ file depend on)
2776 if (bundle->getGenDependencies()) {
2777 // Now that writeResourceSymbols or writeAPK has taken care of writing
2778 // the targets to our dependency file, we'll write the prereqs
2779 fp = fopen(dependencyFile, "a+");
2780 fprintf(fp, " : ");
2781 bool includeRaw = (outputAPKFile != NULL);
2782 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2783 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2784 // and therefore was not added to our pathstores during slurping
2785 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2786 fclose(fp);
2787 }
2788
2789 retVal = 0;
2790 bail:
2791 if (SourcePos::hasErrors()) {
2792 SourcePos::printErrors(stderr);
2793 }
2794 return retVal;
2795 }
2796
2797 /*
2798 * Do PNG Crunching
2799 * PRECONDITIONS
2800 * -S flag points to a source directory containing drawable* folders
2801 * -C flag points to destination directory. The folder structure in the
2802 * source directory will be mirrored to the destination (cache) directory
2803 *
2804 * POSTCONDITIONS
2805 * Destination directory will be updated to match the PNG files in
2806 * the source directory.
2807 */
doCrunch(Bundle * bundle)2808 int doCrunch(Bundle* bundle)
2809 {
2810 fprintf(stdout, "Crunching PNG Files in ");
2811 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2812 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2813
2814 updatePreProcessedCache(bundle);
2815
2816 return NO_ERROR;
2817 }
2818
2819 /*
2820 * Do PNG Crunching on a single flag
2821 * -i points to a single png file
2822 * -o points to a single png output file
2823 */
doSingleCrunch(Bundle * bundle)2824 int doSingleCrunch(Bundle* bundle)
2825 {
2826 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2827 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2828
2829 String8 input(bundle->getSingleCrunchInputFile());
2830 String8 output(bundle->getSingleCrunchOutputFile());
2831
2832 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2833 // we can't return the status_t as it gets truncate to the lower 8 bits.
2834 return 42;
2835 }
2836
2837 return NO_ERROR;
2838 }
2839
runInDaemonMode(Bundle * bundle)2840 int runInDaemonMode(Bundle* bundle) {
2841 std::cout << "Ready" << std::endl;
2842 for (std::string cmd; std::getline(std::cin, cmd);) {
2843 if (cmd == "quit") {
2844 return NO_ERROR;
2845 } else if (cmd == "s") {
2846 // Two argument crunch
2847 std::string inputFile, outputFile;
2848 std::getline(std::cin, inputFile);
2849 std::getline(std::cin, outputFile);
2850 bundle->setSingleCrunchInputFile(inputFile.c_str());
2851 bundle->setSingleCrunchOutputFile(outputFile.c_str());
2852 std::cout << "Crunching " << inputFile << std::endl;
2853 if (doSingleCrunch(bundle) != NO_ERROR) {
2854 std::cout << "Error" << std::endl;
2855 }
2856 std::cout << "Done" << std::endl;
2857 } else {
2858 // in case of invalid command, just bail out.
2859 std::cerr << "Unknown command" << std::endl;
2860 return -1;
2861 }
2862 }
2863 return -1;
2864 }
2865
2866 char CONSOLE_DATA[2925] = {
2867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2868 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2871 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2872 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2873 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2874 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2876 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2877 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2878 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2879 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2880 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2881 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2882 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2883 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2884 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2885 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2886 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2887 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2889 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2890 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2891 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2892 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2893 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2894 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2895 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2896 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2897 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2898 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2899 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2900 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2901 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2902 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2903 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2904 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2905 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2906 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2907 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2908 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2909 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2910 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2911 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2912 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2913 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2914 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2915 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2916 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2917 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2918 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2919 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2920 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2921 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2922 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2923 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2924 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2925 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2926 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2927 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2928 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2929 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2930 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2931 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2932 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2933 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2934 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2935 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2936 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2937 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2938 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2939 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2940 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2941 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2942 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2943 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2944 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2945 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2946 87, 66, 33, 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, 58, 59, 59, 93, 81,
2948 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2949 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 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, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2952 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 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, 58,
2955 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2956 59, 59, 59, 46, 46, 32, 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, 58, 61, 59, 60, 81, 81, 81, 81,
2959 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 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, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2963 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 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, 58, 61, 58, 106,
2966 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2967 32, 46, 32, 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, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2970 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 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 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2974 61, 58, 59, 59, 46, 32, 32, 32, 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, 58, 61, 58, 61, 81, 81, 81,
2977 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 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, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2981 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 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, 58, 61, 59,
2984 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2985 59, 59, 46, 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, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2988 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 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, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2991 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2992 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 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, 32, 32,
2994 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2995 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2996 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2997 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2998 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2999 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
3000 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
3001 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
3002 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
3003 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3004 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3005 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3006 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
3007 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
3008 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3009 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
3010 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3011 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
3012 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
3013 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
3014 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3015 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3016 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
3017 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
3018 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
3019 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
3020 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
3021 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3022 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3023 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
3024 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
3025 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3026 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3027 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3028 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
3029 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
3030 };
3031