blob: a0f0a08bf95c717000b49a105c3bf485d74245d7 [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
Adam Lesinskiad751222014-08-18 14:06:38 -070027#ifndef AAPT_VERSION
28 #define AAPT_VERSION ""
29#endif
30
Adam Lesinski282e1812014-01-23 18:17:42 -080031/*
32 * Show version info. All the cool kids do it.
33 */
34int doVersion(Bundle* bundle)
35{
36 if (bundle->getFileSpecCount() != 0) {
37 printf("(ignoring extra arguments)\n");
38 }
Adam Lesinskiad751222014-08-18 14:06:38 -070039 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
Adam Lesinski282e1812014-01-23 18:17:42 -080040
41 return 0;
42}
43
44
45/*
46 * Open the file read only. The call fails if the file doesn't exist.
47 *
48 * Returns NULL on failure.
49 */
50ZipFile* openReadOnly(const char* fileName)
51{
52 ZipFile* zip;
53 status_t result;
54
55 zip = new ZipFile;
56 result = zip->open(fileName, ZipFile::kOpenReadOnly);
57 if (result != NO_ERROR) {
58 if (result == NAME_NOT_FOUND) {
59 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
60 } else if (result == PERMISSION_DENIED) {
61 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
62 } else {
63 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
64 fileName);
65 }
66 delete zip;
67 return NULL;
68 }
69
70 return zip;
71}
72
73/*
74 * Open the file read-write. The file will be created if it doesn't
75 * already exist and "okayToCreate" is set.
76 *
77 * Returns NULL on failure.
78 */
79ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
80{
81 ZipFile* zip = NULL;
82 status_t result;
83 int flags;
84
85 flags = ZipFile::kOpenReadWrite;
86 if (okayToCreate) {
87 flags |= ZipFile::kOpenCreate;
88 }
89
90 zip = new ZipFile;
91 result = zip->open(fileName, flags);
92 if (result != NO_ERROR) {
93 delete zip;
94 zip = NULL;
95 goto bail;
96 }
97
98bail:
99 return zip;
100}
101
102
103/*
104 * Return a short string describing the compression method.
105 */
106const char* compressionName(int method)
107{
108 if (method == ZipEntry::kCompressStored) {
109 return "Stored";
110 } else if (method == ZipEntry::kCompressDeflated) {
111 return "Deflated";
112 } else {
113 return "Unknown";
114 }
115}
116
117/*
118 * Return the percent reduction in size (0% == no compression).
119 */
120int calcPercent(long uncompressedLen, long compressedLen)
121{
122 if (!uncompressedLen) {
123 return 0;
124 } else {
125 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
126 }
127}
128
129/*
130 * Handle the "list" command, which can be a simple file dump or
131 * a verbose listing.
132 *
133 * The verbose listing closely matches the output of the Info-ZIP "unzip"
134 * command.
135 */
136int doList(Bundle* bundle)
137{
138 int result = 1;
139 ZipFile* zip = NULL;
140 const ZipEntry* entry;
141 long totalUncLen, totalCompLen;
142 const char* zipFileName;
143
144 if (bundle->getFileSpecCount() != 1) {
145 fprintf(stderr, "ERROR: specify zip file name (only)\n");
146 goto bail;
147 }
148 zipFileName = bundle->getFileSpecEntry(0);
149
150 zip = openReadOnly(zipFileName);
151 if (zip == NULL) {
152 goto bail;
153 }
154
155 int count, i;
156
157 if (bundle->getVerbose()) {
158 printf("Archive: %s\n", zipFileName);
159 printf(
160 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
161 printf(
162 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
163 }
164
165 totalUncLen = totalCompLen = 0;
166
167 count = zip->getNumEntries();
168 for (i = 0; i < count; i++) {
169 entry = zip->getEntryByIndex(i);
170 if (bundle->getVerbose()) {
171 char dateBuf[32];
172 time_t when;
173
174 when = entry->getModWhen();
175 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
176 localtime(&when));
177
178 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
179 (long) entry->getUncompressedLen(),
180 compressionName(entry->getCompressionMethod()),
181 (long) entry->getCompressedLen(),
182 calcPercent(entry->getUncompressedLen(),
183 entry->getCompressedLen()),
184 (size_t) entry->getLFHOffset(),
185 dateBuf,
186 entry->getCRC32(),
187 entry->getFileName());
188 } else {
189 printf("%s\n", entry->getFileName());
190 }
191
192 totalUncLen += entry->getUncompressedLen();
193 totalCompLen += entry->getCompressedLen();
194 }
195
196 if (bundle->getVerbose()) {
197 printf(
198 "-------- ------- --- -------\n");
199 printf("%8ld %7ld %2d%% %d files\n",
200 totalUncLen,
201 totalCompLen,
202 calcPercent(totalUncLen, totalCompLen),
203 zip->getNumEntries());
204 }
205
206 if (bundle->getAndroidList()) {
207 AssetManager assets;
208 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
209 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
210 goto bail;
211 }
212
213 const ResTable& res = assets.getResources(false);
214 if (&res == NULL) {
215 printf("\nNo resource table found.\n");
216 } else {
217#ifndef HAVE_ANDROID_OS
218 printf("\nResource table:\n");
219 res.print(false);
220#endif
221 }
222
223 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
224 Asset::ACCESS_BUFFER);
225 if (manifestAsset == NULL) {
226 printf("\nNo AndroidManifest.xml found.\n");
227 } else {
228 printf("\nAndroid manifest:\n");
229 ResXMLTree tree;
230 tree.setTo(manifestAsset->getBuffer(true),
231 manifestAsset->getLength());
232 printXMLBlock(&tree);
233 }
234 delete manifestAsset;
235 }
236
237 result = 0;
238
239bail:
240 delete zip;
241 return result;
242}
243
244static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
245{
246 size_t N = tree.getAttributeCount();
247 for (size_t i=0; i<N; i++) {
248 if (tree.getAttributeNameResID(i) == attrRes) {
249 return (ssize_t)i;
250 }
251 }
252 return -1;
253}
254
255String8 getAttribute(const ResXMLTree& tree, const char* ns,
256 const char* attr, String8* outError)
257{
258 ssize_t idx = tree.indexOfAttribute(ns, attr);
259 if (idx < 0) {
260 return String8();
261 }
262 Res_value value;
263 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
264 if (value.dataType != Res_value::TYPE_STRING) {
265 if (outError != NULL) {
266 *outError = "attribute is not a string value";
267 }
268 return String8();
269 }
270 }
271 size_t len;
272 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
273 return str ? String8(str, len) : String8();
274}
275
276static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
277{
278 ssize_t idx = indexOfAttribute(tree, attrRes);
279 if (idx < 0) {
280 return String8();
281 }
282 Res_value value;
283 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
284 if (value.dataType != Res_value::TYPE_STRING) {
285 if (outError != NULL) {
286 *outError = "attribute is not a string value";
287 }
288 return String8();
289 }
290 }
291 size_t len;
292 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
293 return str ? String8(str, len) : String8();
294}
295
296static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
297 String8* outError, int32_t defValue = -1)
298{
299 ssize_t idx = indexOfAttribute(tree, attrRes);
300 if (idx < 0) {
301 return defValue;
302 }
303 Res_value value;
304 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
305 if (value.dataType < Res_value::TYPE_FIRST_INT
306 || value.dataType > Res_value::TYPE_LAST_INT) {
307 if (outError != NULL) {
308 *outError = "attribute is not an integer value";
309 }
310 return defValue;
311 }
312 }
313 return value.data;
314}
315
316static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
317 uint32_t attrRes, String8* outError, int32_t defValue = -1)
318{
319 ssize_t idx = indexOfAttribute(tree, attrRes);
320 if (idx < 0) {
321 return defValue;
322 }
323 Res_value value;
324 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
325 if (value.dataType == Res_value::TYPE_REFERENCE) {
326 resTable->resolveReference(&value, 0);
327 }
328 if (value.dataType < Res_value::TYPE_FIRST_INT
329 || value.dataType > Res_value::TYPE_LAST_INT) {
330 if (outError != NULL) {
331 *outError = "attribute is not an integer value";
332 }
333 return defValue;
334 }
335 }
336 return value.data;
337}
338
339static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
340 uint32_t attrRes, String8* outError)
341{
342 ssize_t idx = indexOfAttribute(tree, attrRes);
343 if (idx < 0) {
344 return String8();
345 }
346 Res_value value;
347 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
348 if (value.dataType == Res_value::TYPE_STRING) {
349 size_t len;
350 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
351 return str ? String8(str, len) : String8();
352 }
353 resTable->resolveReference(&value, 0);
354 if (value.dataType != Res_value::TYPE_STRING) {
355 if (outError != NULL) {
356 *outError = "attribute is not a string value";
357 }
358 return String8();
359 }
360 }
361 size_t len;
362 const Res_value* value2 = &value;
363 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
364 return str ? String8(str, len) : String8();
365}
366
367static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
368 const ResXMLTree& tree, uint32_t attrRes, String8* outError)
369{
370 ssize_t idx = indexOfAttribute(tree, attrRes);
371 if (idx < 0) {
372 if (outError != NULL) {
373 *outError = "attribute could not be found";
374 }
375 return;
376 }
377 if (tree.getAttributeValue(idx, value) != NO_ERROR) {
378 if (value->dataType == Res_value::TYPE_REFERENCE) {
379 resTable->resolveReference(value, 0);
380 }
381 // The attribute was found and was resolved if need be.
382 return;
383 }
384 if (outError != NULL) {
385 *outError = "error getting resolved resource attribute";
386 }
387}
388
Maurice Chu76327312013-10-16 18:28:46 -0700389static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
390 uint32_t attrRes, String8 attrLabel, String8* outError)
391{
392 Res_value value;
393 getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
394 if (*outError != "") {
395 *outError = "error print resolved resource attribute";
396 return;
397 }
398 if (value.dataType == Res_value::TYPE_STRING) {
399 String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700400 printf("%s='%s'", attrLabel.string(),
401 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700402 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
403 value.dataType <= Res_value::TYPE_LAST_INT) {
404 printf("%s='%d'", attrLabel.string(), value.data);
405 } else {
406 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
407 }
408}
409
Adam Lesinski282e1812014-01-23 18:17:42 -0800410// These are attribute resource constants for the platform, as found
411// in android.R.attr
412enum {
413 LABEL_ATTR = 0x01010001,
414 ICON_ATTR = 0x01010002,
415 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700416 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700417 EXPORTED_ATTR = 0x01010010,
418 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700419 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800420 DEBUGGABLE_ATTR = 0x0101000f,
421 VALUE_ATTR = 0x01010024,
422 VERSION_CODE_ATTR = 0x0101021b,
423 VERSION_NAME_ATTR = 0x0101021c,
424 SCREEN_ORIENTATION_ATTR = 0x0101001e,
425 MIN_SDK_VERSION_ATTR = 0x0101020c,
426 MAX_SDK_VERSION_ATTR = 0x01010271,
427 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
428 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
429 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
430 REQ_NAVIGATION_ATTR = 0x0101022a,
431 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
432 TARGET_SDK_VERSION_ATTR = 0x01010270,
433 TEST_ONLY_ATTR = 0x01010272,
434 ANY_DENSITY_ATTR = 0x0101026c,
435 GL_ES_VERSION_ATTR = 0x01010281,
436 SMALL_SCREEN_ATTR = 0x01010284,
437 NORMAL_SCREEN_ATTR = 0x01010285,
438 LARGE_SCREEN_ATTR = 0x01010286,
439 XLARGE_SCREEN_ATTR = 0x010102bf,
440 REQUIRED_ATTR = 0x0101028e,
Adam Lesinskicaf797c2014-08-22 12:56:26 -0700441 INSTALL_LOCATION_ATTR = 0x010102b7,
Adam Lesinski282e1812014-01-23 18:17:42 -0800442 SCREEN_SIZE_ATTR = 0x010102ca,
443 SCREEN_DENSITY_ATTR = 0x010102cb,
444 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
445 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
446 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
447 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700448 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800449 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800450};
451
Maurice Chu2675f762013-10-22 17:33:11 -0700452String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800453 ssize_t idx = componentName.find(".");
454 String8 retStr(pkgName);
455 if (idx == 0) {
456 retStr += componentName;
457 } else if (idx < 0) {
458 retStr += ".";
459 retStr += componentName;
460 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700461 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800462 }
Maurice Chu2675f762013-10-22 17:33:11 -0700463 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800464}
465
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700466static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800467 size_t len;
468 ResXMLTree::event_code_t code;
469 int depth = 0;
470 bool first = true;
471 printf("compatible-screens:");
472 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
473 if (code == ResXMLTree::END_TAG) {
474 depth--;
475 if (depth < 0) {
476 break;
477 }
478 continue;
479 }
480 if (code != ResXMLTree::START_TAG) {
481 continue;
482 }
483 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700484 const char16_t* ctag16 = tree.getElementName(&len);
485 if (ctag16 == NULL) {
486 *outError = "failed to get XML element name (bad string pool)";
487 return;
488 }
489 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800490 if (tag == "screen") {
491 int32_t screenSize = getIntegerAttribute(tree,
492 SCREEN_SIZE_ATTR, NULL, -1);
493 int32_t screenDensity = getIntegerAttribute(tree,
494 SCREEN_DENSITY_ATTR, NULL, -1);
495 if (screenSize > 0 && screenDensity > 0) {
496 if (!first) {
497 printf(",");
498 }
499 first = false;
500 printf("'%d/%d'", screenSize, screenDensity);
501 }
502 }
503 }
504 printf("\n");
505}
506
Adam Lesinski58f1f362013-11-12 12:59:08 -0800507static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
508 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
509 if (maxSdkVersion != -1) {
510 printf(" maxSdkVersion='%d'", maxSdkVersion);
511 }
512 printf("\n");
513
514 if (optional) {
515 printf("optional-permission: name='%s'",
516 ResTable::normalizeForOutput(name.string()).string());
517 if (maxSdkVersion != -1) {
518 printf(" maxSdkVersion='%d'", maxSdkVersion);
519 }
520 printf("\n");
521 }
522}
523
524static void printUsesImpliedPermission(const String8& name, const String8& reason) {
525 printf("uses-implied-permission: name='%s' reason='%s'\n",
526 ResTable::normalizeForOutput(name.string()).string(),
527 ResTable::normalizeForOutput(reason.string()).string());
528}
529
Adam Lesinski94fc9122013-09-30 17:16:09 -0700530Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
531 String8 *outError = NULL)
532{
533 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
534 if (aidAsset == NULL) {
535 if (outError != NULL) *outError = "xml resource does not exist";
536 return Vector<String8>();
537 }
538
539 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
540
541 bool withinApduService = false;
542 Vector<String8> categories;
543
544 String8 error;
545 ResXMLTree tree;
546 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
547
548 size_t len;
549 int depth = 0;
550 ResXMLTree::event_code_t code;
551 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
552 if (code == ResXMLTree::END_TAG) {
553 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700554 const char16_t* ctag16 = tree.getElementName(&len);
555 if (ctag16 == NULL) {
556 *outError = "failed to get XML element name (bad string pool)";
557 return Vector<String8>();
558 }
559 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700560
561 if (depth == 0 && tag == serviceTagName) {
562 withinApduService = false;
563 }
564
565 } else if (code == ResXMLTree::START_TAG) {
566 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700567 const char16_t* ctag16 = tree.getElementName(&len);
568 if (ctag16 == NULL) {
569 *outError = "failed to get XML element name (bad string pool)";
570 return Vector<String8>();
571 }
572 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700573
574 if (depth == 1) {
575 if (tag == serviceTagName) {
576 withinApduService = true;
577 }
578 } else if (depth == 2 && withinApduService) {
579 if (tag == "aid-group") {
580 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
581 if (error != "") {
582 if (outError != NULL) *outError = error;
583 return Vector<String8>();
584 }
585
586 categories.add(category);
587 }
588 }
589 }
590 }
591 aidAsset->close();
592 return categories;
593}
594
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700595static void printComponentPresence(const char* componentName) {
596 printf("provides-component:'%s'\n", componentName);
597}
598
Adam Lesinski2c72b682014-06-24 09:56:01 -0700599/**
600 * Represents a feature that has been automatically added due to
601 * a pre-requisite or some other reason.
602 */
603struct ImpliedFeature {
604 /**
605 * Name of the implied feature.
606 */
607 String8 name;
608
609 /**
610 * List of human-readable reasons for why this feature was implied.
611 */
612 SortedVector<String8> reasons;
613};
614
615/**
616 * Represents a <feature-group> tag in the AndroidManifest.xml
617 */
618struct FeatureGroup {
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700619 FeatureGroup() : openGLESVersion(-1) {}
620
Adam Lesinski2c72b682014-06-24 09:56:01 -0700621 /**
622 * Human readable label
623 */
624 String8 label;
625
626 /**
627 * Explicit features defined in the group
628 */
629 KeyedVector<String8, bool> features;
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700630
631 /**
632 * OpenGL ES version required
633 */
634 int openGLESVersion;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700635};
636
637static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
638 const char* name, const char* reason) {
639 String8 name8(name);
640 ssize_t idx = impliedFeatures->indexOfKey(name8);
641 if (idx < 0) {
642 idx = impliedFeatures->add(name8, ImpliedFeature());
643 impliedFeatures->editValueAt(idx).name = name8;
644 }
645 impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
646}
647
648static void printFeatureGroup(const FeatureGroup& grp,
649 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
650 printf("feature-group: label='%s'\n", grp.label.string());
651
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700652 if (grp.openGLESVersion > 0) {
653 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
654 }
655
Adam Lesinski2c72b682014-06-24 09:56:01 -0700656 const size_t numFeatures = grp.features.size();
657 for (size_t i = 0; i < numFeatures; i++) {
658 if (!grp.features[i]) {
659 continue;
660 }
661
662 const String8& featureName = grp.features.keyAt(i);
663 printf(" uses-feature: name='%s'\n",
664 ResTable::normalizeForOutput(featureName.string()).string());
665 }
666
667 const size_t numImpliedFeatures =
668 (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
669 for (size_t i = 0; i < numImpliedFeatures; i++) {
670 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
671 if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
672 // The feature is explicitly set, no need to use implied
673 // definition.
674 continue;
675 }
676
677 String8 printableFeatureName(ResTable::normalizeForOutput(
678 impliedFeature.name.string()));
679 printf(" uses-feature: name='%s'\n", printableFeatureName.string());
680 printf(" uses-implied-feature: name='%s' reason='",
681 printableFeatureName.string());
682 const size_t numReasons = impliedFeature.reasons.size();
683 for (size_t j = 0; j < numReasons; j++) {
684 printf("%s", impliedFeature.reasons[j].string());
685 if (j + 2 < numReasons) {
686 printf(", ");
687 } else if (j + 1 < numReasons) {
688 printf(", and ");
689 }
690 }
691 printf("'\n");
692 }
693}
694
695static void addParentFeatures(FeatureGroup* grp, const String8& name) {
696 if (name == "android.hardware.camera.autofocus" ||
697 name == "android.hardware.camera.flash") {
698 grp->features.add(String8("android.hardware.camera"), true);
699 } else if (name == "android.hardware.location.gps" ||
700 name == "android.hardware.location.network") {
701 grp->features.add(String8("android.hardware.location"), true);
702 } else if (name == "android.hardware.touchscreen.multitouch") {
703 grp->features.add(String8("android.hardware.touchscreen"), true);
704 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
705 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
706 grp->features.add(String8("android.hardware.touchscreen"), true);
Adam Lesinskid7a94da2014-07-25 14:38:54 -0700707 } else if (name == "android.hardware.opengles.aep") {
708 const int openGLESVersion31 = 0x00030001;
709 if (openGLESVersion31 > grp->openGLESVersion) {
710 grp->openGLESVersion = openGLESVersion31;
711 }
Adam Lesinski2c72b682014-06-24 09:56:01 -0700712 }
713}
714
Adam Lesinski282e1812014-01-23 18:17:42 -0800715/*
716 * Handle the "dump" command, to extract select data from an archive.
717 */
718extern char CONSOLE_DATA[2925]; // see EOF
719int doDump(Bundle* bundle)
720{
721 status_t result = UNKNOWN_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -0800722
723 if (bundle->getFileSpecCount() < 1) {
724 fprintf(stderr, "ERROR: no dump option specified\n");
725 return 1;
726 }
727
728 if (bundle->getFileSpecCount() < 2) {
729 fprintf(stderr, "ERROR: no dump file specified\n");
730 return 1;
731 }
732
733 const char* option = bundle->getFileSpecEntry(0);
734 const char* filename = bundle->getFileSpecEntry(1);
735
736 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000737 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800738 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
739 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
740 return 1;
741 }
742
743 // Make a dummy config for retrieving resources... we need to supply
744 // non-default values for some configs so that we can retrieve resources
745 // in the app that don't have a default. The most important of these is
746 // the API version because key resources like icons will have an implicit
747 // version if they are using newer config types like density.
748 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000749 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800750 config.language[0] = 'e';
751 config.language[1] = 'n';
752 config.country[0] = 'U';
753 config.country[1] = 'S';
754 config.orientation = ResTable_config::ORIENTATION_PORT;
755 config.density = ResTable_config::DENSITY_MEDIUM;
756 config.sdkVersion = 10000; // Very high.
757 config.screenWidthDp = 320;
758 config.screenHeightDp = 480;
759 config.smallestScreenWidthDp = 320;
Adam Lesinskic2dea8d2014-08-04 16:40:41 -0700760 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
Adam Lesinski282e1812014-01-23 18:17:42 -0800761 assets.setConfiguration(config);
762
763 const ResTable& res = assets.getResources(false);
764 if (&res == NULL) {
765 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700766 return 1;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700767 } else if (res.getError() != NO_ERROR) {
768 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
Adam Lesinski63e646e2014-07-30 11:40:39 -0700769 return 1;
Adam Lesinski282e1812014-01-23 18:17:42 -0800770 }
771
Adam Lesinski2cb761e2014-08-15 13:59:02 -0700772 // The dynamicRefTable can be null if there are no resources for this asset cookie.
773 // This fine.
Adam Lesinski63e646e2014-07-30 11:40:39 -0700774 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700775
776 Asset* asset = NULL;
777
Adam Lesinski282e1812014-01-23 18:17:42 -0800778 if (strcmp("resources", option) == 0) {
779#ifndef HAVE_ANDROID_OS
780 res.print(bundle->getValues());
781#endif
782
783 } else if (strcmp("strings", option) == 0) {
784 const ResStringPool* pool = res.getTableStringBlock(0);
785 printStringPool(pool);
786
787 } else if (strcmp("xmltree", option) == 0) {
788 if (bundle->getFileSpecCount() < 3) {
789 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
790 goto bail;
791 }
792
793 for (int i=2; i<bundle->getFileSpecCount(); i++) {
794 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700795 ResXMLTree tree(dynamicRefTable);
796 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800797 if (asset == NULL) {
798 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
799 goto bail;
800 }
801
802 if (tree.setTo(asset->getBuffer(true),
803 asset->getLength()) != NO_ERROR) {
804 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
805 goto bail;
806 }
807 tree.restart();
808 printXMLBlock(&tree);
809 tree.uninit();
810 delete asset;
811 asset = NULL;
812 }
813
814 } else if (strcmp("xmlstrings", option) == 0) {
815 if (bundle->getFileSpecCount() < 3) {
816 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
817 goto bail;
818 }
819
820 for (int i=2; i<bundle->getFileSpecCount(); i++) {
821 const char* resname = bundle->getFileSpecEntry(i);
Adam Lesinski63e646e2014-07-30 11:40:39 -0700822 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800823 if (asset == NULL) {
824 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
825 goto bail;
826 }
827
Adam Lesinski63e646e2014-07-30 11:40:39 -0700828 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800829 if (tree.setTo(asset->getBuffer(true),
830 asset->getLength()) != NO_ERROR) {
831 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
832 goto bail;
833 }
834 printStringPool(&tree.getStrings());
835 delete asset;
836 asset = NULL;
837 }
838
839 } else {
Adam Lesinski63e646e2014-07-30 11:40:39 -0700840 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
Adam Lesinski282e1812014-01-23 18:17:42 -0800841 if (asset == NULL) {
842 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
843 goto bail;
844 }
845
Adam Lesinski63e646e2014-07-30 11:40:39 -0700846 ResXMLTree tree(dynamicRefTable);
Adam Lesinski282e1812014-01-23 18:17:42 -0800847 if (tree.setTo(asset->getBuffer(true),
848 asset->getLength()) != NO_ERROR) {
849 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
850 goto bail;
851 }
852 tree.restart();
853
854 if (strcmp("permissions", option) == 0) {
855 size_t len;
856 ResXMLTree::event_code_t code;
857 int depth = 0;
858 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
859 if (code == ResXMLTree::END_TAG) {
860 depth--;
861 continue;
862 }
863 if (code != ResXMLTree::START_TAG) {
864 continue;
865 }
866 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700867 const char16_t* ctag16 = tree.getElementName(&len);
868 if (ctag16 == NULL) {
869 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
870 goto bail;
871 }
872 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800873 //printf("Depth %d tag %s\n", depth, tag.string());
874 if (depth == 1) {
875 if (tag != "manifest") {
876 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
877 goto bail;
878 }
879 String8 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700880 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800881 } else if (depth == 2 && tag == "permission") {
882 String8 error;
883 String8 name = getAttribute(tree, NAME_ATTR, &error);
884 if (error != "") {
885 fprintf(stderr, "ERROR: %s\n", error.string());
886 goto bail;
887 }
Maurice Chu2675f762013-10-22 17:33:11 -0700888 printf("permission: %s\n",
889 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800890 } else if (depth == 2 && tag == "uses-permission") {
891 String8 error;
892 String8 name = getAttribute(tree, NAME_ATTR, &error);
893 if (error != "") {
894 fprintf(stderr, "ERROR: %s\n", error.string());
895 goto bail;
896 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800897 printUsesPermission(name,
898 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
899 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
Adam Lesinski282e1812014-01-23 18:17:42 -0800900 }
901 }
902 } else if (strcmp("badging", option) == 0) {
903 Vector<String8> locales;
904 res.getLocales(&locales);
905
906 Vector<ResTable_config> configs;
907 res.getConfigurations(&configs);
908 SortedVector<int> densities;
909 const size_t NC = configs.size();
910 for (size_t i=0; i<NC; i++) {
911 int dens = configs[i].density;
912 if (dens == 0) {
913 dens = 160;
914 }
915 densities.add(dens);
916 }
917
918 size_t len;
919 ResXMLTree::event_code_t code;
920 int depth = 0;
921 String8 error;
922 bool withinActivity = false;
923 bool isMainActivity = false;
924 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800925 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800926 bool isSearchable = false;
927 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700928 bool withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -0700929 bool withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800930 bool withinReceiver = false;
931 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700932 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800933 bool withinIntentFilter = false;
934 bool hasMainActivity = false;
935 bool hasOtherActivities = false;
936 bool hasOtherReceivers = false;
937 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700938 bool hasIntentFilter = false;
939
Adam Lesinski282e1812014-01-23 18:17:42 -0800940 bool hasWallpaperService = false;
941 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700942 bool hasAccessibilityService = false;
943 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800944 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700945 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700946 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700947 bool hasDocumentsProvider = false;
948 bool hasCameraActivity = false;
949 bool hasCameraSecureActivity = false;
950 bool hasLauncher = false;
951 bool hasNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400952 bool hasDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700953
Adam Lesinski282e1812014-01-23 18:17:42 -0800954 bool actMainActivity = false;
955 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700956 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800957 bool actImeService = false;
958 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700959 bool actAccessibilityService = false;
960 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700961 bool actHostApduService = false;
962 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700963 bool actDocumentsProvider = false;
964 bool actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400965 bool actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700966 bool actCamera = false;
967 bool actCameraSecure = false;
968 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700969 bool hasMetaHostPaymentCategory = false;
970 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700971
972 // These permissions are required by services implementing services
973 // the system binds to (IME, Accessibility, PrintServices, etc.)
974 bool hasBindDeviceAdminPermission = false;
975 bool hasBindInputMethodPermission = false;
976 bool hasBindAccessibilityServicePermission = false;
977 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700978 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700979 bool hasRequiredSafAttributes = false;
980 bool hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -0400981 bool hasBindDreamServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800982
983 // These two implement the implicit permissions that are granted
984 // to pre-1.6 applications.
985 bool hasWriteExternalStoragePermission = false;
986 bool hasReadPhoneStatePermission = false;
987
988 // If an app requests write storage, they will also get read storage.
989 bool hasReadExternalStoragePermission = false;
990
991 // Implement transition to read and write call log.
992 bool hasReadContactsPermission = false;
993 bool hasWriteContactsPermission = false;
994 bool hasReadCallLogPermission = false;
995 bool hasWriteCallLogPermission = false;
996
Adam Lesinskie47fd122014-08-15 22:25:36 -0700997 // If an app declares itself as multiArch, we report the
998 // native libraries differently.
999 bool hasMultiArch = false;
1000
Adam Lesinski282e1812014-01-23 18:17:42 -08001001 // This next group of variables is used to implement a group of
1002 // backward-compatibility heuristics necessitated by the addition of
1003 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
1004 // heuristic is "if an app requests a permission but doesn't explicitly
1005 // request the corresponding <uses-feature>, presume it's there anyway".
Adam Lesinski2c72b682014-06-24 09:56:01 -07001006
Adam Lesinski282e1812014-01-23 18:17:42 -08001007 // 2.2 also added some other features that apps can request, but that
1008 // have no corresponding permission, so we cannot implement any
1009 // back-compatibility heuristic for them. The below are thus unnecessary
1010 // (but are retained here for documentary purposes.)
1011 //bool specCompassFeature = false;
1012 //bool specAccelerometerFeature = false;
1013 //bool specProximityFeature = false;
1014 //bool specAmbientLightFeature = false;
1015 //bool specLiveWallpaperFeature = false;
1016
1017 int targetSdk = 0;
1018 int smallScreen = 1;
1019 int normalScreen = 1;
1020 int largeScreen = 1;
1021 int xlargeScreen = 1;
1022 int anyDensity = 1;
1023 int requiresSmallestWidthDp = 0;
1024 int compatibleWidthLimitDp = 0;
1025 int largestWidthLimitDp = 0;
1026 String8 pkg;
1027 String8 activityName;
1028 String8 activityLabel;
1029 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001030 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -08001031 String8 receiverName;
1032 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001033 Vector<String8> supportedInput;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001034
1035 FeatureGroup commonFeatures;
1036 Vector<FeatureGroup> featureGroups;
1037 KeyedVector<String8, ImpliedFeature> impliedFeatures;
1038
Adam Lesinski282e1812014-01-23 18:17:42 -08001039 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1040 if (code == ResXMLTree::END_TAG) {
1041 depth--;
1042 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001043 if (withinSupportsInput && !supportedInput.isEmpty()) {
1044 printf("supports-input: '");
1045 const size_t N = supportedInput.size();
1046 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07001047 printf("%s", ResTable::normalizeForOutput(
1048 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001049 if (i != N - 1) {
1050 printf("' '");
1051 } else {
1052 printf("'\n");
1053 }
1054 }
1055 supportedInput.clear();
1056 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001057 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001058 withinSupportsInput = false;
Adam Lesinski2c72b682014-06-24 09:56:01 -07001059 withinFeatureGroup = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001060 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001061 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -07001062 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001063 if (isLauncherActivity) {
1064 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001065 if (aName.length() > 0) {
1066 printf(" name='%s' ",
1067 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001068 }
1069 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001070 ResTable::normalizeForOutput(activityLabel.string()).string(),
1071 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001072 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001073 if (isLeanbackLauncherActivity) {
1074 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001075 if (aName.length() > 0) {
1076 printf(" name='%s' ",
1077 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001078 }
1079 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -08001080 ResTable::normalizeForOutput(activityLabel.string()).string(),
1081 ResTable::normalizeForOutput(activityIcon.string()).string(),
1082 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001083 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001084 }
1085 if (!hasIntentFilter) {
1086 hasOtherActivities |= withinActivity;
1087 hasOtherReceivers |= withinReceiver;
1088 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001089 } else {
1090 if (withinService) {
1091 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1092 hasBindNfcServicePermission);
1093 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1094 hasBindNfcServicePermission);
1095 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001096 }
1097 withinActivity = false;
1098 withinService = false;
1099 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001100 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001101 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001102 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001103 } else if (depth < 4) {
1104 if (withinIntentFilter) {
1105 if (withinActivity) {
1106 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001107 hasLauncher |= catLauncher;
1108 hasCameraActivity |= actCamera;
1109 hasCameraSecureActivity |= actCameraSecure;
1110 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -08001111 } else if (withinReceiver) {
1112 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001113 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1114 hasBindDeviceAdminPermission);
1115 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001116 } else if (withinService) {
1117 hasImeService |= actImeService;
1118 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001119 hasAccessibilityService |= (actAccessibilityService &&
1120 hasBindAccessibilityServicePermission);
1121 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001122 hasNotificationListenerService |= actNotificationListenerService &&
1123 hasBindNotificationListenerServicePermission;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001124 hasDreamService |= actDreamService && hasBindDreamServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001125 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001126 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001127 !actHostApduService && !actOffHostApduService &&
1128 !actNotificationListenerService);
1129 } else if (withinProvider) {
1130 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001131 }
1132 }
1133 withinIntentFilter = false;
1134 }
1135 continue;
1136 }
1137 if (code != ResXMLTree::START_TAG) {
1138 continue;
1139 }
1140 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001141
1142 const char16_t* ctag16 = tree.getElementName(&len);
1143 if (ctag16 == NULL) {
1144 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1145 goto bail;
1146 }
1147 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001148 //printf("Depth %d, %s\n", depth, tag.string());
1149 if (depth == 1) {
1150 if (tag != "manifest") {
1151 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1152 goto bail;
1153 }
1154 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001155 printf("package: name='%s' ",
1156 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001157 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1158 if (error != "") {
1159 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1160 goto bail;
1161 }
1162 if (versionCode > 0) {
1163 printf("versionCode='%d' ", versionCode);
1164 } else {
1165 printf("versionCode='' ");
1166 }
1167 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1168 if (error != "") {
1169 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1170 goto bail;
1171 }
Adam Lesinski25d35a92014-08-11 09:41:56 -07001172 printf("versionName='%s'",
Maurice Chu2675f762013-10-22 17:33:11 -07001173 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski25d35a92014-08-11 09:41:56 -07001174
1175 String8 splitName = getAttribute(tree, NULL, "split", NULL);
1176 if (!splitName.isEmpty()) {
1177 printf(" split='%s'", ResTable::normalizeForOutput(
1178 splitName.string()).string());
1179 }
1180 printf("\n");
Adam Lesinskicaf797c2014-08-22 12:56:26 -07001181
1182 int32_t installLocation = getResolvedIntegerAttribute(&res, tree,
1183 INSTALL_LOCATION_ATTR, &error, -1);
1184 if (error != "") {
1185 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
1186 error.string());
1187 goto bail;
1188 }
1189
1190 if (installLocation >= 0) {
1191 printf("install-location:'");
1192 switch (installLocation) {
1193 case 0:
1194 printf("auto");
1195 break;
1196 case 1:
1197 printf("internalOnly");
1198 break;
1199 case 2:
1200 printf("preferExternal");
1201 break;
1202 default:
1203 fprintf(stderr, "Invalid installLocation %d\n", installLocation);
1204 goto bail;
1205 }
1206 printf("'\n");
1207 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001208 } else if (depth == 2) {
1209 withinApplication = false;
1210 if (tag == "application") {
1211 withinApplication = true;
1212
1213 String8 label;
1214 const size_t NL = locales.size();
1215 for (size_t i=0; i<NL; i++) {
1216 const char* localeStr = locales[i].string();
1217 assets.setLocale(localeStr != NULL ? localeStr : "");
1218 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1219 if (llabel != "") {
1220 if (localeStr == NULL || strlen(localeStr) == 0) {
1221 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001222 printf("application-label:'%s'\n",
1223 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001224 } else {
1225 if (label == "") {
1226 label = llabel;
1227 }
1228 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001229 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001230 }
1231 }
1232 }
1233
1234 ResTable_config tmpConfig = config;
1235 const size_t ND = densities.size();
1236 for (size_t i=0; i<ND; i++) {
1237 tmpConfig.density = densities[i];
1238 assets.setConfiguration(tmpConfig);
1239 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1240 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001241 printf("application-icon-%d:'%s'\n", densities[i],
1242 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001243 }
1244 }
1245 assets.setConfiguration(config);
1246
1247 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1248 if (error != "") {
1249 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1250 goto bail;
1251 }
1252 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1253 if (error != "") {
1254 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1255 goto bail;
1256 }
Maurice Chu2675f762013-10-22 17:33:11 -07001257 printf("application: label='%s' ",
1258 ResTable::normalizeForOutput(label.string()).string());
1259 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001260 if (testOnly != 0) {
1261 printf("testOnly='%d'\n", testOnly);
1262 }
1263
1264 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1265 if (error != "") {
1266 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1267 goto bail;
1268 }
1269 if (debuggable != 0) {
1270 printf("application-debuggable\n");
1271 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07001272
1273 // We must search by name because the multiArch flag hasn't been API
1274 // frozen yet.
1275 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
1276 "multiArch");
1277 if (multiArchIndex >= 0) {
1278 Res_value value;
1279 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
1280 if (value.dataType >= Res_value::TYPE_FIRST_INT &&
1281 value.dataType <= Res_value::TYPE_LAST_INT) {
1282 hasMultiArch = value.data;
1283 }
1284 }
1285 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001286 } else if (tag == "uses-sdk") {
1287 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1288 if (error != "") {
1289 error = "";
1290 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1291 if (error != "") {
1292 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1293 error.string());
1294 goto bail;
1295 }
1296 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001297 printf("sdkVersion:'%s'\n",
1298 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001299 } else if (code != -1) {
1300 targetSdk = code;
1301 printf("sdkVersion:'%d'\n", code);
1302 }
1303 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1304 if (code != -1) {
1305 printf("maxSdkVersion:'%d'\n", code);
1306 }
1307 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1308 if (error != "") {
1309 error = "";
1310 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1311 if (error != "") {
1312 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1313 error.string());
1314 goto bail;
1315 }
1316 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001317 printf("targetSdkVersion:'%s'\n",
1318 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001319 } else if (code != -1) {
1320 if (targetSdk < code) {
1321 targetSdk = code;
1322 }
1323 printf("targetSdkVersion:'%d'\n", code);
1324 }
1325 } else if (tag == "uses-configuration") {
1326 int32_t reqTouchScreen = getIntegerAttribute(tree,
1327 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1328 int32_t reqKeyboardType = getIntegerAttribute(tree,
1329 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1330 int32_t reqHardKeyboard = getIntegerAttribute(tree,
1331 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1332 int32_t reqNavigation = getIntegerAttribute(tree,
1333 REQ_NAVIGATION_ATTR, NULL, 0);
1334 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1335 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1336 printf("uses-configuration:");
1337 if (reqTouchScreen != 0) {
1338 printf(" reqTouchScreen='%d'", reqTouchScreen);
1339 }
1340 if (reqKeyboardType != 0) {
1341 printf(" reqKeyboardType='%d'", reqKeyboardType);
1342 }
1343 if (reqHardKeyboard != 0) {
1344 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1345 }
1346 if (reqNavigation != 0) {
1347 printf(" reqNavigation='%d'", reqNavigation);
1348 }
1349 if (reqFiveWayNav != 0) {
1350 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1351 }
1352 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001353 } else if (tag == "supports-input") {
1354 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001355 } else if (tag == "supports-screens") {
1356 smallScreen = getIntegerAttribute(tree,
1357 SMALL_SCREEN_ATTR, NULL, 1);
1358 normalScreen = getIntegerAttribute(tree,
1359 NORMAL_SCREEN_ATTR, NULL, 1);
1360 largeScreen = getIntegerAttribute(tree,
1361 LARGE_SCREEN_ATTR, NULL, 1);
1362 xlargeScreen = getIntegerAttribute(tree,
1363 XLARGE_SCREEN_ATTR, NULL, 1);
1364 anyDensity = getIntegerAttribute(tree,
1365 ANY_DENSITY_ATTR, NULL, 1);
1366 requiresSmallestWidthDp = getIntegerAttribute(tree,
1367 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1368 compatibleWidthLimitDp = getIntegerAttribute(tree,
1369 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1370 largestWidthLimitDp = getIntegerAttribute(tree,
1371 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001372 } else if (tag == "feature-group") {
1373 withinFeatureGroup = true;
1374 FeatureGroup group;
1375 group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1376 if (error != "") {
1377 fprintf(stderr, "ERROR getting 'android:label' attribute:"
1378 " %s\n", error.string());
1379 goto bail;
1380 }
1381 featureGroups.add(group);
1382
Adam Lesinski282e1812014-01-23 18:17:42 -08001383 } else if (tag == "uses-feature") {
1384 String8 name = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001385 if (name != "" && error == "") {
1386 int req = getIntegerAttribute(tree,
1387 REQUIRED_ATTR, NULL, 1);
1388
Adam Lesinski2c72b682014-06-24 09:56:01 -07001389 commonFeatures.features.add(name, req);
1390 if (req) {
1391 addParentFeatures(&commonFeatures, name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001392 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001393 } else {
1394 int vers = getIntegerAttribute(tree,
1395 GL_ES_VERSION_ATTR, &error);
1396 if (error == "") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001397 if (vers > commonFeatures.openGLESVersion) {
1398 commonFeatures.openGLESVersion = vers;
1399 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001400 }
1401 }
1402 } else if (tag == "uses-permission") {
1403 String8 name = getAttribute(tree, NAME_ATTR, &error);
1404 if (name != "" && error == "") {
1405 if (name == "android.permission.CAMERA") {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001406 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
Adam Lesinski2c72b682014-06-24 09:56:01 -07001407 String8::format("requested %s permission", name.string())
1408 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001409 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001410 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1411 String8::format("requested %s permission", name.string())
1412 .string());
1413 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1414 String8::format("requested %s permission", name.string())
1415 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001416 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001417 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1418 String8::format("requested %s permission", name.string())
1419 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001420 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001421 addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1422 String8::format("requested %s permission", name.string())
1423 .string());
1424 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1425 String8::format("requested %s permission", name.string())
1426 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001427 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1428 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001429 addImpliedFeature(&impliedFeatures, "android.hardware.location",
1430 String8::format("requested %s permission", name.string())
1431 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001432 } else if (name == "android.permission.BLUETOOTH" ||
1433 name == "android.permission.BLUETOOTH_ADMIN") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001434 if (targetSdk > 4) {
1435 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1436 String8::format("requested %s permission", name.string())
1437 .string());
1438 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1439 "targetSdkVersion > 4");
1440 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001441 } else if (name == "android.permission.RECORD_AUDIO") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001442 addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1443 String8::format("requested %s permission", name.string())
1444 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001445 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1446 name == "android.permission.CHANGE_WIFI_STATE" ||
1447 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001448 addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1449 String8::format("requested %s permission", name.string())
1450 .string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001451 } else if (name == "android.permission.CALL_PHONE" ||
1452 name == "android.permission.CALL_PRIVILEGED" ||
1453 name == "android.permission.MODIFY_PHONE_STATE" ||
1454 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1455 name == "android.permission.READ_SMS" ||
1456 name == "android.permission.RECEIVE_SMS" ||
1457 name == "android.permission.RECEIVE_MMS" ||
1458 name == "android.permission.RECEIVE_WAP_PUSH" ||
1459 name == "android.permission.SEND_SMS" ||
1460 name == "android.permission.WRITE_APN_SETTINGS" ||
1461 name == "android.permission.WRITE_SMS") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001462 addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1463 String8("requested a telephony permission").string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001464 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1465 hasWriteExternalStoragePermission = true;
1466 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1467 hasReadExternalStoragePermission = true;
1468 } else if (name == "android.permission.READ_PHONE_STATE") {
1469 hasReadPhoneStatePermission = true;
1470 } else if (name == "android.permission.READ_CONTACTS") {
1471 hasReadContactsPermission = true;
1472 } else if (name == "android.permission.WRITE_CONTACTS") {
1473 hasWriteContactsPermission = true;
1474 } else if (name == "android.permission.READ_CALL_LOG") {
1475 hasReadCallLogPermission = true;
1476 } else if (name == "android.permission.WRITE_CALL_LOG") {
1477 hasWriteCallLogPermission = true;
1478 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001479
1480 printUsesPermission(name,
1481 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1482 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1483 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001484 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1485 error.string());
1486 goto bail;
1487 }
1488 } else if (tag == "uses-package") {
1489 String8 name = getAttribute(tree, NAME_ATTR, &error);
1490 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001491 printf("uses-package:'%s'\n",
1492 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001493 } else {
1494 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1495 error.string());
1496 goto bail;
1497 }
1498 } else if (tag == "original-package") {
1499 String8 name = getAttribute(tree, NAME_ATTR, &error);
1500 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001501 printf("original-package:'%s'\n",
1502 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001503 } else {
1504 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1505 error.string());
1506 goto bail;
1507 }
1508 } else if (tag == "supports-gl-texture") {
1509 String8 name = getAttribute(tree, NAME_ATTR, &error);
1510 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001511 printf("supports-gl-texture:'%s'\n",
1512 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001513 } else {
1514 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1515 error.string());
1516 goto bail;
1517 }
1518 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001519 printCompatibleScreens(tree, &error);
1520 if (error != "") {
1521 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1522 error.string());
1523 goto bail;
1524 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001525 depth--;
1526 } else if (tag == "package-verifier") {
1527 String8 name = getAttribute(tree, NAME_ATTR, &error);
1528 if (name != "" && error == "") {
1529 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1530 if (publicKey != "" && error == "") {
1531 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001532 ResTable::normalizeForOutput(name.string()).string(),
1533 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001534 }
1535 }
1536 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001537 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001538 withinActivity = false;
1539 withinReceiver = false;
1540 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001541 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001542 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001543 hasMetaHostPaymentCategory = false;
1544 hasMetaOffHostPaymentCategory = false;
1545 hasBindDeviceAdminPermission = false;
1546 hasBindInputMethodPermission = false;
1547 hasBindAccessibilityServicePermission = false;
1548 hasBindPrintServicePermission = false;
1549 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001550 hasRequiredSafAttributes = false;
1551 hasBindNotificationListenerServicePermission = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001552 hasBindDreamServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001553 if (withinApplication) {
1554 if(tag == "activity") {
1555 withinActivity = true;
1556 activityName = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001557 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001558 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1559 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001560 goto bail;
1561 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001562
Michael Wrightec4fdec2013-09-06 16:50:52 -07001563 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1564 if (error != "") {
1565 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1566 error.string());
1567 goto bail;
1568 }
1569
1570 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1571 if (error != "") {
1572 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1573 error.string());
1574 goto bail;
1575 }
1576
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001577 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1578 if (error != "") {
1579 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1580 error.string());
1581 goto bail;
1582 }
1583
Michael Wrightec4fdec2013-09-06 16:50:52 -07001584 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1585 SCREEN_ORIENTATION_ATTR, &error);
1586 if (error == "") {
1587 if (orien == 0 || orien == 6 || orien == 8) {
1588 // Requests landscape, sensorLandscape, or reverseLandscape.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001589 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1590 "one or more activities have specified a landscape orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001591 } else if (orien == 1 || orien == 7 || orien == 9) {
1592 // Requests portrait, sensorPortrait, or reversePortrait.
Adam Lesinski2c72b682014-06-24 09:56:01 -07001593 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1594 "one or more activities have specified a portrait orientation");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001595 }
1596 }
1597 } else if (tag == "uses-library") {
1598 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1599 if (error != "") {
1600 fprintf(stderr,
1601 "ERROR getting 'android:name' attribute for uses-library"
1602 " %s\n", error.string());
1603 goto bail;
1604 }
1605 int req = getIntegerAttribute(tree,
1606 REQUIRED_ATTR, NULL, 1);
1607 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001608 req ? "" : "-not-required", ResTable::normalizeForOutput(
1609 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001610 } else if (tag == "receiver") {
1611 withinReceiver = true;
1612 receiverName = getAttribute(tree, NAME_ATTR, &error);
1613
1614 if (error != "") {
1615 fprintf(stderr,
1616 "ERROR getting 'android:name' attribute for receiver:"
1617 " %s\n", error.string());
1618 goto bail;
1619 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001620
1621 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1622 if (error == "") {
1623 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1624 hasBindDeviceAdminPermission = true;
1625 }
1626 } else {
1627 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1628 " receiver '%s': %s\n", receiverName.string(), error.string());
1629 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001630 } else if (tag == "service") {
1631 withinService = true;
1632 serviceName = getAttribute(tree, NAME_ATTR, &error);
1633
1634 if (error != "") {
1635 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1636 "service:%s\n", error.string());
1637 goto bail;
1638 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001639
1640 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1641 if (error == "") {
1642 if (permission == "android.permission.BIND_INPUT_METHOD") {
1643 hasBindInputMethodPermission = true;
1644 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1645 hasBindAccessibilityServicePermission = true;
1646 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1647 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001648 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1649 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001650 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1651 hasBindNotificationListenerServicePermission = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001652 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1653 hasBindDreamServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001654 }
1655 } else {
1656 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1657 " service '%s': %s\n", serviceName.string(), error.string());
1658 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001659 } else if (tag == "provider") {
1660 withinProvider = true;
1661
1662 bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
1663 if (error != "") {
1664 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1665 " %s\n", error.string());
1666 goto bail;
1667 }
1668
1669 bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
1670 GRANT_URI_PERMISSIONS_ATTR, &error);
1671 if (error != "") {
1672 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1673 " %s\n", error.string());
1674 goto bail;
1675 }
1676
1677 String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
1678 if (error != "") {
1679 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1680 " %s\n", error.string());
1681 goto bail;
1682 }
1683
1684 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1685 permission == "android.permission.MANAGE_DOCUMENTS";
1686
Michael Wrightec4fdec2013-09-06 16:50:52 -07001687 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskib71adb62014-05-15 14:14:41 -07001688 String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001689 if (error != "") {
1690 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1691 "meta-data:%s\n", error.string());
1692 goto bail;
1693 }
Maurice Chu2675f762013-10-22 17:33:11 -07001694 printf("meta-data: name='%s' ",
1695 ResTable::normalizeForOutput(metaDataName.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -07001696 printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1697 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001698 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001699 // Try looking for a RESOURCE_ATTR
1700 error = "";
1701 printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1702 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001703 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001704 fprintf(stderr, "ERROR getting 'android:value' or "
1705 "'android:resource' attribute for "
1706 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001707 goto bail;
1708 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001709 }
Maurice Chu76327312013-10-16 18:28:46 -07001710 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001711 } else if (withinSupportsInput && tag == "input-type") {
1712 String8 name = getAttribute(tree, NAME_ATTR, &error);
1713 if (name != "" && error == "") {
1714 supportedInput.add(name);
1715 } else {
1716 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1717 error.string());
1718 goto bail;
1719 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001720 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001721 } else if (withinFeatureGroup && tag == "uses-feature") {
Adam Lesinski2c72b682014-06-24 09:56:01 -07001722 FeatureGroup& top = featureGroups.editTop();
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001723
1724 String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1725 if (name != "" && error == "") {
Adam Lesinskid3edfde2014-08-08 17:32:44 -07001726 top.features.add(name, true);
1727 addParentFeatures(&top, name);
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001728 } else {
1729 int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
1730 if (error == "") {
1731 if (vers > top.openGLESVersion) {
1732 top.openGLESVersion = vers;
1733 }
1734 }
Adam Lesinski2c72b682014-06-24 09:56:01 -07001735 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001736 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001737 } else if (depth == 4) {
1738 if (tag == "intent-filter") {
1739 hasIntentFilter = true;
1740 withinIntentFilter = true;
1741 actMainActivity = false;
1742 actWidgetReceivers = false;
1743 actImeService = false;
1744 actWallpaperService = false;
1745 actAccessibilityService = false;
1746 actPrintService = false;
1747 actDeviceAdminEnabled = false;
1748 actHostApduService = false;
1749 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001750 actDocumentsProvider = false;
1751 actNotificationListenerService = false;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001752 actDreamService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001753 actCamera = false;
1754 actCameraSecure = false;
1755 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001756 } else if (withinService && tag == "meta-data") {
1757 String8 name = getAttribute(tree, NAME_ATTR, &error);
1758 if (error != "") {
1759 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1760 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1761 goto bail;
1762 }
1763
1764 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1765 name == "android.nfc.cardemulation.off_host_apdu_service") {
1766 bool offHost = true;
1767 if (name == "android.nfc.cardemulation.host_apdu_service") {
1768 offHost = false;
1769 }
1770
1771 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1772 if (error != "") {
1773 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1774 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1775 goto bail;
1776 }
1777
1778 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1779 offHost, &error);
1780 if (error != "") {
1781 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1782 serviceName.string());
1783 goto bail;
1784 }
1785
1786 const size_t catLen = categories.size();
1787 for (size_t i = 0; i < catLen; i++) {
1788 bool paymentCategory = (categories[i] == "payment");
1789 if (offHost) {
1790 hasMetaOffHostPaymentCategory |= paymentCategory;
1791 } else {
1792 hasMetaHostPaymentCategory |= paymentCategory;
1793 }
1794 }
1795 }
1796 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001797 } else if ((depth == 5) && withinIntentFilter) {
1798 String8 action;
1799 if (tag == "action") {
1800 action = getAttribute(tree, NAME_ATTR, &error);
1801 if (error != "") {
1802 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1803 error.string());
1804 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001805 }
1806
Adam Lesinskia5018c92013-09-30 16:23:15 -07001807 if (withinActivity) {
1808 if (action == "android.intent.action.MAIN") {
1809 isMainActivity = true;
1810 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001811 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1812 action == "android.media.action.VIDEO_CAMERA") {
1813 actCamera = true;
1814 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1815 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001816 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001817 } else if (withinReceiver) {
1818 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1819 actWidgetReceivers = true;
1820 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1821 actDeviceAdminEnabled = true;
1822 }
1823 } else if (withinService) {
1824 if (action == "android.view.InputMethod") {
1825 actImeService = true;
1826 } else if (action == "android.service.wallpaper.WallpaperService") {
1827 actWallpaperService = true;
1828 } else if (action == "android.accessibilityservice.AccessibilityService") {
1829 actAccessibilityService = true;
1830 } else if (action == "android.printservice.PrintService") {
1831 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001832 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1833 actHostApduService = true;
1834 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1835 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001836 } else if (action == "android.service.notification.NotificationListenerService") {
1837 actNotificationListenerService = true;
John Spurlockeb8d1be2014-06-25 17:46:15 -04001838 } else if (action == "android.service.dreams.DreamService") {
1839 actDreamService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001840 }
1841 } else if (withinProvider) {
1842 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1843 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001844 }
1845 }
1846 if (action == "android.intent.action.SEARCH") {
1847 isSearchable = true;
1848 }
1849 }
1850
1851 if (tag == "category") {
1852 String8 category = getAttribute(tree, NAME_ATTR, &error);
1853 if (error != "") {
1854 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1855 error.string());
1856 goto bail;
1857 }
1858 if (withinActivity) {
1859 if (category == "android.intent.category.LAUNCHER") {
1860 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001861 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1862 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001863 } else if (category == "android.intent.category.HOME") {
1864 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001865 }
1866 }
1867 }
1868 }
1869 }
1870
1871 // Pre-1.6 implicitly granted permission compatibility logic
1872 if (targetSdk < 4) {
1873 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001874 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1875 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1876 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001877 hasWriteExternalStoragePermission = true;
1878 }
1879 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001880 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1881 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1882 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001883 }
1884 }
1885
1886 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1887 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1888 // do this (regardless of target API version) because we can't have
1889 // an app with write permission but not read permission.
1890 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001891 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1892 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1893 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001894 }
1895
1896 // Pre-JellyBean call log permission compatibility.
1897 if (targetSdk < 16) {
1898 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001899 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1900 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1901 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001902 }
1903 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001904 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1905 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1906 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001907 }
1908 }
1909
Adam Lesinski2c72b682014-06-24 09:56:01 -07001910 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1911 "default feature for all apps");
1912
1913 const size_t numFeatureGroups = featureGroups.size();
1914 if (numFeatureGroups == 0) {
1915 // If no <feature-group> tags were defined, apply auto-implied features.
1916 printFeatureGroup(commonFeatures, &impliedFeatures);
1917
1918 } else {
1919 // <feature-group> tags are defined, so we ignore implied features and
1920 for (size_t i = 0; i < numFeatureGroups; i++) {
1921 FeatureGroup& grp = featureGroups.editItemAt(i);
1922
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001923 if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1924 grp.openGLESVersion = commonFeatures.openGLESVersion;
1925 }
1926
Adam Lesinski2c72b682014-06-24 09:56:01 -07001927 // Merge the features defined in the top level (not inside a <feature-group>)
1928 // with this feature group.
1929 const size_t numCommonFeatures = commonFeatures.features.size();
1930 for (size_t j = 0; j < numCommonFeatures; j++) {
1931 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
Adam Lesinskid7a94da2014-07-25 14:38:54 -07001932 grp.features.add(commonFeatures.features.keyAt(j),
1933 commonFeatures.features[j]);
Adam Lesinski2c72b682014-06-24 09:56:01 -07001934 }
1935 }
1936
1937 if (!grp.features.isEmpty()) {
1938 printFeatureGroup(grp);
Adam Lesinski282e1812014-01-23 18:17:42 -08001939 }
1940 }
1941 }
1942
Adam Lesinski282e1812014-01-23 18:17:42 -08001943
Adam Lesinski282e1812014-01-23 18:17:42 -08001944 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001945 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001946 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001947 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001948 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001949 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001950 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001951 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001952 }
1953 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001954 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001955 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001956 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001957 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001958 }
1959 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001960 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001961 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001962 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001963 printComponentPresence("payment");
1964 }
1965 if (isSearchable) {
1966 printComponentPresence("search");
1967 }
1968 if (hasDocumentsProvider) {
1969 printComponentPresence("document-provider");
1970 }
1971 if (hasLauncher) {
1972 printComponentPresence("launcher");
1973 }
1974 if (hasNotificationListenerService) {
1975 printComponentPresence("notification-listener");
1976 }
John Spurlockeb8d1be2014-06-25 17:46:15 -04001977 if (hasDreamService) {
1978 printComponentPresence("dream");
1979 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001980 if (hasCameraActivity) {
1981 printComponentPresence("camera");
1982 }
1983 if (hasCameraSecureActivity) {
1984 printComponentPresence("camera-secure");
1985 }
1986
1987 if (hasMainActivity) {
1988 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001989 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001990 if (hasOtherActivities) {
1991 printf("other-activities\n");
1992 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001993 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001994 printf("other-receivers\n");
1995 }
1996 if (hasOtherServices) {
1997 printf("other-services\n");
1998 }
1999
2000 // For modern apps, if screen size buckets haven't been specified
2001 // but the new width ranges have, then infer the buckets from them.
2002 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
2003 && requiresSmallestWidthDp > 0) {
2004 int compatWidth = compatibleWidthLimitDp;
2005 if (compatWidth <= 0) {
2006 compatWidth = requiresSmallestWidthDp;
2007 }
2008 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
2009 smallScreen = -1;
2010 } else {
2011 smallScreen = 0;
2012 }
2013 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
2014 normalScreen = -1;
2015 } else {
2016 normalScreen = 0;
2017 }
2018 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
2019 largeScreen = -1;
2020 } else {
2021 largeScreen = 0;
2022 }
2023 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
2024 xlargeScreen = -1;
2025 } else {
2026 xlargeScreen = 0;
2027 }
2028 }
2029
2030 // Determine default values for any unspecified screen sizes,
2031 // based on the target SDK of the package. As of 4 (donut)
2032 // the screen size support was introduced, so all default to
2033 // enabled.
2034 if (smallScreen > 0) {
2035 smallScreen = targetSdk >= 4 ? -1 : 0;
2036 }
2037 if (normalScreen > 0) {
2038 normalScreen = -1;
2039 }
2040 if (largeScreen > 0) {
2041 largeScreen = targetSdk >= 4 ? -1 : 0;
2042 }
2043 if (xlargeScreen > 0) {
2044 // Introduced in Gingerbread.
2045 xlargeScreen = targetSdk >= 9 ? -1 : 0;
2046 }
2047 if (anyDensity > 0) {
2048 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
2049 || compatibleWidthLimitDp > 0) ? -1 : 0;
2050 }
2051 printf("supports-screens:");
2052 if (smallScreen != 0) {
2053 printf(" 'small'");
2054 }
2055 if (normalScreen != 0) {
2056 printf(" 'normal'");
2057 }
2058 if (largeScreen != 0) {
2059 printf(" 'large'");
2060 }
2061 if (xlargeScreen != 0) {
2062 printf(" 'xlarge'");
2063 }
2064 printf("\n");
2065 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2066 if (requiresSmallestWidthDp > 0) {
2067 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2068 }
2069 if (compatibleWidthLimitDp > 0) {
2070 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2071 }
2072 if (largestWidthLimitDp > 0) {
2073 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2074 }
2075
2076 printf("locales:");
2077 const size_t NL = locales.size();
2078 for (size_t i=0; i<NL; i++) {
2079 const char* localeStr = locales[i].string();
2080 if (localeStr == NULL || strlen(localeStr) == 0) {
2081 localeStr = "--_--";
2082 }
2083 printf(" '%s'", localeStr);
2084 }
2085 printf("\n");
2086
2087 printf("densities:");
2088 const size_t ND = densities.size();
2089 for (size_t i=0; i<ND; i++) {
2090 printf(" '%d'", densities[i]);
2091 }
2092 printf("\n");
2093
2094 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2095 if (dir != NULL) {
2096 if (dir->getFileCount() > 0) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002097 SortedVector<String8> architectures;
Adam Lesinski282e1812014-01-23 18:17:42 -08002098 for (size_t i=0; i<dir->getFileCount(); i++) {
Adam Lesinskie47fd122014-08-15 22:25:36 -07002099 architectures.add(ResTable::normalizeForOutput(
2100 dir->getFileName(i).string()));
Adam Lesinski282e1812014-01-23 18:17:42 -08002101 }
Adam Lesinskie47fd122014-08-15 22:25:36 -07002102
2103 bool outputAltNativeCode = false;
2104 // A multiArch package is one that contains 64-bit and
2105 // 32-bit versions of native code and expects 3rd-party
2106 // apps to load these native code libraries. Since most
2107 // 64-bit systems also support 32-bit apps, the apps
2108 // loading this multiArch package's code may be either
2109 // 32-bit or 64-bit.
2110 if (hasMultiArch) {
2111 // If this is a multiArch package, report the 64-bit
2112 // version only. Then as a separate entry, report the
2113 // rest.
2114 //
2115 // If we report the 32-bit architecture, this APK will
2116 // be installed on a 32-bit device, causing a large waste
2117 // of bandwidth and disk space. This assumes that
2118 // the developer of the multiArch package has also
2119 // made a version that is 32-bit only.
2120 String8 intel64("x86_64");
2121 String8 arm64("arm64-v8a");
2122 ssize_t index = architectures.indexOf(intel64);
2123 if (index < 0) {
2124 index = architectures.indexOf(arm64);
2125 }
2126
2127 if (index >= 0) {
2128 printf("native-code: '%s'\n", architectures[index].string());
2129 architectures.removeAt(index);
2130 outputAltNativeCode = true;
2131 }
2132 }
2133
2134 const size_t archCount = architectures.size();
2135 if (archCount > 0) {
2136 if (outputAltNativeCode) {
2137 printf("alt-");
2138 }
2139 printf("native-code:");
2140 for (size_t i = 0; i < archCount; i++) {
2141 printf(" '%s'", architectures[i].string());
2142 }
2143 printf("\n");
2144 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002145 }
2146 delete dir;
2147 }
2148 } else if (strcmp("badger", option) == 0) {
2149 printf("%s", CONSOLE_DATA);
2150 } else if (strcmp("configurations", option) == 0) {
2151 Vector<ResTable_config> configs;
2152 res.getConfigurations(&configs);
2153 const size_t N = configs.size();
2154 for (size_t i=0; i<N; i++) {
2155 printf("%s\n", configs[i].toString().string());
2156 }
2157 } else {
2158 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2159 goto bail;
2160 }
2161 }
2162
2163 result = NO_ERROR;
2164
2165bail:
2166 if (asset) {
2167 delete asset;
2168 }
2169 return (result != NO_ERROR);
2170}
2171
2172
2173/*
2174 * Handle the "add" command, which wants to add files to a new or
2175 * pre-existing archive.
2176 */
2177int doAdd(Bundle* bundle)
2178{
2179 ZipFile* zip = NULL;
2180 status_t result = UNKNOWN_ERROR;
2181 const char* zipFileName;
2182
2183 if (bundle->getUpdate()) {
2184 /* avoid confusion */
2185 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2186 goto bail;
2187 }
2188
2189 if (bundle->getFileSpecCount() < 1) {
2190 fprintf(stderr, "ERROR: must specify zip file name\n");
2191 goto bail;
2192 }
2193 zipFileName = bundle->getFileSpecEntry(0);
2194
2195 if (bundle->getFileSpecCount() < 2) {
2196 fprintf(stderr, "NOTE: nothing to do\n");
2197 goto bail;
2198 }
2199
2200 zip = openReadWrite(zipFileName, true);
2201 if (zip == NULL) {
2202 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2203 goto bail;
2204 }
2205
2206 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2207 const char* fileName = bundle->getFileSpecEntry(i);
2208
2209 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2210 printf(" '%s'... (from gzip)\n", fileName);
2211 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2212 } else {
2213 if (bundle->getJunkPath()) {
2214 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002215 printf(" '%s' as '%s'...\n", fileName,
2216 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002217 result = zip->add(fileName, storageName.string(),
2218 bundle->getCompressionMethod(), NULL);
2219 } else {
2220 printf(" '%s'...\n", fileName);
2221 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2222 }
2223 }
2224 if (result != NO_ERROR) {
2225 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2226 if (result == NAME_NOT_FOUND) {
2227 fprintf(stderr, ": file not found\n");
2228 } else if (result == ALREADY_EXISTS) {
2229 fprintf(stderr, ": already exists in archive\n");
2230 } else {
2231 fprintf(stderr, "\n");
2232 }
2233 goto bail;
2234 }
2235 }
2236
2237 result = NO_ERROR;
2238
2239bail:
2240 delete zip;
2241 return (result != NO_ERROR);
2242}
2243
2244
2245/*
2246 * Delete files from an existing archive.
2247 */
2248int doRemove(Bundle* bundle)
2249{
2250 ZipFile* zip = NULL;
2251 status_t result = UNKNOWN_ERROR;
2252 const char* zipFileName;
2253
2254 if (bundle->getFileSpecCount() < 1) {
2255 fprintf(stderr, "ERROR: must specify zip file name\n");
2256 goto bail;
2257 }
2258 zipFileName = bundle->getFileSpecEntry(0);
2259
2260 if (bundle->getFileSpecCount() < 2) {
2261 fprintf(stderr, "NOTE: nothing to do\n");
2262 goto bail;
2263 }
2264
2265 zip = openReadWrite(zipFileName, false);
2266 if (zip == NULL) {
2267 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2268 zipFileName);
2269 goto bail;
2270 }
2271
2272 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2273 const char* fileName = bundle->getFileSpecEntry(i);
2274 ZipEntry* entry;
2275
2276 entry = zip->getEntryByName(fileName);
2277 if (entry == NULL) {
2278 printf(" '%s' NOT FOUND\n", fileName);
2279 continue;
2280 }
2281
2282 result = zip->remove(entry);
2283
2284 if (result != NO_ERROR) {
2285 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2286 bundle->getFileSpecEntry(i), zipFileName);
2287 goto bail;
2288 }
2289 }
2290
2291 /* update the archive */
2292 zip->flush();
2293
2294bail:
2295 delete zip;
2296 return (result != NO_ERROR);
2297}
2298
Adam Lesinski3921e872014-05-13 10:56:25 -07002299static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002300 const size_t numDirs = dir->getDirs().size();
2301 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002302 bool ignore = ignoreConfig;
2303 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2304 const char* dirStr = subDir->getLeaf().string();
2305 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2306 ignore = true;
2307 }
2308 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002309 if (err != NO_ERROR) {
2310 return err;
2311 }
2312 }
2313
2314 const size_t numFiles = dir->getFiles().size();
2315 for (size_t i = 0; i < numFiles; i++) {
2316 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2317 const size_t numConfigs = gp->getFiles().size();
2318 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002319 status_t err = NO_ERROR;
2320 if (ignoreConfig) {
2321 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2322 } else {
2323 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2324 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002325 if (err != NO_ERROR) {
2326 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2327 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2328 return err;
2329 }
2330 }
2331 }
2332 return NO_ERROR;
2333}
2334
2335static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2336 if (split->isBase()) {
2337 return original;
2338 }
2339
2340 String8 ext(original.getPathExtension());
2341 if (ext == String8(".apk")) {
2342 return String8::format("%s_%s%s",
2343 original.getBasePath().string(),
2344 split->getDirectorySafeName().string(),
2345 ext.string());
2346 }
2347
2348 return String8::format("%s_%s", original.string(),
2349 split->getDirectorySafeName().string());
2350}
Adam Lesinski282e1812014-01-23 18:17:42 -08002351
2352/*
2353 * Package up an asset directory and associated application files.
2354 */
2355int doPackage(Bundle* bundle)
2356{
2357 const char* outputAPKFile;
2358 int retVal = 1;
2359 status_t err;
2360 sp<AaptAssets> assets;
2361 int N;
2362 FILE* fp;
2363 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002364 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002365
Anton Krumina2ef5c02014-03-12 14:46:44 -07002366 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002367 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2368 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002369 if (err != NO_ERROR) {
2370 goto bail;
2371 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002372 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002373 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2374 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002375 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002376 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002377 }
2378
2379 N = bundle->getFileSpecCount();
2380 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002381 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002382 fprintf(stderr, "ERROR: no input files\n");
2383 goto bail;
2384 }
2385
2386 outputAPKFile = bundle->getOutputAPKFile();
2387
2388 // Make sure the filenames provided exist and are of the appropriate type.
2389 if (outputAPKFile) {
2390 FileType type;
2391 type = getFileType(outputAPKFile);
2392 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2393 fprintf(stderr,
2394 "ERROR: output file '%s' exists but is not regular file\n",
2395 outputAPKFile);
2396 goto bail;
2397 }
2398 }
2399
2400 // Load the assets.
2401 assets = new AaptAssets();
2402
2403 // Set up the resource gathering in assets if we're going to generate
2404 // dependency files. Every time we encounter a resource while slurping
2405 // the tree, we'll add it to these stores so we have full resource paths
2406 // to write to a dependency file.
2407 if (bundle->getGenDependencies()) {
2408 sp<FilePathStore> resPathStore = new FilePathStore;
2409 assets->setFullResPaths(resPathStore);
2410 sp<FilePathStore> assetPathStore = new FilePathStore;
2411 assets->setFullAssetPaths(assetPathStore);
2412 }
2413
2414 err = assets->slurpFromArgs(bundle);
2415 if (err < 0) {
2416 goto bail;
2417 }
2418
2419 if (bundle->getVerbose()) {
2420 assets->print(String8());
2421 }
2422
Adam Lesinskifab50872014-04-16 14:40:42 -07002423 // Create the ApkBuilder, which will collect the compiled files
2424 // to write to the final APK (or sets of APKs if we are building
2425 // a Split APK.
2426 builder = new ApkBuilder(configFilter);
2427
2428 // If we are generating a Split APK, find out which configurations to split on.
2429 if (bundle->getSplitConfigurations().size() > 0) {
2430 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2431 const size_t numSplits = splitStrs.size();
2432 for (size_t i = 0; i < numSplits; i++) {
2433 std::set<ConfigDescription> configs;
2434 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2435 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2436 goto bail;
2437 }
2438
2439 err = builder->createSplitForConfigs(configs);
2440 if (err != NO_ERROR) {
2441 goto bail;
2442 }
2443 }
2444 }
2445
Adam Lesinski282e1812014-01-23 18:17:42 -08002446 // If they asked for any fileAs that need to be compiled, do so.
2447 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002448 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002449 if (err != 0) {
2450 goto bail;
2451 }
2452 }
2453
2454 // At this point we've read everything and processed everything. From here
2455 // on out it's just writing output files.
2456 if (SourcePos::hasErrors()) {
2457 goto bail;
2458 }
2459
2460 // Update symbols with information about which ones are needed as Java symbols.
2461 assets->applyJavaSymbols();
2462 if (SourcePos::hasErrors()) {
2463 goto bail;
2464 }
2465
2466 // If we've been asked to generate a dependency file, do that here
2467 if (bundle->getGenDependencies()) {
2468 // If this is the packaging step, generate the dependency file next to
2469 // the output apk (e.g. bin/resources.ap_.d)
2470 if (outputAPKFile) {
2471 dependencyFile = String8(outputAPKFile);
2472 // Add the .d extension to the dependency file.
2473 dependencyFile.append(".d");
2474 } else {
2475 // Else if this is the R.java dependency generation step,
2476 // generate the dependency file in the R.java package subdirectory
2477 // e.g. gen/com/foo/app/R.java.d
2478 dependencyFile = String8(bundle->getRClassDir());
2479 dependencyFile.appendPath("R.java.d");
2480 }
2481 // Make sure we have a clean dependency file to start with
2482 fp = fopen(dependencyFile, "w");
2483 fclose(fp);
2484 }
2485
2486 // Write out R.java constants
2487 if (!assets->havePrivateSymbols()) {
2488 if (bundle->getCustomPackage() == NULL) {
2489 // Write the R.java file into the appropriate class directory
2490 // e.g. gen/com/foo/app/R.java
Adam Lesinski1e4663852014-08-15 14:47:28 -07002491 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
2492 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002493 } else {
2494 const String8 customPkg(bundle->getCustomPackage());
Adam Lesinski1e4663852014-08-15 14:47:28 -07002495 err = writeResourceSymbols(bundle, assets, customPkg, true,
2496 bundle->getBuildSharedLibrary());
Adam Lesinski282e1812014-01-23 18:17:42 -08002497 }
2498 if (err < 0) {
2499 goto bail;
2500 }
2501 // If we have library files, we're going to write our R.java file into
2502 // the appropriate class directory for those libraries as well.
2503 // e.g. gen/com/foo/app/lib/R.java
2504 if (bundle->getExtraPackages() != NULL) {
2505 // Split on colon
2506 String8 libs(bundle->getExtraPackages());
2507 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2508 while (packageString != NULL) {
2509 // Write the R.java file out with the correct package name
Adam Lesinski1e4663852014-08-15 14:47:28 -07002510 err = writeResourceSymbols(bundle, assets, String8(packageString), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002511 if (err < 0) {
2512 goto bail;
2513 }
2514 packageString = strtok(NULL, ":");
2515 }
2516 libs.unlockBuffer();
2517 }
2518 } else {
Adam Lesinski1e4663852014-08-15 14:47:28 -07002519 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002520 if (err < 0) {
2521 goto bail;
2522 }
Adam Lesinski1e4663852014-08-15 14:47:28 -07002523 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
Adam Lesinski282e1812014-01-23 18:17:42 -08002524 if (err < 0) {
2525 goto bail;
2526 }
2527 }
2528
2529 // Write out the ProGuard file
2530 err = writeProguardFile(bundle, assets);
2531 if (err < 0) {
2532 goto bail;
2533 }
2534
2535 // Write the apk
2536 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002537 // Gather all resources and add them to the APK Builder. The builder will then
2538 // figure out which Split they belong in.
2539 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002540 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002541 goto bail;
2542 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002543
2544 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2545 const size_t numSplits = splits.size();
2546 for (size_t i = 0; i < numSplits; i++) {
2547 const sp<ApkSplit>& split = splits[i];
2548 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2549 err = writeAPK(bundle, outputPath, split);
2550 if (err != NO_ERROR) {
2551 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2552 goto bail;
2553 }
2554 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002555 }
2556
2557 // If we've been asked to generate a dependency file, we need to finish up here.
2558 // the writeResourceSymbols and writeAPK functions have already written the target
2559 // half of the dependency file, now we need to write the prerequisites. (files that
2560 // the R.java file or .ap_ file depend on)
2561 if (bundle->getGenDependencies()) {
2562 // Now that writeResourceSymbols or writeAPK has taken care of writing
2563 // the targets to our dependency file, we'll write the prereqs
2564 fp = fopen(dependencyFile, "a+");
2565 fprintf(fp, " : ");
2566 bool includeRaw = (outputAPKFile != NULL);
2567 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2568 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2569 // and therefore was not added to our pathstores during slurping
2570 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2571 fclose(fp);
2572 }
2573
2574 retVal = 0;
2575bail:
2576 if (SourcePos::hasErrors()) {
2577 SourcePos::printErrors(stderr);
2578 }
2579 return retVal;
2580}
2581
2582/*
2583 * Do PNG Crunching
2584 * PRECONDITIONS
2585 * -S flag points to a source directory containing drawable* folders
2586 * -C flag points to destination directory. The folder structure in the
2587 * source directory will be mirrored to the destination (cache) directory
2588 *
2589 * POSTCONDITIONS
2590 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002591 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002592 */
2593int doCrunch(Bundle* bundle)
2594{
2595 fprintf(stdout, "Crunching PNG Files in ");
2596 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2597 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2598
2599 updatePreProcessedCache(bundle);
2600
2601 return NO_ERROR;
2602}
2603
2604/*
2605 * Do PNG Crunching on a single flag
2606 * -i points to a single png file
2607 * -o points to a single png output file
2608 */
2609int doSingleCrunch(Bundle* bundle)
2610{
2611 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2612 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2613
2614 String8 input(bundle->getSingleCrunchInputFile());
2615 String8 output(bundle->getSingleCrunchOutputFile());
2616
2617 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2618 // we can't return the status_t as it gets truncate to the lower 8 bits.
2619 return 42;
2620 }
2621
2622 return NO_ERROR;
2623}
2624
2625char CONSOLE_DATA[2925] = {
2626 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2627 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2628 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2629 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2630 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2631 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2632 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2633 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2634 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2635 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2636 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2637 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2638 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2639 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2640 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2641 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2642 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2643 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2644 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2645 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2646 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2647 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2648 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2649 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2650 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2652 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2653 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2654 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2655 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2656 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2657 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2658 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2659 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2660 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2661 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2662 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2663 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2664 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2665 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2666 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2667 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2668 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2670 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2671 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2672 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2673 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2674 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2675 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2676 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2677 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2678 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2679 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2680 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2681 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2682 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2683 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2684 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2685 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2686 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2687 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2688 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2689 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2690 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2691 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2692 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2693 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2694 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2695 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2696 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2697 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2698 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2699 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2700 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2701 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2702 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2703 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2704 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2705 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2706 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2707 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2708 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2709 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2710 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2711 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2712 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2713 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2714 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2715 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2716 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2717 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2718 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2719 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2720 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2721 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2722 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2723 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2724 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2725 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2726 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2727 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2728 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2729 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2730 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2731 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2732 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2733 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2734 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2735 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2736 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2737 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2738 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2739 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2740 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2741 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2742 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2743 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2744 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2745 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2746 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2747 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2748 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2749 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2750 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2751 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2752 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2753 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2754 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2755 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2756 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2757 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2758 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2759 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2760 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2761 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2762 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2763 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2764 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2765 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2766 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2767 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2768 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2769 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2770 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2771 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2772 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2773 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2774 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2775 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2776 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2777 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2778 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2779 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2780 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2781 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2782 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2783 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2784 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2785 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2786 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2787 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2788 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2789 };