blob: 755a77ab19c1001131f65e428ca9ad595014fa5e [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
Adam Lesinskifab50872014-04-16 14:40:42 -07006#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08007#include "Bundle.h"
Adam Lesinski2c72b682014-06-24 09:56:01 -07008#include "Images.h"
9#include "Main.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080010#include "ResourceFilter.h"
11#include "ResourceTable.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080012#include "XMLNode.h"
13
Adam Lesinski282e1812014-01-23 18:17:42 -080014#include <utils/Errors.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070015#include <utils/KeyedVector.h>
16#include <utils/List.h>
17#include <utils/Log.h>
18#include <utils/SortedVector.h>
19#include <utils/threads.h>
20#include <utils/Vector.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080021
Adam Lesinski282e1812014-01-23 18:17:42 -080022#include <errno.h>
Adam Lesinski2c72b682014-06-24 09:56:01 -070023#include <fcntl.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080024
25using namespace android;
26
27/*
28 * Show version info. All the cool kids do it.
29 */
30int doVersion(Bundle* bundle)
31{
32 if (bundle->getFileSpecCount() != 0) {
33 printf("(ignoring extra arguments)\n");
34 }
35 printf("Android Asset Packaging Tool, v0.2\n");
36
37 return 0;
38}
39
40
41/*
42 * Open the file read only. The call fails if the file doesn't exist.
43 *
44 * Returns NULL on failure.
45 */
46ZipFile* openReadOnly(const char* fileName)
47{
48 ZipFile* zip;
49 status_t result;
50
51 zip = new ZipFile;
52 result = zip->open(fileName, ZipFile::kOpenReadOnly);
53 if (result != NO_ERROR) {
54 if (result == NAME_NOT_FOUND) {
55 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
56 } else if (result == PERMISSION_DENIED) {
57 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
58 } else {
59 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
60 fileName);
61 }
62 delete zip;
63 return NULL;
64 }
65
66 return zip;
67}
68
69/*
70 * Open the file read-write. The file will be created if it doesn't
71 * already exist and "okayToCreate" is set.
72 *
73 * Returns NULL on failure.
74 */
75ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
76{
77 ZipFile* zip = NULL;
78 status_t result;
79 int flags;
80
81 flags = ZipFile::kOpenReadWrite;
82 if (okayToCreate) {
83 flags |= ZipFile::kOpenCreate;
84 }
85
86 zip = new ZipFile;
87 result = zip->open(fileName, flags);
88 if (result != NO_ERROR) {
89 delete zip;
90 zip = NULL;
91 goto bail;
92 }
93
94bail:
95 return zip;
96}
97
98
99/*
100 * Return a short string describing the compression method.
101 */
102const char* compressionName(int method)
103{
104 if (method == ZipEntry::kCompressStored) {
105 return "Stored";
106 } else if (method == ZipEntry::kCompressDeflated) {
107 return "Deflated";
108 } else {
109 return "Unknown";
110 }
111}
112
113/*
114 * Return the percent reduction in size (0% == no compression).
115 */
116int calcPercent(long uncompressedLen, long compressedLen)
117{
118 if (!uncompressedLen) {
119 return 0;
120 } else {
121 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
122 }
123}
124
125/*
126 * Handle the "list" command, which can be a simple file dump or
127 * a verbose listing.
128 *
129 * The verbose listing closely matches the output of the Info-ZIP "unzip"
130 * command.
131 */
132int doList(Bundle* bundle)
133{
134 int result = 1;
135 ZipFile* zip = NULL;
136 const ZipEntry* entry;
137 long totalUncLen, totalCompLen;
138 const char* zipFileName;
139
140 if (bundle->getFileSpecCount() != 1) {
141 fprintf(stderr, "ERROR: specify zip file name (only)\n");
142 goto bail;
143 }
144 zipFileName = bundle->getFileSpecEntry(0);
145
146 zip = openReadOnly(zipFileName);
147 if (zip == NULL) {
148 goto bail;
149 }
150
151 int count, i;
152
153 if (bundle->getVerbose()) {
154 printf("Archive: %s\n", zipFileName);
155 printf(
156 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
157 printf(
158 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
159 }
160
161 totalUncLen = totalCompLen = 0;
162
163 count = zip->getNumEntries();
164 for (i = 0; i < count; i++) {
165 entry = zip->getEntryByIndex(i);
166 if (bundle->getVerbose()) {
167 char dateBuf[32];
168 time_t when;
169
170 when = entry->getModWhen();
171 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
172 localtime(&when));
173
174 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
175 (long) entry->getUncompressedLen(),
176 compressionName(entry->getCompressionMethod()),
177 (long) entry->getCompressedLen(),
178 calcPercent(entry->getUncompressedLen(),
179 entry->getCompressedLen()),
180 (size_t) entry->getLFHOffset(),
181 dateBuf,
182 entry->getCRC32(),
183 entry->getFileName());
184 } else {
185 printf("%s\n", entry->getFileName());
186 }
187
188 totalUncLen += entry->getUncompressedLen();
189 totalCompLen += entry->getCompressedLen();
190 }
191
192 if (bundle->getVerbose()) {
193 printf(
194 "-------- ------- --- -------\n");
195 printf("%8ld %7ld %2d%% %d files\n",
196 totalUncLen,
197 totalCompLen,
198 calcPercent(totalUncLen, totalCompLen),
199 zip->getNumEntries());
200 }
201
202 if (bundle->getAndroidList()) {
203 AssetManager assets;
204 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
205 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
206 goto bail;
207 }
208
209 const ResTable& res = assets.getResources(false);
210 if (&res == NULL) {
211 printf("\nNo resource table found.\n");
212 } else {
213#ifndef HAVE_ANDROID_OS
214 printf("\nResource table:\n");
215 res.print(false);
216#endif
217 }
218
219 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
220 Asset::ACCESS_BUFFER);
221 if (manifestAsset == NULL) {
222 printf("\nNo AndroidManifest.xml found.\n");
223 } else {
224 printf("\nAndroid manifest:\n");
225 ResXMLTree tree;
226 tree.setTo(manifestAsset->getBuffer(true),
227 manifestAsset->getLength());
228 printXMLBlock(&tree);
229 }
230 delete manifestAsset;
231 }
232
233 result = 0;
234
235bail:
236 delete zip;
237 return result;
238}
239
240static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
241{
242 size_t N = tree.getAttributeCount();
243 for (size_t i=0; i<N; i++) {
244 if (tree.getAttributeNameResID(i) == attrRes) {
245 return (ssize_t)i;
246 }
247 }
248 return -1;
249}
250
251String8 getAttribute(const ResXMLTree& tree, const char* ns,
252 const char* attr, String8* outError)
253{
254 ssize_t idx = tree.indexOfAttribute(ns, attr);
255 if (idx < 0) {
256 return String8();
257 }
258 Res_value value;
259 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
260 if (value.dataType != Res_value::TYPE_STRING) {
261 if (outError != NULL) {
262 *outError = "attribute is not a string value";
263 }
264 return String8();
265 }
266 }
267 size_t len;
268 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
269 return str ? String8(str, len) : String8();
270}
271
272static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
273{
274 ssize_t idx = indexOfAttribute(tree, attrRes);
275 if (idx < 0) {
276 return String8();
277 }
278 Res_value value;
279 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
280 if (value.dataType != Res_value::TYPE_STRING) {
281 if (outError != NULL) {
282 *outError = "attribute is not a string value";
283 }
284 return String8();
285 }
286 }
287 size_t len;
288 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
289 return str ? String8(str, len) : String8();
290}
291
292static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
293 String8* outError, int32_t defValue = -1)
294{
295 ssize_t idx = indexOfAttribute(tree, attrRes);
296 if (idx < 0) {
297 return defValue;
298 }
299 Res_value value;
300 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
301 if (value.dataType < Res_value::TYPE_FIRST_INT
302 || value.dataType > Res_value::TYPE_LAST_INT) {
303 if (outError != NULL) {
304 *outError = "attribute is not an integer value";
305 }
306 return defValue;
307 }
308 }
309 return value.data;
310}
311
312static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
313 uint32_t attrRes, String8* outError, int32_t defValue = -1)
314{
315 ssize_t idx = indexOfAttribute(tree, attrRes);
316 if (idx < 0) {
317 return defValue;
318 }
319 Res_value value;
320 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
321 if (value.dataType == Res_value::TYPE_REFERENCE) {
322 resTable->resolveReference(&value, 0);
323 }
324 if (value.dataType < Res_value::TYPE_FIRST_INT
325 || value.dataType > Res_value::TYPE_LAST_INT) {
326 if (outError != NULL) {
327 *outError = "attribute is not an integer value";
328 }
329 return defValue;
330 }
331 }
332 return value.data;
333}
334
335static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
336 uint32_t attrRes, String8* outError)
337{
338 ssize_t idx = indexOfAttribute(tree, attrRes);
339 if (idx < 0) {
340 return String8();
341 }
342 Res_value value;
343 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
344 if (value.dataType == Res_value::TYPE_STRING) {
345 size_t len;
346 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
347 return str ? String8(str, len) : String8();
348 }
349 resTable->resolveReference(&value, 0);
350 if (value.dataType != Res_value::TYPE_STRING) {
351 if (outError != NULL) {
352 *outError = "attribute is not a string value";
353 }
354 return String8();
355 }
356 }
357 size_t len;
358 const Res_value* value2 = &value;
359 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
360 return str ? String8(str, len) : String8();
361}
362
363static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
364 const ResXMLTree& tree, uint32_t attrRes, String8* outError)
365{
366 ssize_t idx = indexOfAttribute(tree, attrRes);
367 if (idx < 0) {
368 if (outError != NULL) {
369 *outError = "attribute could not be found";
370 }
371 return;
372 }
373 if (tree.getAttributeValue(idx, value) != NO_ERROR) {
374 if (value->dataType == Res_value::TYPE_REFERENCE) {
375 resTable->resolveReference(value, 0);
376 }
377 // The attribute was found and was resolved if need be.
378 return;
379 }
380 if (outError != NULL) {
381 *outError = "error getting resolved resource attribute";
382 }
383}
384
Maurice Chu76327312013-10-16 18:28:46 -0700385static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
386 uint32_t attrRes, String8 attrLabel, String8* outError)
387{
388 Res_value value;
389 getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
390 if (*outError != "") {
391 *outError = "error print resolved resource attribute";
392 return;
393 }
394 if (value.dataType == Res_value::TYPE_STRING) {
395 String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700396 printf("%s='%s'", attrLabel.string(),
397 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700398 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
399 value.dataType <= Res_value::TYPE_LAST_INT) {
400 printf("%s='%d'", attrLabel.string(), value.data);
401 } else {
402 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
403 }
404}
405
Adam Lesinski282e1812014-01-23 18:17:42 -0800406// These are attribute resource constants for the platform, as found
407// in android.R.attr
408enum {
409 LABEL_ATTR = 0x01010001,
410 ICON_ATTR = 0x01010002,
411 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700412 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700413 EXPORTED_ATTR = 0x01010010,
414 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700415 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800416 DEBUGGABLE_ATTR = 0x0101000f,
417 VALUE_ATTR = 0x01010024,
418 VERSION_CODE_ATTR = 0x0101021b,
419 VERSION_NAME_ATTR = 0x0101021c,
420 SCREEN_ORIENTATION_ATTR = 0x0101001e,
421 MIN_SDK_VERSION_ATTR = 0x0101020c,
422 MAX_SDK_VERSION_ATTR = 0x01010271,
423 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
424 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
425 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
426 REQ_NAVIGATION_ATTR = 0x0101022a,
427 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
428 TARGET_SDK_VERSION_ATTR = 0x01010270,
429 TEST_ONLY_ATTR = 0x01010272,
430 ANY_DENSITY_ATTR = 0x0101026c,
431 GL_ES_VERSION_ATTR = 0x01010281,
432 SMALL_SCREEN_ATTR = 0x01010284,
433 NORMAL_SCREEN_ATTR = 0x01010285,
434 LARGE_SCREEN_ATTR = 0x01010286,
435 XLARGE_SCREEN_ATTR = 0x010102bf,
436 REQUIRED_ATTR = 0x0101028e,
437 SCREEN_SIZE_ATTR = 0x010102ca,
438 SCREEN_DENSITY_ATTR = 0x010102cb,
439 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
440 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
441 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
442 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700443 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800444 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800445};
446
Maurice Chu2675f762013-10-22 17:33:11 -0700447String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800448 ssize_t idx = componentName.find(".");
449 String8 retStr(pkgName);
450 if (idx == 0) {
451 retStr += componentName;
452 } else if (idx < 0) {
453 retStr += ".";
454 retStr += componentName;
455 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700456 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800457 }
Maurice Chu2675f762013-10-22 17:33:11 -0700458 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800459}
460
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700461static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800462 size_t len;
463 ResXMLTree::event_code_t code;
464 int depth = 0;
465 bool first = true;
466 printf("compatible-screens:");
467 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
468 if (code == ResXMLTree::END_TAG) {
469 depth--;
470 if (depth < 0) {
471 break;
472 }
473 continue;
474 }
475 if (code != ResXMLTree::START_TAG) {
476 continue;
477 }
478 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700479 const char16_t* ctag16 = tree.getElementName(&len);
480 if (ctag16 == NULL) {
481 *outError = "failed to get XML element name (bad string pool)";
482 return;
483 }
484 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800485 if (tag == "screen") {
486 int32_t screenSize = getIntegerAttribute(tree,
487 SCREEN_SIZE_ATTR, NULL, -1);
488 int32_t screenDensity = getIntegerAttribute(tree,
489 SCREEN_DENSITY_ATTR, NULL, -1);
490 if (screenSize > 0 && screenDensity > 0) {
491 if (!first) {
492 printf(",");
493 }
494 first = false;
495 printf("'%d/%d'", screenSize, screenDensity);
496 }
497 }
498 }
499 printf("\n");
500}
501
Adam Lesinski58f1f362013-11-12 12:59:08 -0800502static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
503 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
504 if (maxSdkVersion != -1) {
505 printf(" maxSdkVersion='%d'", maxSdkVersion);
506 }
507 printf("\n");
508
509 if (optional) {
510 printf("optional-permission: name='%s'",
511 ResTable::normalizeForOutput(name.string()).string());
512 if (maxSdkVersion != -1) {
513 printf(" maxSdkVersion='%d'", maxSdkVersion);
514 }
515 printf("\n");
516 }
517}
518
519static void printUsesImpliedPermission(const String8& name, const String8& reason) {
520 printf("uses-implied-permission: name='%s' reason='%s'\n",
521 ResTable::normalizeForOutput(name.string()).string(),
522 ResTable::normalizeForOutput(reason.string()).string());
523}
524
Adam Lesinski94fc9122013-09-30 17:16:09 -0700525Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
526 String8 *outError = NULL)
527{
528 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
529 if (aidAsset == NULL) {
530 if (outError != NULL) *outError = "xml resource does not exist";
531 return Vector<String8>();
532 }
533
534 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
535
536 bool withinApduService = false;
537 Vector<String8> categories;
538
539 String8 error;
540 ResXMLTree tree;
541 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
542
543 size_t len;
544 int depth = 0;
545 ResXMLTree::event_code_t code;
546 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
547 if (code == ResXMLTree::END_TAG) {
548 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700549 const char16_t* ctag16 = tree.getElementName(&len);
550 if (ctag16 == NULL) {
551 *outError = "failed to get XML element name (bad string pool)";
552 return Vector<String8>();
553 }
554 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700555
556 if (depth == 0 && tag == serviceTagName) {
557 withinApduService = false;
558 }
559
560 } else if (code == ResXMLTree::START_TAG) {
561 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700562 const char16_t* ctag16 = tree.getElementName(&len);
563 if (ctag16 == NULL) {
564 *outError = "failed to get XML element name (bad string pool)";
565 return Vector<String8>();
566 }
567 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700568
569 if (depth == 1) {
570 if (tag == serviceTagName) {
571 withinApduService = true;
572 }
573 } else if (depth == 2 && withinApduService) {
574 if (tag == "aid-group") {
575 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
576 if (error != "") {
577 if (outError != NULL) *outError = error;
578 return Vector<String8>();
579 }
580
581 categories.add(category);
582 }
583 }
584 }
585 }
586 aidAsset->close();
587 return categories;
588}
589
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700590static void printComponentPresence(const char* componentName) {
591 printf("provides-component:'%s'\n", componentName);
592}
593
Adam Lesinski2c72b682014-06-24 09:56:01 -0700594/**
595 * Represents a feature that has been automatically added due to
596 * a pre-requisite or some other reason.
597 */
598struct ImpliedFeature {
599 /**
600 * Name of the implied feature.
601 */
602 String8 name;
603
604 /**
605 * List of human-readable reasons for why this feature was implied.
606 */
607 SortedVector<String8> reasons;
608};
609
610/**
611 * Represents a <feature-group> tag in the AndroidManifest.xml
612 */
613struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700614 FeatureGroup() : openGLESVersion(-1) {}
615
Adam Lesinski2c72b682014-06-24 09:56:01 -0700616 /**
617 * Human readable label
618 */
619 String8 label;
620
621 /**
622 * Explicit features defined in the group
623 */
624 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700625
626 /**
627 * OpenGL ES version required
628 */
629 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700630};
631
632static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
633 const char* name, const char* reason) {
634 String8 name8(name);
635 ssize_t idx = impliedFeatures->indexOfKey(name8);
636 if (idx < 0) {
637 idx = impliedFeatures->add(name8, ImpliedFeature());
638 impliedFeatures->editValueAt(idx).name = name8;
639 }
640 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
641}
642
643static void printFeatureGroup(const FeatureGroup& grp,
644 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
645 printf("feature-group: label='%s'\n", grp.label.string());
646
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700647 if (grp.openGLESVersion > 0) {
648 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
649 }
650
Adam Lesinski2c72b682014-06-24 09:56:01 -0700651 const size_t numFeatures = grp.features.size();
652 for (size_t i = 0; i < numFeatures; i++) {
653 if (!grp.features[i]) {
654 continue;
655 }
656
657 const String8& featureName = grp.features.keyAt(i);
658 printf(" uses-feature: name='%s'\n",
659 ResTable::normalizeForOutput(featureName.string()).string());
660 }
661
662 const size_t numImpliedFeatures =
663 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
664 for (size_t i = 0; i < numImpliedFeatures; i++) {
665 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
666 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
667 // The feature is explicitly set, no need to use implied
668 // definition.
669 continue;
670 }
671
672 String8 printableFeatureName(ResTable::normalizeForOutput(
673 impliedFeature.name.string()));
674 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
675 printf(" uses-implied-feature: name='%s' reason='",
676 printableFeatureName.string());
677 const size_t numReasons = impliedFeature.reasons.size();
678 for (size_t j = 0; j < numReasons; j++) {
679 printf("%s", impliedFeature.reasons[j].string());
680 if (j + 2 < numReasons) {
681 printf(", ");
682 } else if (j + 1 < numReasons) {
683 printf(", and ");
684 }
685 }
686 printf("'\n");
687 }
688}
689
690static void addParentFeatures(FeatureGroup* grp, const String8& name) {
691 if (name == "android.hardware.camera.autofocus" ||
692 name == "android.hardware.camera.flash") {
693 grp->features.add(String8("android.hardware.camera"), true);
694 } else if (name == "android.hardware.location.gps" ||
695 name == "android.hardware.location.network") {
696 grp->features.add(String8("android.hardware.location"), true);
697 } else if (name == "android.hardware.touchscreen.multitouch") {
698 grp->features.add(String8("android.hardware.touchscreen"), true);
699 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
700 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
701 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700702 } else if (name == "android.hardware.opengles.aep") {
703 const int openGLESVersion31 = 0x00030001;
704 if (openGLESVersion31 > grp->openGLESVersion) {
705 grp->openGLESVersion = openGLESVersion31;
706 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700707 }
708}
709
Adam Lesinski282e1812014-01-23 18:17:42 -0800710/*
711 * Handle the "dump" command, to extract select data from an archive.
712 */
713extern char CONSOLE_DATA[2925]; // see EOF
714int doDump(Bundle* bundle)
715{
716 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800717
718 if (bundle->getFileSpecCount() < 1) {
719 fprintf(stderr, "ERROR: no dump option specified\n");
720 return 1;
721 }
722
723 if (bundle->getFileSpecCount() < 2) {
724 fprintf(stderr, "ERROR: no dump file specified\n");
725 return 1;
726 }
727
728 const char* option = bundle->getFileSpecEntry(0);
729 const char* filename = bundle->getFileSpecEntry(1);
730
731 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000732 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800733 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
734 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
735 return 1;
736 }
737
738 // Make a dummy config for retrieving resources... we need to supply
739 // non-default values for some configs so that we can retrieve resources
740 // in the app that don't have a default. The most important of these is
741 // the API version because key resources like icons will have an implicit
742 // version if they are using newer config types like density.
743 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000744 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800745 config.language[0] = 'e';
746 config.language[1] = 'n';
747 config.country[0] = 'U';
748 config.country[1] = 'S';
749 config.orientation = ResTable_config::ORIENTATION_PORT;
750 config.density = ResTable_config::DENSITY_MEDIUM;
751 config.sdkVersion = 10000; // Very high.
752 config.screenWidthDp = 320;
753 config.screenHeightDp = 480;
754 config.smallestScreenWidthDp = 320;
755 assets.setConfiguration(config);
756
757 const ResTable& res = assets.getResources(false);
758 if (&res == NULL) {
759 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700760 return 1;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700761 } else if (res.getError() != NO_ERROR) {
762 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700763 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800764 }
765
Adam Lesinski63e646e2014-07-30 11:40:39 -0700766 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
767 if (dynamicRefTable == NULL) {
768 fprintf(stderr, "ERROR: failed to find dynamic reference table for asset cookie %d\n",
769 assetsCookie);
770 return 1;
771 }
772
773 Asset* asset = NULL;
774
Adam Lesinski282e1812014-01-23 18:17:42 -0800775 if (strcmp("resources", option) == 0) {
776#ifndef HAVE_ANDROID_OS
777 res.print(bundle->getValues());
778#endif
779
780 } else if (strcmp("strings", option) == 0) {
781 const ResStringPool* pool = res.getTableStringBlock(0);
782 printStringPool(pool);
783
784 } else if (strcmp("xmltree", option) == 0) {
785 if (bundle->getFileSpecCount() < 3) {
786 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
787 goto bail;
788 }
789
790 for (int i=2; i<bundle->getFileSpecCount(); i++) {
791 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700792 ResXMLTree tree(dynamicRefTable);
793 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800794 if (asset == NULL) {
795 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
796 goto bail;
797 }
798
799 if (tree.setTo(asset->getBuffer(true),
800 asset->getLength()) != NO_ERROR) {
801 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
802 goto bail;
803 }
804 tree.restart();
805 printXMLBlock(&tree);
806 tree.uninit();
807 delete asset;
808 asset = NULL;
809 }
810
811 } else if (strcmp("xmlstrings", option) == 0) {
812 if (bundle->getFileSpecCount() < 3) {
813 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
814 goto bail;
815 }
816
817 for (int i=2; i<bundle->getFileSpecCount(); i++) {
818 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700819 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800820 if (asset == NULL) {
821 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
822 goto bail;
823 }
824
Adam Lesinski63e646e2014-07-30 11:40:39 -0700825 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800826 if (tree.setTo(asset->getBuffer(true),
827 asset->getLength()) != NO_ERROR) {
828 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
829 goto bail;
830 }
831 printStringPool(&tree.getStrings());
832 delete asset;
833 asset = NULL;
834 }
835
836 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700837 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800838 if (asset == NULL) {
839 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
840 goto bail;
841 }
842
Adam Lesinski63e646e2014-07-30 11:40:39 -0700843 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800844 if (tree.setTo(asset->getBuffer(true),
845 asset->getLength()) != NO_ERROR) {
846 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
847 goto bail;
848 }
849 tree.restart();
850
851 if (strcmp("permissions", option) == 0) {
852 size_t len;
853 ResXMLTree::event_code_t code;
854 int depth = 0;
855 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
856 if (code == ResXMLTree::END_TAG) {
857 depth--;
858 continue;
859 }
860 if (code != ResXMLTree::START_TAG) {
861 continue;
862 }
863 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700864 const char16_t* ctag16 = tree.getElementName(&len);
865 if (ctag16 == NULL) {
866 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
867 goto bail;
868 }
869 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800870 //printf("Depth %d tag %s\n", depth, tag.string());
871 if (depth == 1) {
872 if (tag != "manifest") {
873 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
874 goto bail;
875 }
876 String8 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700877 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800878 } else if (depth == 2 && tag == "permission") {
879 String8 error;
880 String8 name = getAttribute(tree, NAME_ATTR, &error);
881 if (error != "") {
882 fprintf(stderr, "ERROR: %s\n", error.string());
883 goto bail;
884 }
Maurice Chu2675f762013-10-22 17:33:11 -0700885 printf("permission: %s\n",
886 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800887 } else if (depth == 2 && tag == "uses-permission") {
888 String8 error;
889 String8 name = getAttribute(tree, NAME_ATTR, &error);
890 if (error != "") {
891 fprintf(stderr, "ERROR: %s\n", error.string());
892 goto bail;
893 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800894 printUsesPermission(name,
895 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
896 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
Adam Lesinski282e1812014-01-23 18:17:42 -0800897 }
898 }
899 } else if (strcmp("badging", option) == 0) {
900 Vector<String8> locales;
901 res.getLocales(&locales);
902
903 Vector<ResTable_config> configs;
904 res.getConfigurations(&configs);
905 SortedVector<int> densities;
906 const size_t NC = configs.size();
907 for (size_t i=0; i<NC; i++) {
908 int dens = configs[i].density;
909 if (dens == 0) {
910 dens = 160;
911 }
912 densities.add(dens);
913 }
914
915 size_t len;
916 ResXMLTree::event_code_t code;
917 int depth = 0;
918 String8 error;
919 bool withinActivity = false;
920 bool isMainActivity = false;
921 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800922 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800923 bool isSearchable = false;
924 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700925 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700926 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800927 bool withinReceiver = false;
928 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700929 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800930 bool withinIntentFilter = false;
931 bool hasMainActivity = false;
932 bool hasOtherActivities = false;
933 bool hasOtherReceivers = false;
934 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700935 bool hasIntentFilter = false;
936
Adam Lesinski282e1812014-01-23 18:17:42 -0800937 bool hasWallpaperService = false;
938 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700939 bool hasAccessibilityService = false;
940 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800941 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700942 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700943 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700944 bool hasDocumentsProvider = false;
945 bool hasCameraActivity = false;
946 bool hasCameraSecureActivity = false;
947 bool hasLauncher = false;
948 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400949 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700950
Adam Lesinski282e1812014-01-23 18:17:42 -0800951 bool actMainActivity = false;
952 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700953 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800954 bool actImeService = false;
955 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700956 bool actAccessibilityService = false;
957 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700958 bool actHostApduService = false;
959 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700960 bool actDocumentsProvider = false;
961 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400962 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700963 bool actCamera = false;
964 bool actCameraSecure = false;
965 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700966 bool hasMetaHostPaymentCategory = false;
967 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700968
969 // These permissions are required by services implementing services
970 // the system binds to (IME, Accessibility, PrintServices, etc.)
971 bool hasBindDeviceAdminPermission = false;
972 bool hasBindInputMethodPermission = false;
973 bool hasBindAccessibilityServicePermission = false;
974 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700975 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700976 bool hasRequiredSafAttributes = false;
977 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400978 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800979
980 // These two implement the implicit permissions that are granted
981 // to pre-1.6 applications.
982 bool hasWriteExternalStoragePermission = false;
983 bool hasReadPhoneStatePermission = false;
984
985 // If an app requests write storage, they will also get read storage.
986 bool hasReadExternalStoragePermission = false;
987
988 // Implement transition to read and write call log.
989 bool hasReadContactsPermission = false;
990 bool hasWriteContactsPermission = false;
991 bool hasReadCallLogPermission = false;
992 bool hasWriteCallLogPermission = false;
993
994 // This next group of variables is used to implement a group of
995 // backward-compatibility heuristics necessitated by the addition of
996 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
997 // heuristic is "if an app requests a permission but doesn't explicitly
998 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -0700999
Adam Lesinski282e1812014-01-23 18:17:42 -08001000 // 2.2 also added some other features that apps can request, but that
1001 // have no corresponding permission, so we cannot implement any
1002 // back-compatibility heuristic for them. The below are thus unnecessary
1003 // (but are retained here for documentary purposes.)
1004 //bool specCompassFeature = false;
1005 //bool specAccelerometerFeature = false;
1006 //bool specProximityFeature = false;
1007 //bool specAmbientLightFeature = false;
1008 //bool specLiveWallpaperFeature = false;
1009
1010 int targetSdk = 0;
1011 int smallScreen = 1;
1012 int normalScreen = 1;
1013 int largeScreen = 1;
1014 int xlargeScreen = 1;
1015 int anyDensity = 1;
1016 int requiresSmallestWidthDp = 0;
1017 int compatibleWidthLimitDp = 0;
1018 int largestWidthLimitDp = 0;
1019 String8 pkg;
1020 String8 activityName;
1021 String8 activityLabel;
1022 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001023 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001024 String8 receiverName;
1025 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001026 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001027
1028 FeatureGroup commonFeatures;
1029 Vector<FeatureGroup> featureGroups;
1030 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1031
Adam Lesinski282e1812014-01-23 18:17:42 -08001032 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1033 if (code == ResXMLTree::END_TAG) {
1034 depth--;
1035 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001036 if (withinSupportsInput && !supportedInput.isEmpty()) {
1037 printf("supports-input: '");
1038 const size_t N = supportedInput.size();
1039 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001040 printf("%s", ResTable::normalizeForOutput(
1041 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001042 if (i != N - 1) {
1043 printf("' '");
1044 } else {
1045 printf("'\n");
1046 }
1047 }
1048 supportedInput.clear();
1049 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001050 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001051 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001052 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001053 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001054 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001055 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001056 if (isLauncherActivity) {
1057 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001058 if (aName.length() > 0) {
1059 printf(" name='%s' ",
1060 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001061 }
1062 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001063 ResTable::normalizeForOutput(activityLabel.string()).string(),
1064 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001065 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001066 if (isLeanbackLauncherActivity) {
1067 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001068 if (aName.length() > 0) {
1069 printf(" name='%s' ",
1070 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001071 }
1072 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001073 ResTable::normalizeForOutput(activityLabel.string()).string(),
1074 ResTable::normalizeForOutput(activityIcon.string()).string(),
1075 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001076 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001077 }
1078 if (!hasIntentFilter) {
1079 hasOtherActivities |= withinActivity;
1080 hasOtherReceivers |= withinReceiver;
1081 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001082 } else {
1083 if (withinService) {
1084 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1085 hasBindNfcServicePermission);
1086 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1087 hasBindNfcServicePermission);
1088 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001089 }
1090 withinActivity = false;
1091 withinService = false;
1092 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001093 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001094 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001095 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001096 } else if (depth < 4) {
1097 if (withinIntentFilter) {
1098 if (withinActivity) {
1099 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001100 hasLauncher |= catLauncher;
1101 hasCameraActivity |= actCamera;
1102 hasCameraSecureActivity |= actCameraSecure;
1103 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001104 } else if (withinReceiver) {
1105 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001106 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1107 hasBindDeviceAdminPermission);
1108 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001109 } else if (withinService) {
1110 hasImeService |= actImeService;
1111 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001112 hasAccessibilityService |= (actAccessibilityService &&
1113 hasBindAccessibilityServicePermission);
1114 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001115 hasNotificationListenerService |= actNotificationListenerService &&
1116 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001117 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001118 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001119 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001120 !actHostApduService && !actOffHostApduService &&
1121 !actNotificationListenerService);
1122 } else if (withinProvider) {
1123 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001124 }
1125 }
1126 withinIntentFilter = false;
1127 }
1128 continue;
1129 }
1130 if (code != ResXMLTree::START_TAG) {
1131 continue;
1132 }
1133 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001134
1135 const char16_t* ctag16 = tree.getElementName(&len);
1136 if (ctag16 == NULL) {
1137 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1138 goto bail;
1139 }
1140 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001141 //printf("Depth %d, %s\n", depth, tag.string());
1142 if (depth == 1) {
1143 if (tag != "manifest") {
1144 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1145 goto bail;
1146 }
1147 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001148 printf("package: name='%s' ",
1149 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001150 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1151 if (error != "") {
1152 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1153 goto bail;
1154 }
1155 if (versionCode > 0) {
1156 printf("versionCode='%d' ", versionCode);
1157 } else {
1158 printf("versionCode='' ");
1159 }
1160 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1161 if (error != "") {
1162 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1163 goto bail;
1164 }
Maurice Chu2675f762013-10-22 17:33:11 -07001165 printf("versionName='%s'\n",
1166 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001167 } else if (depth == 2) {
1168 withinApplication = false;
1169 if (tag == "application") {
1170 withinApplication = true;
1171
1172 String8 label;
1173 const size_t NL = locales.size();
1174 for (size_t i=0; i<NL; i++) {
1175 const char* localeStr = locales[i].string();
1176 assets.setLocale(localeStr != NULL ? localeStr : "");
1177 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1178 if (llabel != "") {
1179 if (localeStr == NULL || strlen(localeStr) == 0) {
1180 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001181 printf("application-label:'%s'\n",
1182 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001183 } else {
1184 if (label == "") {
1185 label = llabel;
1186 }
1187 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001188 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001189 }
1190 }
1191 }
1192
1193 ResTable_config tmpConfig = config;
1194 const size_t ND = densities.size();
1195 for (size_t i=0; i<ND; i++) {
1196 tmpConfig.density = densities[i];
1197 assets.setConfiguration(tmpConfig);
1198 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1199 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001200 printf("application-icon-%d:'%s'\n", densities[i],
1201 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001202 }
1203 }
1204 assets.setConfiguration(config);
1205
1206 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1207 if (error != "") {
1208 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1209 goto bail;
1210 }
1211 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1212 if (error != "") {
1213 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1214 goto bail;
1215 }
Maurice Chu2675f762013-10-22 17:33:11 -07001216 printf("application: label='%s' ",
1217 ResTable::normalizeForOutput(label.string()).string());
1218 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001219 if (testOnly != 0) {
1220 printf("testOnly='%d'\n", testOnly);
1221 }
1222
1223 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1224 if (error != "") {
1225 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1226 goto bail;
1227 }
1228 if (debuggable != 0) {
1229 printf("application-debuggable\n");
1230 }
1231 } else if (tag == "uses-sdk") {
1232 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1233 if (error != "") {
1234 error = "";
1235 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1236 if (error != "") {
1237 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1238 error.string());
1239 goto bail;
1240 }
1241 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001242 printf("sdkVersion:'%s'\n",
1243 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001244 } else if (code != -1) {
1245 targetSdk = code;
1246 printf("sdkVersion:'%d'\n", code);
1247 }
1248 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1249 if (code != -1) {
1250 printf("maxSdkVersion:'%d'\n", code);
1251 }
1252 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1253 if (error != "") {
1254 error = "";
1255 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1256 if (error != "") {
1257 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1258 error.string());
1259 goto bail;
1260 }
1261 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001262 printf("targetSdkVersion:'%s'\n",
1263 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001264 } else if (code != -1) {
1265 if (targetSdk < code) {
1266 targetSdk = code;
1267 }
1268 printf("targetSdkVersion:'%d'\n", code);
1269 }
1270 } else if (tag == "uses-configuration") {
1271 int32_t reqTouchScreen = getIntegerAttribute(tree,
1272 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1273 int32_t reqKeyboardType = getIntegerAttribute(tree,
1274 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1275 int32_t reqHardKeyboard = getIntegerAttribute(tree,
1276 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1277 int32_t reqNavigation = getIntegerAttribute(tree,
1278 REQ_NAVIGATION_ATTR, NULL, 0);
1279 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1280 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1281 printf("uses-configuration:");
1282 if (reqTouchScreen != 0) {
1283 printf(" reqTouchScreen='%d'", reqTouchScreen);
1284 }
1285 if (reqKeyboardType != 0) {
1286 printf(" reqKeyboardType='%d'", reqKeyboardType);
1287 }
1288 if (reqHardKeyboard != 0) {
1289 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1290 }
1291 if (reqNavigation != 0) {
1292 printf(" reqNavigation='%d'", reqNavigation);
1293 }
1294 if (reqFiveWayNav != 0) {
1295 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1296 }
1297 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001298 } else if (tag == "supports-input") {
1299 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001300 } else if (tag == "supports-screens") {
1301 smallScreen = getIntegerAttribute(tree,
1302 SMALL_SCREEN_ATTR, NULL, 1);
1303 normalScreen = getIntegerAttribute(tree,
1304 NORMAL_SCREEN_ATTR, NULL, 1);
1305 largeScreen = getIntegerAttribute(tree,
1306 LARGE_SCREEN_ATTR, NULL, 1);
1307 xlargeScreen = getIntegerAttribute(tree,
1308 XLARGE_SCREEN_ATTR, NULL, 1);
1309 anyDensity = getIntegerAttribute(tree,
1310 ANY_DENSITY_ATTR, NULL, 1);
1311 requiresSmallestWidthDp = getIntegerAttribute(tree,
1312 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1313 compatibleWidthLimitDp = getIntegerAttribute(tree,
1314 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1315 largestWidthLimitDp = getIntegerAttribute(tree,
1316 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001317 } else if (tag == "feature-group") {
1318 withinFeatureGroup = true;
1319 FeatureGroup group;
1320 group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1321 if (error != "") {
1322 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1323 " %s\n", error.string());
1324 goto bail;
1325 }
1326 featureGroups.add(group);
1327
Adam Lesinski282e1812014-01-23 18:17:42 -08001328 } else if (tag == "uses-feature") {
1329 String8 name = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001330 if (name != "" && error == "") {
1331 int req = getIntegerAttribute(tree,
1332 REQUIRED_ATTR, NULL, 1);
1333
Adam Lesinski2c72b682014-06-24 09:56:01 -07001334 commonFeatures.features.add(name, req);
1335 if (req) {
1336 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001337 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001338 } else {
1339 int vers = getIntegerAttribute(tree,
1340 GL_ES_VERSION_ATTR, &error);
1341 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001342 if (vers > commonFeatures.openGLESVersion) {
1343 commonFeatures.openGLESVersion = vers;
1344 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001345 }
1346 }
1347 } else if (tag == "uses-permission") {
1348 String8 name = getAttribute(tree, NAME_ATTR, &error);
1349 if (name != "" && error == "") {
1350 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001351 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001352 String8::format("requested %s permission", name.string())
1353 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001354 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001355 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1356 String8::format("requested %s permission", name.string())
1357 .string());
1358 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1359 String8::format("requested %s permission", name.string())
1360 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001361 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001362 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1363 String8::format("requested %s permission", name.string())
1364 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001365 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001366 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1367 String8::format("requested %s permission", name.string())
1368 .string());
1369 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1370 String8::format("requested %s permission", name.string())
1371 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001372 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1373 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001374 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1375 String8::format("requested %s permission", name.string())
1376 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001377 } else if (name == "android.permission.BLUETOOTH" ||
1378 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001379 if (targetSdk > 4) {
1380 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1381 String8::format("requested %s permission", name.string())
1382 .string());
1383 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1384 "targetSdkVersion > 4");
1385 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001386 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001387 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1388 String8::format("requested %s permission", name.string())
1389 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001390 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1391 name == "android.permission.CHANGE_WIFI_STATE" ||
1392 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001393 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1394 String8::format("requested %s permission", name.string())
1395 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001396 } else if (name == "android.permission.CALL_PHONE" ||
1397 name == "android.permission.CALL_PRIVILEGED" ||
1398 name == "android.permission.MODIFY_PHONE_STATE" ||
1399 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1400 name == "android.permission.READ_SMS" ||
1401 name == "android.permission.RECEIVE_SMS" ||
1402 name == "android.permission.RECEIVE_MMS" ||
1403 name == "android.permission.RECEIVE_WAP_PUSH" ||
1404 name == "android.permission.SEND_SMS" ||
1405 name == "android.permission.WRITE_APN_SETTINGS" ||
1406 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001407 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1408 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001409 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1410 hasWriteExternalStoragePermission = true;
1411 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1412 hasReadExternalStoragePermission = true;
1413 } else if (name == "android.permission.READ_PHONE_STATE") {
1414 hasReadPhoneStatePermission = true;
1415 } else if (name == "android.permission.READ_CONTACTS") {
1416 hasReadContactsPermission = true;
1417 } else if (name == "android.permission.WRITE_CONTACTS") {
1418 hasWriteContactsPermission = true;
1419 } else if (name == "android.permission.READ_CALL_LOG") {
1420 hasReadCallLogPermission = true;
1421 } else if (name == "android.permission.WRITE_CALL_LOG") {
1422 hasWriteCallLogPermission = true;
1423 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001424
1425 printUsesPermission(name,
1426 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1427 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1428 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001429 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1430 error.string());
1431 goto bail;
1432 }
1433 } else if (tag == "uses-package") {
1434 String8 name = getAttribute(tree, NAME_ATTR, &error);
1435 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001436 printf("uses-package:'%s'\n",
1437 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001438 } else {
1439 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1440 error.string());
1441 goto bail;
1442 }
1443 } else if (tag == "original-package") {
1444 String8 name = getAttribute(tree, NAME_ATTR, &error);
1445 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001446 printf("original-package:'%s'\n",
1447 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001448 } else {
1449 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1450 error.string());
1451 goto bail;
1452 }
1453 } else if (tag == "supports-gl-texture") {
1454 String8 name = getAttribute(tree, NAME_ATTR, &error);
1455 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001456 printf("supports-gl-texture:'%s'\n",
1457 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001458 } else {
1459 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1460 error.string());
1461 goto bail;
1462 }
1463 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001464 printCompatibleScreens(tree, &error);
1465 if (error != "") {
1466 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1467 error.string());
1468 goto bail;
1469 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001470 depth--;
1471 } else if (tag == "package-verifier") {
1472 String8 name = getAttribute(tree, NAME_ATTR, &error);
1473 if (name != "" && error == "") {
1474 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1475 if (publicKey != "" && error == "") {
1476 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001477 ResTable::normalizeForOutput(name.string()).string(),
1478 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001479 }
1480 }
1481 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001482 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001483 withinActivity = false;
1484 withinReceiver = false;
1485 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001486 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001487 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001488 hasMetaHostPaymentCategory = false;
1489 hasMetaOffHostPaymentCategory = false;
1490 hasBindDeviceAdminPermission = false;
1491 hasBindInputMethodPermission = false;
1492 hasBindAccessibilityServicePermission = false;
1493 hasBindPrintServicePermission = false;
1494 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001495 hasRequiredSafAttributes = false;
1496 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001497 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001498 if (withinApplication) {
1499 if(tag == "activity") {
1500 withinActivity = true;
1501 activityName = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001502 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001503 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1504 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001505 goto bail;
1506 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001507
Michael Wrightec4fdec2013-09-06 16:50:52 -07001508 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1509 if (error != "") {
1510 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1511 error.string());
1512 goto bail;
1513 }
1514
1515 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1516 if (error != "") {
1517 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1518 error.string());
1519 goto bail;
1520 }
1521
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001522 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1523 if (error != "") {
1524 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1525 error.string());
1526 goto bail;
1527 }
1528
Michael Wrightec4fdec2013-09-06 16:50:52 -07001529 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1530 SCREEN_ORIENTATION_ATTR, &error);
1531 if (error == "") {
1532 if (orien == 0 || orien == 6 || orien == 8) {
1533 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001534 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1535 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001536 } else if (orien == 1 || orien == 7 || orien == 9) {
1537 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001538 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1539 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001540 }
1541 }
1542 } else if (tag == "uses-library") {
1543 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1544 if (error != "") {
1545 fprintf(stderr,
1546 "ERROR getting 'android:name' attribute for uses-library"
1547 " %s\n", error.string());
1548 goto bail;
1549 }
1550 int req = getIntegerAttribute(tree,
1551 REQUIRED_ATTR, NULL, 1);
1552 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001553 req ? "" : "-not-required", ResTable::normalizeForOutput(
1554 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001555 } else if (tag == "receiver") {
1556 withinReceiver = true;
1557 receiverName = getAttribute(tree, NAME_ATTR, &error);
1558
1559 if (error != "") {
1560 fprintf(stderr,
1561 "ERROR getting 'android:name' attribute for receiver:"
1562 " %s\n", error.string());
1563 goto bail;
1564 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001565
1566 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1567 if (error == "") {
1568 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1569 hasBindDeviceAdminPermission = true;
1570 }
1571 } else {
1572 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1573 " receiver '%s': %s\n", receiverName.string(), error.string());
1574 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001575 } else if (tag == "service") {
1576 withinService = true;
1577 serviceName = getAttribute(tree, NAME_ATTR, &error);
1578
1579 if (error != "") {
1580 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1581 "service:%s\n", error.string());
1582 goto bail;
1583 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001584
1585 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1586 if (error == "") {
1587 if (permission == "android.permission.BIND_INPUT_METHOD") {
1588 hasBindInputMethodPermission = true;
1589 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1590 hasBindAccessibilityServicePermission = true;
1591 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1592 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001593 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1594 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001595 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1596 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001597 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1598 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001599 }
1600 } else {
1601 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1602 " service '%s': %s\n", serviceName.string(), error.string());
1603 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001604 } else if (tag == "provider") {
1605 withinProvider = true;
1606
1607 bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
1608 if (error != "") {
1609 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1610 " %s\n", error.string());
1611 goto bail;
1612 }
1613
1614 bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
1615 GRANT_URI_PERMISSIONS_ATTR, &error);
1616 if (error != "") {
1617 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1618 " %s\n", error.string());
1619 goto bail;
1620 }
1621
1622 String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
1623 if (error != "") {
1624 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1625 " %s\n", error.string());
1626 goto bail;
1627 }
1628
1629 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1630 permission == "android.permission.MANAGE_DOCUMENTS";
1631
Michael Wrightec4fdec2013-09-06 16:50:52 -07001632 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskib71adb62014-05-15 14:14:41 -07001633 String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001634 if (error != "") {
1635 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1636 "meta-data:%s\n", error.string());
1637 goto bail;
1638 }
Maurice Chu2675f762013-10-22 17:33:11 -07001639 printf("meta-data: name='%s' ",
1640 ResTable::normalizeForOutput(metaDataName.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -07001641 printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1642 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001643 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001644 // Try looking for a RESOURCE_ATTR
1645 error = "";
1646 printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1647 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001648 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001649 fprintf(stderr, "ERROR getting 'android:value' or "
1650 "'android:resource' attribute for "
1651 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001652 goto bail;
1653 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001654 }
Maurice Chu76327312013-10-16 18:28:46 -07001655 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001656 } else if (withinSupportsInput && tag == "input-type") {
1657 String8 name = getAttribute(tree, NAME_ATTR, &error);
1658 if (name != "" && error == "") {
1659 supportedInput.add(name);
1660 } else {
1661 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1662 error.string());
1663 goto bail;
1664 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001665 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001666 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001667 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001668
1669 String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1670 if (name != "" && error == "") {
1671 int required = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1672 top.features.add(name, required);
1673 if (required) {
1674 addParentFeatures(&top, name);
1675 }
1676
1677 } else {
1678 int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
1679 if (error == "") {
1680 if (vers > top.openGLESVersion) {
1681 top.openGLESVersion = vers;
1682 }
1683 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001684 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001685 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001686 } else if (depth == 4) {
1687 if (tag == "intent-filter") {
1688 hasIntentFilter = true;
1689 withinIntentFilter = true;
1690 actMainActivity = false;
1691 actWidgetReceivers = false;
1692 actImeService = false;
1693 actWallpaperService = false;
1694 actAccessibilityService = false;
1695 actPrintService = false;
1696 actDeviceAdminEnabled = false;
1697 actHostApduService = false;
1698 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001699 actDocumentsProvider = false;
1700 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001701 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001702 actCamera = false;
1703 actCameraSecure = false;
1704 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001705 } else if (withinService && tag == "meta-data") {
1706 String8 name = getAttribute(tree, NAME_ATTR, &error);
1707 if (error != "") {
1708 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1709 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1710 goto bail;
1711 }
1712
1713 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1714 name == "android.nfc.cardemulation.off_host_apdu_service") {
1715 bool offHost = true;
1716 if (name == "android.nfc.cardemulation.host_apdu_service") {
1717 offHost = false;
1718 }
1719
1720 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1721 if (error != "") {
1722 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1723 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1724 goto bail;
1725 }
1726
1727 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1728 offHost, &error);
1729 if (error != "") {
1730 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1731 serviceName.string());
1732 goto bail;
1733 }
1734
1735 const size_t catLen = categories.size();
1736 for (size_t i = 0; i < catLen; i++) {
1737 bool paymentCategory = (categories[i] == "payment");
1738 if (offHost) {
1739 hasMetaOffHostPaymentCategory |= paymentCategory;
1740 } else {
1741 hasMetaHostPaymentCategory |= paymentCategory;
1742 }
1743 }
1744 }
1745 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001746 } else if ((depth == 5) && withinIntentFilter) {
1747 String8 action;
1748 if (tag == "action") {
1749 action = getAttribute(tree, NAME_ATTR, &error);
1750 if (error != "") {
1751 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1752 error.string());
1753 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001754 }
1755
Adam Lesinskia5018c92013-09-30 16:23:15 -07001756 if (withinActivity) {
1757 if (action == "android.intent.action.MAIN") {
1758 isMainActivity = true;
1759 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001760 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1761 action == "android.media.action.VIDEO_CAMERA") {
1762 actCamera = true;
1763 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1764 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001765 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001766 } else if (withinReceiver) {
1767 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1768 actWidgetReceivers = true;
1769 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1770 actDeviceAdminEnabled = true;
1771 }
1772 } else if (withinService) {
1773 if (action == "android.view.InputMethod") {
1774 actImeService = true;
1775 } else if (action == "android.service.wallpaper.WallpaperService") {
1776 actWallpaperService = true;
1777 } else if (action == "android.accessibilityservice.AccessibilityService") {
1778 actAccessibilityService = true;
1779 } else if (action == "android.printservice.PrintService") {
1780 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001781 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1782 actHostApduService = true;
1783 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1784 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001785 } else if (action == "android.service.notification.NotificationListenerService") {
1786 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001787 } else if (action == "android.service.dreams.DreamService") {
1788 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001789 }
1790 } else if (withinProvider) {
1791 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1792 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001793 }
1794 }
1795 if (action == "android.intent.action.SEARCH") {
1796 isSearchable = true;
1797 }
1798 }
1799
1800 if (tag == "category") {
1801 String8 category = getAttribute(tree, NAME_ATTR, &error);
1802 if (error != "") {
1803 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1804 error.string());
1805 goto bail;
1806 }
1807 if (withinActivity) {
1808 if (category == "android.intent.category.LAUNCHER") {
1809 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001810 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1811 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001812 } else if (category == "android.intent.category.HOME") {
1813 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001814 }
1815 }
1816 }
1817 }
1818 }
1819
1820 // Pre-1.6 implicitly granted permission compatibility logic
1821 if (targetSdk < 4) {
1822 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001823 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1824 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1825 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001826 hasWriteExternalStoragePermission = true;
1827 }
1828 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001829 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1830 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1831 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001832 }
1833 }
1834
1835 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1836 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1837 // do this (regardless of target API version) because we can't have
1838 // an app with write permission but not read permission.
1839 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001840 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1841 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1842 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001843 }
1844
1845 // Pre-JellyBean call log permission compatibility.
1846 if (targetSdk < 16) {
1847 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001848 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1849 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1850 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001851 }
1852 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001853 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1854 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1855 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001856 }
1857 }
1858
Adam Lesinski2c72b682014-06-24 09:56:01 -07001859 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1860 "default feature for all apps");
1861
1862 const size_t numFeatureGroups = featureGroups.size();
1863 if (numFeatureGroups == 0) {
1864 // If no <feature-group> tags were defined, apply auto-implied features.
1865 printFeatureGroup(commonFeatures, &impliedFeatures);
1866
1867 } else {
1868 // <feature-group> tags are defined, so we ignore implied features and
1869 for (size_t i = 0; i < numFeatureGroups; i++) {
1870 FeatureGroup& grp = featureGroups.editItemAt(i);
1871
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001872 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1873 grp.openGLESVersion = commonFeatures.openGLESVersion;
1874 }
1875
Adam Lesinski2c72b682014-06-24 09:56:01 -07001876 // Merge the features defined in the top level (not inside a <feature-group>)
1877 // with this feature group.
1878 const size_t numCommonFeatures = commonFeatures.features.size();
1879 for (size_t j = 0; j < numCommonFeatures; j++) {
1880 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001881 grp.features.add(commonFeatures.features.keyAt(j),
1882 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001883 }
1884 }
1885
1886 if (!grp.features.isEmpty()) {
1887 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001888 }
1889 }
1890 }
1891
Adam Lesinski282e1812014-01-23 18:17:42 -08001892
Adam Lesinski282e1812014-01-23 18:17:42 -08001893 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001894 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001895 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001896 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001897 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001898 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001899 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001900 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001901 }
1902 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001903 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001904 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001905 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001906 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001907 }
1908 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001909 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001910 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001911 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001912 printComponentPresence("payment");
1913 }
1914 if (isSearchable) {
1915 printComponentPresence("search");
1916 }
1917 if (hasDocumentsProvider) {
1918 printComponentPresence("document-provider");
1919 }
1920 if (hasLauncher) {
1921 printComponentPresence("launcher");
1922 }
1923 if (hasNotificationListenerService) {
1924 printComponentPresence("notification-listener");
1925 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001926 if (hasDreamService) {
1927 printComponentPresence("dream");
1928 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001929 if (hasCameraActivity) {
1930 printComponentPresence("camera");
1931 }
1932 if (hasCameraSecureActivity) {
1933 printComponentPresence("camera-secure");
1934 }
1935
1936 if (hasMainActivity) {
1937 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001938 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001939 if (hasOtherActivities) {
1940 printf("other-activities\n");
1941 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001942 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001943 printf("other-receivers\n");
1944 }
1945 if (hasOtherServices) {
1946 printf("other-services\n");
1947 }
1948
1949 // For modern apps, if screen size buckets haven't been specified
1950 // but the new width ranges have, then infer the buckets from them.
1951 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1952 && requiresSmallestWidthDp > 0) {
1953 int compatWidth = compatibleWidthLimitDp;
1954 if (compatWidth <= 0) {
1955 compatWidth = requiresSmallestWidthDp;
1956 }
1957 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1958 smallScreen = -1;
1959 } else {
1960 smallScreen = 0;
1961 }
1962 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1963 normalScreen = -1;
1964 } else {
1965 normalScreen = 0;
1966 }
1967 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1968 largeScreen = -1;
1969 } else {
1970 largeScreen = 0;
1971 }
1972 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1973 xlargeScreen = -1;
1974 } else {
1975 xlargeScreen = 0;
1976 }
1977 }
1978
1979 // Determine default values for any unspecified screen sizes,
1980 // based on the target SDK of the package. As of 4 (donut)
1981 // the screen size support was introduced, so all default to
1982 // enabled.
1983 if (smallScreen > 0) {
1984 smallScreen = targetSdk >= 4 ? -1 : 0;
1985 }
1986 if (normalScreen > 0) {
1987 normalScreen = -1;
1988 }
1989 if (largeScreen > 0) {
1990 largeScreen = targetSdk >= 4 ? -1 : 0;
1991 }
1992 if (xlargeScreen > 0) {
1993 // Introduced in Gingerbread.
1994 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1995 }
1996 if (anyDensity > 0) {
1997 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1998 || compatibleWidthLimitDp > 0) ? -1 : 0;
1999 }
2000 printf("supports-screens:");
2001 if (smallScreen != 0) {
2002 printf(" 'small'");
2003 }
2004 if (normalScreen != 0) {
2005 printf(" 'normal'");
2006 }
2007 if (largeScreen != 0) {
2008 printf(" 'large'");
2009 }
2010 if (xlargeScreen != 0) {
2011 printf(" 'xlarge'");
2012 }
2013 printf("\n");
2014 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2015 if (requiresSmallestWidthDp > 0) {
2016 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2017 }
2018 if (compatibleWidthLimitDp > 0) {
2019 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2020 }
2021 if (largestWidthLimitDp > 0) {
2022 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2023 }
2024
2025 printf("locales:");
2026 const size_t NL = locales.size();
2027 for (size_t i=0; i<NL; i++) {
2028 const char* localeStr = locales[i].string();
2029 if (localeStr == NULL || strlen(localeStr) == 0) {
2030 localeStr = "--_--";
2031 }
2032 printf(" '%s'", localeStr);
2033 }
2034 printf("\n");
2035
2036 printf("densities:");
2037 const size_t ND = densities.size();
2038 for (size_t i=0; i<ND; i++) {
2039 printf(" '%d'", densities[i]);
2040 }
2041 printf("\n");
2042
2043 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2044 if (dir != NULL) {
2045 if (dir->getFileCount() > 0) {
2046 printf("native-code:");
2047 for (size_t i=0; i<dir->getFileCount(); i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07002048 printf(" '%s'", ResTable::normalizeForOutput(
2049 dir->getFileName(i).string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002050 }
2051 printf("\n");
2052 }
2053 delete dir;
2054 }
2055 } else if (strcmp("badger", option) == 0) {
2056 printf("%s", CONSOLE_DATA);
2057 } else if (strcmp("configurations", option) == 0) {
2058 Vector<ResTable_config> configs;
2059 res.getConfigurations(&configs);
2060 const size_t N = configs.size();
2061 for (size_t i=0; i<N; i++) {
2062 printf("%s\n", configs[i].toString().string());
2063 }
2064 } else {
2065 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2066 goto bail;
2067 }
2068 }
2069
2070 result = NO_ERROR;
2071
2072bail:
2073 if (asset) {
2074 delete asset;
2075 }
2076 return (result != NO_ERROR);
2077}
2078
2079
2080/*
2081 * Handle the "add" command, which wants to add files to a new or
2082 * pre-existing archive.
2083 */
2084int doAdd(Bundle* bundle)
2085{
2086 ZipFile* zip = NULL;
2087 status_t result = UNKNOWN_ERROR;
2088 const char* zipFileName;
2089
2090 if (bundle->getUpdate()) {
2091 /* avoid confusion */
2092 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2093 goto bail;
2094 }
2095
2096 if (bundle->getFileSpecCount() < 1) {
2097 fprintf(stderr, "ERROR: must specify zip file name\n");
2098 goto bail;
2099 }
2100 zipFileName = bundle->getFileSpecEntry(0);
2101
2102 if (bundle->getFileSpecCount() < 2) {
2103 fprintf(stderr, "NOTE: nothing to do\n");
2104 goto bail;
2105 }
2106
2107 zip = openReadWrite(zipFileName, true);
2108 if (zip == NULL) {
2109 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2110 goto bail;
2111 }
2112
2113 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2114 const char* fileName = bundle->getFileSpecEntry(i);
2115
2116 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2117 printf(" '%s'... (from gzip)\n", fileName);
2118 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2119 } else {
2120 if (bundle->getJunkPath()) {
2121 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002122 printf(" '%s' as '%s'...\n", fileName,
2123 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002124 result = zip->add(fileName, storageName.string(),
2125 bundle->getCompressionMethod(), NULL);
2126 } else {
2127 printf(" '%s'...\n", fileName);
2128 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2129 }
2130 }
2131 if (result != NO_ERROR) {
2132 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2133 if (result == NAME_NOT_FOUND) {
2134 fprintf(stderr, ": file not found\n");
2135 } else if (result == ALREADY_EXISTS) {
2136 fprintf(stderr, ": already exists in archive\n");
2137 } else {
2138 fprintf(stderr, "\n");
2139 }
2140 goto bail;
2141 }
2142 }
2143
2144 result = NO_ERROR;
2145
2146bail:
2147 delete zip;
2148 return (result != NO_ERROR);
2149}
2150
2151
2152/*
2153 * Delete files from an existing archive.
2154 */
2155int doRemove(Bundle* bundle)
2156{
2157 ZipFile* zip = NULL;
2158 status_t result = UNKNOWN_ERROR;
2159 const char* zipFileName;
2160
2161 if (bundle->getFileSpecCount() < 1) {
2162 fprintf(stderr, "ERROR: must specify zip file name\n");
2163 goto bail;
2164 }
2165 zipFileName = bundle->getFileSpecEntry(0);
2166
2167 if (bundle->getFileSpecCount() < 2) {
2168 fprintf(stderr, "NOTE: nothing to do\n");
2169 goto bail;
2170 }
2171
2172 zip = openReadWrite(zipFileName, false);
2173 if (zip == NULL) {
2174 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2175 zipFileName);
2176 goto bail;
2177 }
2178
2179 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2180 const char* fileName = bundle->getFileSpecEntry(i);
2181 ZipEntry* entry;
2182
2183 entry = zip->getEntryByName(fileName);
2184 if (entry == NULL) {
2185 printf(" '%s' NOT FOUND\n", fileName);
2186 continue;
2187 }
2188
2189 result = zip->remove(entry);
2190
2191 if (result != NO_ERROR) {
2192 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2193 bundle->getFileSpecEntry(i), zipFileName);
2194 goto bail;
2195 }
2196 }
2197
2198 /* update the archive */
2199 zip->flush();
2200
2201bail:
2202 delete zip;
2203 return (result != NO_ERROR);
2204}
2205
Adam Lesinski3921e872014-05-13 10:56:25 -07002206static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002207 const size_t numDirs = dir->getDirs().size();
2208 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002209 bool ignore = ignoreConfig;
2210 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2211 const char* dirStr = subDir->getLeaf().string();
2212 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2213 ignore = true;
2214 }
2215 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002216 if (err != NO_ERROR) {
2217 return err;
2218 }
2219 }
2220
2221 const size_t numFiles = dir->getFiles().size();
2222 for (size_t i = 0; i < numFiles; i++) {
2223 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2224 const size_t numConfigs = gp->getFiles().size();
2225 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002226 status_t err = NO_ERROR;
2227 if (ignoreConfig) {
2228 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2229 } else {
2230 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2231 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002232 if (err != NO_ERROR) {
2233 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2234 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2235 return err;
2236 }
2237 }
2238 }
2239 return NO_ERROR;
2240}
2241
2242static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2243 if (split->isBase()) {
2244 return original;
2245 }
2246
2247 String8 ext(original.getPathExtension());
2248 if (ext == String8(".apk")) {
2249 return String8::format("%s_%s%s",
2250 original.getBasePath().string(),
2251 split->getDirectorySafeName().string(),
2252 ext.string());
2253 }
2254
2255 return String8::format("%s_%s", original.string(),
2256 split->getDirectorySafeName().string());
2257}
Adam Lesinski282e1812014-01-23 18:17:42 -08002258
2259/*
2260 * Package up an asset directory and associated application files.
2261 */
2262int doPackage(Bundle* bundle)
2263{
2264 const char* outputAPKFile;
2265 int retVal = 1;
2266 status_t err;
2267 sp<AaptAssets> assets;
2268 int N;
2269 FILE* fp;
2270 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002271 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002272
Anton Krumina2ef5c02014-03-12 14:46:44 -07002273 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002274 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2275 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002276 if (err != NO_ERROR) {
2277 goto bail;
2278 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002279 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002280 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2281 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002282 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002283 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002284 }
2285
2286 N = bundle->getFileSpecCount();
2287 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002288 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002289 fprintf(stderr, "ERROR: no input files\n");
2290 goto bail;
2291 }
2292
2293 outputAPKFile = bundle->getOutputAPKFile();
2294
2295 // Make sure the filenames provided exist and are of the appropriate type.
2296 if (outputAPKFile) {
2297 FileType type;
2298 type = getFileType(outputAPKFile);
2299 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2300 fprintf(stderr,
2301 "ERROR: output file '%s' exists but is not regular file\n",
2302 outputAPKFile);
2303 goto bail;
2304 }
2305 }
2306
2307 // Load the assets.
2308 assets = new AaptAssets();
2309
2310 // Set up the resource gathering in assets if we're going to generate
2311 // dependency files. Every time we encounter a resource while slurping
2312 // the tree, we'll add it to these stores so we have full resource paths
2313 // to write to a dependency file.
2314 if (bundle->getGenDependencies()) {
2315 sp<FilePathStore> resPathStore = new FilePathStore;
2316 assets->setFullResPaths(resPathStore);
2317 sp<FilePathStore> assetPathStore = new FilePathStore;
2318 assets->setFullAssetPaths(assetPathStore);
2319 }
2320
2321 err = assets->slurpFromArgs(bundle);
2322 if (err < 0) {
2323 goto bail;
2324 }
2325
2326 if (bundle->getVerbose()) {
2327 assets->print(String8());
2328 }
2329
Adam Lesinskifab50872014-04-16 14:40:42 -07002330 // Create the ApkBuilder, which will collect the compiled files
2331 // to write to the final APK (or sets of APKs if we are building
2332 // a Split APK.
2333 builder = new ApkBuilder(configFilter);
2334
2335 // If we are generating a Split APK, find out which configurations to split on.
2336 if (bundle->getSplitConfigurations().size() > 0) {
2337 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2338 const size_t numSplits = splitStrs.size();
2339 for (size_t i = 0; i < numSplits; i++) {
2340 std::set<ConfigDescription> configs;
2341 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2342 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2343 goto bail;
2344 }
2345
2346 err = builder->createSplitForConfigs(configs);
2347 if (err != NO_ERROR) {
2348 goto bail;
2349 }
2350 }
2351 }
2352
Adam Lesinski282e1812014-01-23 18:17:42 -08002353 // If they asked for any fileAs that need to be compiled, do so.
2354 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002355 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002356 if (err != 0) {
2357 goto bail;
2358 }
2359 }
2360
2361 // At this point we've read everything and processed everything. From here
2362 // on out it's just writing output files.
2363 if (SourcePos::hasErrors()) {
2364 goto bail;
2365 }
2366
2367 // Update symbols with information about which ones are needed as Java symbols.
2368 assets->applyJavaSymbols();
2369 if (SourcePos::hasErrors()) {
2370 goto bail;
2371 }
2372
2373 // If we've been asked to generate a dependency file, do that here
2374 if (bundle->getGenDependencies()) {
2375 // If this is the packaging step, generate the dependency file next to
2376 // the output apk (e.g. bin/resources.ap_.d)
2377 if (outputAPKFile) {
2378 dependencyFile = String8(outputAPKFile);
2379 // Add the .d extension to the dependency file.
2380 dependencyFile.append(".d");
2381 } else {
2382 // Else if this is the R.java dependency generation step,
2383 // generate the dependency file in the R.java package subdirectory
2384 // e.g. gen/com/foo/app/R.java.d
2385 dependencyFile = String8(bundle->getRClassDir());
2386 dependencyFile.appendPath("R.java.d");
2387 }
2388 // Make sure we have a clean dependency file to start with
2389 fp = fopen(dependencyFile, "w");
2390 fclose(fp);
2391 }
2392
2393 // Write out R.java constants
2394 if (!assets->havePrivateSymbols()) {
2395 if (bundle->getCustomPackage() == NULL) {
2396 // Write the R.java file into the appropriate class directory
2397 // e.g. gen/com/foo/app/R.java
2398 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2399 } else {
2400 const String8 customPkg(bundle->getCustomPackage());
2401 err = writeResourceSymbols(bundle, assets, customPkg, true);
2402 }
2403 if (err < 0) {
2404 goto bail;
2405 }
2406 // If we have library files, we're going to write our R.java file into
2407 // the appropriate class directory for those libraries as well.
2408 // e.g. gen/com/foo/app/lib/R.java
2409 if (bundle->getExtraPackages() != NULL) {
2410 // Split on colon
2411 String8 libs(bundle->getExtraPackages());
2412 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2413 while (packageString != NULL) {
2414 // Write the R.java file out with the correct package name
2415 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2416 if (err < 0) {
2417 goto bail;
2418 }
2419 packageString = strtok(NULL, ":");
2420 }
2421 libs.unlockBuffer();
2422 }
2423 } else {
2424 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2425 if (err < 0) {
2426 goto bail;
2427 }
2428 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2429 if (err < 0) {
2430 goto bail;
2431 }
2432 }
2433
2434 // Write out the ProGuard file
2435 err = writeProguardFile(bundle, assets);
2436 if (err < 0) {
2437 goto bail;
2438 }
2439
2440 // Write the apk
2441 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002442 // Gather all resources and add them to the APK Builder. The builder will then
2443 // figure out which Split they belong in.
2444 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002445 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002446 goto bail;
2447 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002448
2449 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2450 const size_t numSplits = splits.size();
2451 for (size_t i = 0; i < numSplits; i++) {
2452 const sp<ApkSplit>& split = splits[i];
2453 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2454 err = writeAPK(bundle, outputPath, split);
2455 if (err != NO_ERROR) {
2456 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2457 goto bail;
2458 }
2459 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002460 }
2461
2462 // If we've been asked to generate a dependency file, we need to finish up here.
2463 // the writeResourceSymbols and writeAPK functions have already written the target
2464 // half of the dependency file, now we need to write the prerequisites. (files that
2465 // the R.java file or .ap_ file depend on)
2466 if (bundle->getGenDependencies()) {
2467 // Now that writeResourceSymbols or writeAPK has taken care of writing
2468 // the targets to our dependency file, we'll write the prereqs
2469 fp = fopen(dependencyFile, "a+");
2470 fprintf(fp, " : ");
2471 bool includeRaw = (outputAPKFile != NULL);
2472 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2473 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2474 // and therefore was not added to our pathstores during slurping
2475 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2476 fclose(fp);
2477 }
2478
2479 retVal = 0;
2480bail:
2481 if (SourcePos::hasErrors()) {
2482 SourcePos::printErrors(stderr);
2483 }
2484 return retVal;
2485}
2486
2487/*
2488 * Do PNG Crunching
2489 * PRECONDITIONS
2490 * -S flag points to a source directory containing drawable* folders
2491 * -C flag points to destination directory. The folder structure in the
2492 * source directory will be mirrored to the destination (cache) directory
2493 *
2494 * POSTCONDITIONS
2495 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002496 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002497 */
2498int doCrunch(Bundle* bundle)
2499{
2500 fprintf(stdout, "Crunching PNG Files in ");
2501 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2502 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2503
2504 updatePreProcessedCache(bundle);
2505
2506 return NO_ERROR;
2507}
2508
2509/*
2510 * Do PNG Crunching on a single flag
2511 * -i points to a single png file
2512 * -o points to a single png output file
2513 */
2514int doSingleCrunch(Bundle* bundle)
2515{
2516 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2517 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2518
2519 String8 input(bundle->getSingleCrunchInputFile());
2520 String8 output(bundle->getSingleCrunchOutputFile());
2521
2522 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2523 // we can't return the status_t as it gets truncate to the lower 8 bits.
2524 return 42;
2525 }
2526
2527 return NO_ERROR;
2528}
2529
2530char CONSOLE_DATA[2925] = {
2531 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2532 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2533 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2534 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2535 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2536 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2537 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2538 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2539 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2540 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2541 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2542 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2543 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2544 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2545 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2546 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2547 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2548 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2549 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2550 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2551 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2552 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2553 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2554 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2555 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2556 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2557 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2558 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2559 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2560 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2561 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2562 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2563 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2564 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2565 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2566 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2567 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2568 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2569 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2570 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2571 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2572 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2573 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2574 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2575 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2576 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2577 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2578 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2579 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2580 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2581 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2582 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2583 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2584 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2585 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2586 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2587 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2588 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2589 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2590 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2591 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2592 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2593 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2594 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2595 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2596 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2597 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2598 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2599 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2600 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2601 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2602 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2603 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2604 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2605 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2606 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2607 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2608 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2609 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2610 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2611 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2612 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2613 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2614 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2615 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2616 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2617 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2618 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2619 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2620 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2621 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2622 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2623 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2624 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2625 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2626 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2627 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2628 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2629 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2630 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2631 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2632 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2633 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2634 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2635 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2636 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2637 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2638 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2639 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2640 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2641 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2642 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2643 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2644 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2645 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2646 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2647 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2648 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2649 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2650 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2652 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2653 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2654 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2655 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2656 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2657 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2658 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2659 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2660 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2661 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2662 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2663 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2664 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2665 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2666 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2667 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2668 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2670 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2671 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2672 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2673 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2674 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2675 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2676 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2677 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2678 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2679 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2680 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2681 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2682 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2683 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2684 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2685 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2686 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2687 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2688 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2689 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2690 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2691 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2692 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2693 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2694 };