Command.cpp revision a125c937de06b1cc43368743592f47513eb88b76
1edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project//
2edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project// Copyright 2006 The Android Open Source Project
3edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project//
4edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project// Android Asset Packaging Tool main entry point.
5edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project//
6edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "Main.h"
7edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "Bundle.h"
8edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "ResourceTable.h"
9edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "XMLNode.h"
10edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
11edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/Log.h>
12edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/threads.h>
13edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/List.h>
14edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/Errors.h>
15edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
16edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <fcntl.h>
17edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <errno.h>
18edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
19edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectusing namespace android;
20edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
21edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project/*
22edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * Show version info.  All the cool kids do it.
23edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project */
24076b1cc3a9b90aa5b381a1ed268ca0b548444c9bMathias Agopianint doVersion(Bundle* bundle)
25edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project{
26edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    if (bundle->getFileSpecCount() != 0)
27c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian        printf("(ignoring extra arguments)\n");
28076b1cc3a9b90aa5b381a1ed268ca0b548444c9bMathias Agopian    printf("Android Asset Packaging Tool, v0.2\n");
29edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
30e3c697fb929c856b59fa56a8e05a2a7eba187c3dMathias Agopian    return 0;
311a4d883dcc1725892bfb5c28dec255a233186524Jamie Gennis}
32076b1cc3a9b90aa5b381a1ed268ca0b548444c9bMathias Agopian
33edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
3499c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall/*
351b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian * Open the file read only.  The call fails if the file doesn't exist.
36875d8e1323536e16dcfc90c9674d7ad32116a69aMathias Agopian *
371b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian * Returns NULL on failure.
38da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian */
390f2f5ff75b7b48ceb64270655ee6b62d09bf4d00Mathias AgopianZipFile* openReadOnly(const char* fileName)
40c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian{
4113127d8921356dff794250e04208c3ed60b3a3dfMathias Agopian    ZipFile* zip;
421f7bec634f19c123410a5155c8d282e177c01930Mathias Agopian    status_t result;
43a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopian
44edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    zip = new ZipFile;
45a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopian    result = zip->open(fileName, ZipFile::kOpenReadOnly);
46edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    if (result != NO_ERROR) {
47edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        if (result == NAME_NOT_FOUND)
48edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            fprintf(stderr, "ERROR: '%s' not found\n", fileName);
49edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        else if (result == PERMISSION_DENIED)
50edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
51edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        else
520f2f5ff75b7b48ceb64270655ee6b62d09bf4d00Mathias Agopian            fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
53edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project                fileName);
54dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis        delete zip;
55ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall        return NULL;
5619e872912af66c53a4350afcc333bbafaf6a2294Jesse Hall    }
57dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis
58dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis    return zip;
5999c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall}
60db89edc94bd2a78226b407f9f7261e202e7fa325Mathias Agopian
61a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopian/*
6292a979a92c34b7de609ce2b1662c73bb8a2728b9Mathias Agopian * Open the file read-write.  The file will be created if it doesn't
63ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall * already exist and "okayToCreate" is set.
6427e2562868dcd3ad26f9b4677b64ae272941704eChih-Wei Huang *
6599c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall * Returns NULL on failure.
6692a979a92c34b7de609ce2b1662c73bb8a2728b9Mathias Agopian */
6792a979a92c34b7de609ce2b1662c73bb8a2728b9Mathias AgopianZipFile* openReadWrite(const char* fileName, bool okayToCreate)
6892a979a92c34b7de609ce2b1662c73bb8a2728b9Mathias Agopian{
6992a979a92c34b7de609ce2b1662c73bb8a2728b9Mathias Agopian    ZipFile* zip = NULL;
7092a979a92c34b7de609ce2b1662c73bb8a2728b9Mathias Agopian    status_t result;
71dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis    int flags;
7292a979a92c34b7de609ce2b1662c73bb8a2728b9Mathias Agopian
7392a979a92c34b7de609ce2b1662c73bb8a2728b9Mathias Agopian    flags = ZipFile::kOpenReadWrite;
7401e29054e672301e4adbbca15b3562a59a20f267Jesse Hall    if (okayToCreate)
75da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian        flags |= ZipFile::kOpenCreate;
76edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
7783cafffeac037ab2f9d00f98f8d2f3da8fc9b857Jesse Hall    zip = new ZipFile;
78a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopian    result = zip->open(fileName, flags);
79a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopian    if (result != NO_ERROR) {
80a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopian        delete zip;
81a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopian        zip = NULL;
82b5dd9c0fee3b3d6d35035dfb992951ebea3e0e4eMathias Agopian        goto bail;
83385977f6d6c4e76379df384d50695a10cb3757f2Mathias Agopian    }
841f7bec634f19c123410a5155c8d282e177c01930Mathias Agopian
85a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopianbail:
86edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    return zip;
8719e872912af66c53a4350afcc333bbafaf6a2294Jesse Hall}
8819e872912af66c53a4350afcc333bbafaf6a2294Jesse Hall
8919e872912af66c53a4350afcc333bbafaf6a2294Jesse Hall
90a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopian/*
911b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian * Return a short string describing the compression method.
921b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian */
93edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectconst char* compressionName(int method)
944ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong{
954ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong    if (method == ZipEntry::kCompressStored)
964ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong        return "Stored";
974ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong    else if (method == ZipEntry::kCompressDeflated)
984ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong        return "Deflated";
994ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong    else
1004ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong        return "Unknown";
1014ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong}
1024ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong
1034ee5696dfbcea1078b24e7c089de74e7cf4e55caJohn Dong/*
1041f7bec634f19c123410a5155c8d282e177c01930Mathias Agopian * Return the percent reduction in size (0% == no compression).
1051f7bec634f19c123410a5155c8d282e177c01930Mathias Agopian */
106a49126087b4494f4ef50873f3a3f6727265f6621Mathias Agopianint calcPercent(long uncompressedLen, long compressedLen)
1071f7bec634f19c123410a5155c8d282e177c01930Mathias Agopian{
108da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian    if (!uncompressedLen)
109da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian        return 0;
1101f7bec634f19c123410a5155c8d282e177c01930Mathias Agopian    else
111ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall        return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
112ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall}
1135f20e2d4462da3471f59152b32cd8640fa4a21daMathias Agopian
1148dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden/*
1158dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden * Handle the "list" command, which can be a simple file dump or
1168dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden * a verbose listing.
1178dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden *
1188dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden * The verbose listing closely matches the output of the Info-ZIP "unzip"
1198dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden * command.
1208dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden */
1218dfa92fef9759a881e96ee58d59875d35023aab9Andy McFaddenint doList(Bundle* bundle)
1228dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden{
1238dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden    int result = 1;
1248dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden    ZipFile* zip = NULL;
1258dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden    const ZipEntry* entry;
1268dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden    long totalUncLen, totalCompLen;
1278dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden    const char* zipFileName;
12898a121aa916eb7acbf11df0e3e31a6fede6fc9ddMathias Agopian
12900e8c7a88a5b9c4104a71013a713acd3e4d3b77bMathias Agopian    if (bundle->getFileSpecCount() != 1) {
130a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian        fprintf(stderr, "ERROR: specify zip file name (only)\n");
131a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian        goto bail;
132ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall    }
133ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall    zipFileName = bundle->getFileSpecEntry(0);
134ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall
135ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall    zip = openReadOnly(zipFileName);
136ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall    if (zip == NULL)
137ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall        goto bail;
138ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall
13902d86567d95b99e1142941ed7ec23a4465822813Jesse Hall    int count, i;
14002d86567d95b99e1142941ed7ec23a4465822813Jesse Hall
14102d86567d95b99e1142941ed7ec23a4465822813Jesse Hall    if (bundle->getVerbose()) {
14202d86567d95b99e1142941ed7ec23a4465822813Jesse Hall        printf("Archive:  %s\n", zipFileName);
14302d86567d95b99e1142941ed7ec23a4465822813Jesse Hall        printf(
14402d86567d95b99e1142941ed7ec23a4465822813Jesse Hall            " Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name\n");
14502d86567d95b99e1142941ed7ec23a4465822813Jesse Hall        printf(
14602d86567d95b99e1142941ed7ec23a4465822813Jesse Hall            "--------  ------  ------- -----  -------      ----  ----  ------    ----\n");
14702d86567d95b99e1142941ed7ec23a4465822813Jesse Hall    }
148ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall
149ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall    totalUncLen = totalCompLen = 0;
150ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall
151ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall    count = zip->getNumEntries();
152ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall    for (i = 0; i < count; i++) {
153ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall        entry = zip->getEntryByIndex(i);
154ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall        if (bundle->getVerbose()) {
155ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall            char dateBuf[32];
156ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall            time_t when;
157ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall
158ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall            when = entry->getModWhen();
159ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall            strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
160ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall                localtime(&when));
161ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall
162ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall            printf("%8ld  %-7.7s %7ld %3d%%  %8zd  %s  %08lx  %s\n",
163ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall                (long) entry->getUncompressedLen(),
164ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall                compressionName(entry->getCompressionMethod()),
165ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall                (long) entry->getCompressedLen(),
166ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall                calcPercent(entry->getUncompressedLen(),
167ffe1f19ca9707f84cb9fdb06209bf36cd8c2ef0aJesse Hall                            entry->getCompressedLen()),
1689e2463e71796964cfaa06bf09a880875ac3537bcMathias Agopian                (size_t) entry->getLFHOffset(),
1699e2463e71796964cfaa06bf09a880875ac3537bcMathias Agopian                dateBuf,
1709e2463e71796964cfaa06bf09a880875ac3537bcMathias Agopian                entry->getCRC32(),
1719e2463e71796964cfaa06bf09a880875ac3537bcMathias Agopian                entry->getFileName());
1729e2463e71796964cfaa06bf09a880875ac3537bcMathias Agopian        } else {
1739e2463e71796964cfaa06bf09a880875ac3537bcMathias Agopian            printf("%s\n", entry->getFileName());
1749e2463e71796964cfaa06bf09a880875ac3537bcMathias Agopian        }
1750f2f5ff75b7b48ceb64270655ee6b62d09bf4d00Mathias Agopian
176076b1cc3a9b90aa5b381a1ed268ca0b548444c9bMathias Agopian        totalUncLen += entry->getUncompressedLen();
177edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        totalCompLen += entry->getCompressedLen();
178edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    }
1790f2f5ff75b7b48ceb64270655ee6b62d09bf4d00Mathias Agopian
18099c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall    if (bundle->getVerbose()) {
18174faca212e2675aa55a30235c77cb6403471a4b9Mathias Agopian        printf(
182edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        "--------          -------  ---                            -------\n");
1830f2f5ff75b7b48ceb64270655ee6b62d09bf4d00Mathias Agopian        printf("%8ld          %7ld  %2d%%                            %d files\n",
184edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            totalUncLen,
185875d8e1323536e16dcfc90c9674d7ad32116a69aMathias Agopian            totalCompLen,
186edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            calcPercent(totalUncLen, totalCompLen),
187edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            zip->getNumEntries());
188edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    }
189edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
19001e29054e672301e4adbbca15b3562a59a20f267Jesse Hall    if (bundle->getAndroidList()) {
191df3ca30bf663cb8eed88ee3f16fb5e9a65dc00fcMathias Agopian        AssetManager assets;
192b8a5560e1303cb10f5cd482af466fc04d2bdfcabMathias Agopian        if (!assets.addAssetPath(String8(zipFileName), NULL)) {
193b8a5560e1303cb10f5cd482af466fc04d2bdfcabMathias Agopian            fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
194df3ca30bf663cb8eed88ee3f16fb5e9a65dc00fcMathias Agopian            goto bail;
195df3ca30bf663cb8eed88ee3f16fb5e9a65dc00fcMathias Agopian        }
19601e29054e672301e4adbbca15b3562a59a20f267Jesse Hall
197c701401f8cec2e5309f8b57e2b97baced5093274Dan Stoza        const ResTable& res = assets.getResources(false);
198c701401f8cec2e5309f8b57e2b97baced5093274Dan Stoza        if (&res == NULL) {
1995e78e0965169790111f01354e78b0f8d34c94840Mathias Agopian            printf("\nNo resource table found.\n");
200d870703d5566490cfdfb389d9336b2b8d3c6cc7aMathias Agopian        } else {
201076b1cc3a9b90aa5b381a1ed268ca0b548444c9bMathias Agopian#ifndef HAVE_ANDROID_OS
202edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            printf("\nResource table:\n");
203edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            res.print(false);
2047143316af216fa92c31a60d4407b707637382da1Dan Stoza#endif
2057143316af216fa92c31a60d4407b707637382da1Dan Stoza        }
206028dc8f2d72bc7cd4fbe7808781443125a742f78Jesse Hall
207028dc8f2d72bc7cd4fbe7808781443125a742f78Jesse Hall        Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
20838efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall                                                   Asset::ACCESS_BUFFER);
20938efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall        if (manifestAsset == NULL) {
21038efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall            printf("\nNo AndroidManifest.xml found.\n");
21138efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall        } else {
21238efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall            printf("\nAndroid manifest:\n");
21338efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall            ResXMLTree tree;
21438efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall            tree.setTo(manifestAsset->getBuffer(true),
21538efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall                       manifestAsset->getLength());
21638efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall            printXMLBlock(&tree);
21738efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall        }
21838efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall        delete manifestAsset;
21938efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall    }
22038efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall
22138efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall    result = 0;
22238efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall
22338efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hallbail:
22438efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall    delete zip;
22538efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall    return result;
22638efe86d9459cf5c96a24a34cc5cbf31fdba7e19Jesse Hall}
227da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian
22822a99f0fc2884227b9d7e46959fcbe6270667efbAndy McFaddenstatic ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
22922a99f0fc2884227b9d7e46959fcbe6270667efbAndy McFadden{
23022a99f0fc2884227b9d7e46959fcbe6270667efbAndy McFadden    size_t N = tree.getAttributeCount();
23122a99f0fc2884227b9d7e46959fcbe6270667efbAndy McFadden    for (size_t i=0; i<N; i++) {
23222a99f0fc2884227b9d7e46959fcbe6270667efbAndy McFadden        if (tree.getAttributeNameResID(i) == attrRes) {
23322a99f0fc2884227b9d7e46959fcbe6270667efbAndy McFadden            return (ssize_t)i;
23499c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall        }
23599c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall    }
23622a99f0fc2884227b9d7e46959fcbe6270667efbAndy McFadden    return -1;
23799c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall}
23899c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall
23999c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse HallString8 getAttribute(const ResXMLTree& tree, const char* ns,
24099c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall                            const char* attr, String8* outError)
24199c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall{
24299c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall    ssize_t idx = tree.indexOfAttribute(ns, attr);
24399c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall    if (idx < 0) {
24499c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall        return String8();
24599c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall    }
24699c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall    Res_value value;
247da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
248da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian        if (value.dataType != Res_value::TYPE_STRING) {
249da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian            if (outError != NULL) *outError = "attribute is not a string value";
25052e21483fa48baeb4a88372d75e083bca2e92923Mathias Agopian            return String8();
25199c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall        }
25299c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall    }
25399c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall    size_t len;
25499c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
25532341381c9493d7953e40f7f79653cfc52868863Mathias Agopian    return str ? String8(str, len) : String8();
256da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian}
257da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian
258da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopianstatic String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
259da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian{
260851cfe834295224cd64bdd499872b95b19c4de8cJesse Hall    ssize_t idx = indexOfAttribute(tree, attrRes);
261da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian    if (idx < 0) {
262da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian        return String8();
263da27af9832a0170f1fc40ef3f21371c4d30d21b3Mathias Agopian    }
2640f2f5ff75b7b48ceb64270655ee6b62d09bf4d00Mathias Agopian    Res_value value;
265edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
266edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        if (value.dataType != Res_value::TYPE_STRING) {
267edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            if (outError != NULL) *outError = "attribute is not a string value";
268edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            return String8();
269875d8e1323536e16dcfc90c9674d7ad32116a69aMathias Agopian        }
270da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian    }
27152bbb1ae239c8a4d05543a23fa8c08467d09c3b2Mathias Agopian    size_t len;
272875d8e1323536e16dcfc90c9674d7ad32116a69aMathias Agopian    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
273875d8e1323536e16dcfc90c9674d7ad32116a69aMathias Agopian    return str ? String8(str, len) : String8();
274da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian}
275f460f55c84b0a75de749b8a784059f5ff423ed93Jesse Hall
276f460f55c84b0a75de749b8a784059f5ff423ed93Jesse Hallstatic int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
277da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian        String8* outError, int32_t defValue = -1)
27852bbb1ae239c8a4d05543a23fa8c08467d09c3b2Mathias Agopian{
279931bda1c472ba8c8e965bdba6757ff94154df903Mathias Agopian    ssize_t idx = indexOfAttribute(tree, attrRes);
280da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian    if (idx < 0) {
28152bbb1ae239c8a4d05543a23fa8c08467d09c3b2Mathias Agopian        return defValue;
28252bbb1ae239c8a4d05543a23fa8c08467d09c3b2Mathias Agopian    }
283875d8e1323536e16dcfc90c9674d7ad32116a69aMathias Agopian    Res_value value;
284875d8e1323536e16dcfc90c9674d7ad32116a69aMathias Agopian    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
285875d8e1323536e16dcfc90c9674d7ad32116a69aMathias Agopian        if (value.dataType < Res_value::TYPE_FIRST_INT
286c18790018be5d7ea7061ccbc81f3044e74adc823Dan Stoza                || value.dataType > Res_value::TYPE_LAST_INT) {
287c18790018be5d7ea7061ccbc81f3044e74adc823Dan Stoza            if (outError != NULL) *outError = "attribute is not an integer value";
288bae92d0d605e99a14731add4f11b72413b2835e5Mathias Agopian            return defValue;
289bae92d0d605e99a14731add4f11b72413b2835e5Mathias Agopian        }
2901b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    }
2911b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    return value.data;
29213127d8921356dff794250e04208c3ed60b3a3dfMathias Agopian}
2933b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian
294ef7b9c7eac036cc1230c64821039d18f8cbd2c1cMathias Agopianstatic int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
2953b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian        uint32_t attrRes, String8* outError, int32_t defValue = -1)
2963b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian{
29713127d8921356dff794250e04208c3ed60b3a3dfMathias Agopian    ssize_t idx = indexOfAttribute(tree, attrRes);
298f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    if (idx < 0) {
2993b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian        return defValue;
3003b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian    }
3013b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian    Res_value value;
3023b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
3033b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian        if (value.dataType == Res_value::TYPE_REFERENCE) {
30413127d8921356dff794250e04208c3ed60b3a3dfMathias Agopian            resTable->resolveReference(&value, 0);
3053b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian        }
3063b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian        if (value.dataType < Res_value::TYPE_FIRST_INT
3073b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian                || value.dataType > Res_value::TYPE_LAST_INT) {
3080f2f5ff75b7b48ceb64270655ee6b62d09bf4d00Mathias Agopian            if (outError != NULL) *outError = "attribute is not an integer value";
3093b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian            return defValue;
3103b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian        }
3113b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian    }
312cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian    return value.data;
313cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian}
314cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian
315cd60f99aba9e750700a967db30b74a29145739cfMathias Agopianstatic String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
316cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian        uint32_t attrRes, String8* outError)
317da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian{
318cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian    ssize_t idx = indexOfAttribute(tree, attrRes);
319cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian    if (idx < 0) {
320cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian        return String8();
321cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian    }
322cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian    Res_value value;
323cd60f99aba9e750700a967db30b74a29145739cfMathias Agopian    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
3243b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopian        if (value.dataType == Res_value::TYPE_STRING) {
325d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian            size_t len;
326d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
327d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian            return str ? String8(str, len) : String8();
328d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian        }
329d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian        resTable->resolveReference(&value, 0);
330d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian        if (value.dataType != Res_value::TYPE_STRING) {
331d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian            if (outError != NULL) *outError = "attribute is not a string value";
332d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian            return String8();
333d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian        }
334d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian    }
335d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian    size_t len;
336d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian    const Res_value* value2 = &value;
337d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian    const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
338d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian    return str ? String8(str, len) : String8();
339d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian}
340d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian
341d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian// These are attribute resource constants for the platform, as found
342d3ee231eddce0b69ec5e35188dbd0f4a2c3b9ac3Mathias Agopian// in android.R.attr
3433b1d2b6b2bbfb5df46b1059ec52360974e6f1428Mathias Agopianenum {
34428947d7fbf9f486539322e8e12dd057568e180c2Mathias Agopian    LABEL_ATTR = 0x01010001,
34528947d7fbf9f486539322e8e12dd057568e180c2Mathias Agopian    ICON_ATTR = 0x01010002,
34628947d7fbf9f486539322e8e12dd057568e180c2Mathias Agopian    NAME_ATTR = 0x01010003,
34728947d7fbf9f486539322e8e12dd057568e180c2Mathias Agopian    VERSION_CODE_ATTR = 0x0101021b,
34828947d7fbf9f486539322e8e12dd057568e180c2Mathias Agopian    VERSION_NAME_ATTR = 0x0101021c,
34928947d7fbf9f486539322e8e12dd057568e180c2Mathias Agopian    SCREEN_ORIENTATION_ATTR = 0x0101001e,
35028947d7fbf9f486539322e8e12dd057568e180c2Mathias Agopian    MIN_SDK_VERSION_ATTR = 0x0101020c,
351c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    MAX_SDK_VERSION_ATTR = 0x01010271,
352c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    REQ_TOUCH_SCREEN_ATTR = 0x01010227,
353c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
354c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    REQ_HARD_KEYBOARD_ATTR = 0x01010229,
355c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    REQ_NAVIGATION_ATTR = 0x0101022a,
356c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
357c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    TARGET_SDK_VERSION_ATTR = 0x01010270,
358c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    TEST_ONLY_ATTR = 0x01010272,
359c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    ANY_DENSITY_ATTR = 0x0101026c,
360c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    GL_ES_VERSION_ATTR = 0x01010281,
361c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    SMALL_SCREEN_ATTR = 0x01010284,
362c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    NORMAL_SCREEN_ATTR = 0x01010285,
363c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    LARGE_SCREEN_ATTR = 0x01010286,
364c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    XLARGE_SCREEN_ATTR = 0x010102bf,
365c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    REQUIRED_ATTR = 0x0101028e,
366c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    SCREEN_SIZE_ATTR = 0x010102ca,
367c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    SCREEN_DENSITY_ATTR = 0x010102cb,
368c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
369c1c05de415854eb7a13a16b7e22a22de8515123aMathias Agopian    COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
3700f2f5ff75b7b48ceb64270655ee6b62d09bf4d00Mathias Agopian    LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
3711b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian};
3721b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian
3731b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopianconst char *getComponentName(String8 &pkgName, String8 &componentName) {
3741b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    ssize_t idx = componentName.find(".");
3753165cc21cfea781988407b19bd83292b19f05f55Mathias Agopian    String8 retStr(pkgName);
3761b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    if (idx == 0) {
3771b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian        retStr += componentName;
3783165cc21cfea781988407b19bd83292b19f05f55Mathias Agopian    } else if (idx < 0) {
3791b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian        retStr += ".";
3801b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian        retStr += componentName;
3813165cc21cfea781988407b19bd83292b19f05f55Mathias Agopian    } else {
3821b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian        return componentName.string();
3831b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    }
3843165cc21cfea781988407b19bd83292b19f05f55Mathias Agopian    return retStr.string();
3851b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian}
3861b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian
3871b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopianstatic void printCompatibleScreens(ResXMLTree& tree) {
3881b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    size_t len;
3891b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    ResXMLTree::event_code_t code;
3901b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    int depth = 0;
3911b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    bool first = true;
3921b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    printf("compatible-screens:");
3931b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
39400e8c7a88a5b9c4104a71013a713acd3e4d3b77bMathias Agopian        if (code == ResXMLTree::END_TAG) {
395f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian            depth--;
396f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian            if (depth < 0) {
397f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian                break;
398da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian            }
399f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian            continue;
400f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        }
401da8d0a5c0cf9d41915d3b106cad4aaec3e767c11Mathias Agopian        if (code != ResXMLTree::START_TAG) {
402f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian            continue;
403f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        }
4041b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian        depth++;
405f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        String8 tag(tree.getElementName(&len));
406f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        if (tag == "screen") {
407f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian            int32_t screenSize = getIntegerAttribute(tree,
408f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian                    SCREEN_SIZE_ATTR, NULL, -1);
409f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian            int32_t screenDensity = getIntegerAttribute(tree,
4106360ec42d414b1351ecb6c5fc4b8afa30d8f4ebfJesse Hall                    SCREEN_DENSITY_ATTR, NULL, -1);
411f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian            if (screenSize > 0 && screenDensity > 0) {
412f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian                if (!first) {
413f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian                    printf(",");
414f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian                }
415f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian                first = false;
416f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian                printf("'%d/%d'", screenSize, screenDensity);
417f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian            }
418f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        }
419f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    }
420f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    printf("\n");
421766dc49c17dda977bf7b93a5fd8da41c0b737611Mathias Agopian}
4221b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian
423f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian/*
424f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian * Handle the "dump" command, to extract select data from an archive.
425f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian */
426f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopianint doDump(Bundle* bundle)
427f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian{
428f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    status_t result = UNKNOWN_ERROR;
429f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    Asset* asset = NULL;
430f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian
431f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    if (bundle->getFileSpecCount() < 1) {
432f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        fprintf(stderr, "ERROR: no dump option specified\n");
433f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        return 1;
434f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    }
435f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian
436f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    if (bundle->getFileSpecCount() < 2) {
437f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        fprintf(stderr, "ERROR: no dump file specified\n");
438f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        return 1;
439f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    }
440f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian
441f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    const char* option = bundle->getFileSpecEntry(0);
442f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    const char* filename = bundle->getFileSpecEntry(1);
443f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian
444f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    AssetManager assets;
445f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    void* assetsCookie;
446f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
447f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
448f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian        return 1;
449f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    }
450f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian
451f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    // Make a dummy config for retrieving resources...  we need to supply
452f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    // non-default values for some configs so that we can retrieve resources
453f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    // in the app that don't have a default.  The most important of these is
454f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    // the API version because key resources like icons will have an implicit
4556c7f25afb75ac155bad0b3bc17c0089d0337d060Mathias Agopian    // version if they are using newer config types like density.
456f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    ResTable_config config;
457f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    config.language[0] = 'e';
458f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    config.language[1] = 'n';
459f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    config.country[0] = 'U';
460f5f714aa188884098aaecbe39d0bc61b40311c0dMathias Agopian    config.country[1] = 'S';
4611b03149f3533db04e72e088d3fdd09d0087ca594Mathias Agopian    config.orientation = ResTable_config::ORIENTATION_PORT;
4621d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian    config.density = ResTable_config::DENSITY_MEDIUM;
46374d211ae26a0257c6075a823812e40b55aa1e653Mathias Agopian    config.sdkVersion = 10000; // Very high.
4641d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian    config.screenWidthDp = 320;
46574d211ae26a0257c6075a823812e40b55aa1e653Mathias Agopian    config.screenHeightDp = 480;
4661d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian    config.smallestScreenWidthDp = 320;
46702d86567d95b99e1142941ed7ec23a4465822813Jesse Hall    assets.setConfiguration(config);
46886efcc0cbc1d94250b72ef1f2ea8700a04cd2781Greg Hackmann
469766dc49c17dda977bf7b93a5fd8da41c0b737611Mathias Agopian    const ResTable& res = assets.getResources(false);
4701d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian    if (&res == NULL) {
47102d86567d95b99e1142941ed7ec23a4465822813Jesse Hall        fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
4721d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian        goto bail;
4731d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian    }
474dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis
4751d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian    if (strcmp("resources", option) == 0) {
4761d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian#ifndef HAVE_ANDROID_OS
477766dc49c17dda977bf7b93a5fd8da41c0b737611Mathias Agopian        res.print(bundle->getValues());
4781d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian#endif
4791d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian    } else if (strcmp("xmltree", option) == 0) {
4801d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian        if (bundle->getFileSpecCount() < 3) {
4811d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
48299c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall            goto bail;
48399c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall        }
48499c7dbb24994df2f3e175f7b25dd2c9dd92a72f0Jesse Hall
4851d12d8a8e61163b35cf42c51c558a67138014e82Mathias Agopian        for (int i=2; i<bundle->getFileSpecCount(); i++) {
486            const char* resname = bundle->getFileSpecEntry(i);
487            ResXMLTree tree;
488            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
489            if (asset == NULL) {
490                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
491                goto bail;
492            }
493
494            if (tree.setTo(asset->getBuffer(true),
495                           asset->getLength()) != NO_ERROR) {
496                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
497                goto bail;
498            }
499            tree.restart();
500            printXMLBlock(&tree);
501            tree.uninit();
502            delete asset;
503            asset = NULL;
504        }
505
506    } else if (strcmp("xmlstrings", option) == 0) {
507        if (bundle->getFileSpecCount() < 3) {
508            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
509            goto bail;
510        }
511
512        for (int i=2; i<bundle->getFileSpecCount(); i++) {
513            const char* resname = bundle->getFileSpecEntry(i);
514            ResXMLTree tree;
515            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
516            if (asset == NULL) {
517                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
518                goto bail;
519            }
520
521            if (tree.setTo(asset->getBuffer(true),
522                           asset->getLength()) != NO_ERROR) {
523                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
524                goto bail;
525            }
526            printStringPool(&tree.getStrings());
527            delete asset;
528            asset = NULL;
529        }
530
531    } else {
532        ResXMLTree tree;
533        asset = assets.openNonAsset("AndroidManifest.xml",
534                                            Asset::ACCESS_BUFFER);
535        if (asset == NULL) {
536            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
537            goto bail;
538        }
539
540        if (tree.setTo(asset->getBuffer(true),
541                       asset->getLength()) != NO_ERROR) {
542            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
543            goto bail;
544        }
545        tree.restart();
546
547        if (strcmp("permissions", option) == 0) {
548            size_t len;
549            ResXMLTree::event_code_t code;
550            int depth = 0;
551            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
552                if (code == ResXMLTree::END_TAG) {
553                    depth--;
554                    continue;
555                }
556                if (code != ResXMLTree::START_TAG) {
557                    continue;
558                }
559                depth++;
560                String8 tag(tree.getElementName(&len));
561                //printf("Depth %d tag %s\n", depth, tag.string());
562                if (depth == 1) {
563                    if (tag != "manifest") {
564                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
565                        goto bail;
566                    }
567                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
568                    printf("package: %s\n", pkg.string());
569                } else if (depth == 2 && tag == "permission") {
570                    String8 error;
571                    String8 name = getAttribute(tree, NAME_ATTR, &error);
572                    if (error != "") {
573                        fprintf(stderr, "ERROR: %s\n", error.string());
574                        goto bail;
575                    }
576                    printf("permission: %s\n", name.string());
577                } else if (depth == 2 && tag == "uses-permission") {
578                    String8 error;
579                    String8 name = getAttribute(tree, NAME_ATTR, &error);
580                    if (error != "") {
581                        fprintf(stderr, "ERROR: %s\n", error.string());
582                        goto bail;
583                    }
584                    printf("uses-permission: %s\n", name.string());
585                }
586            }
587        } else if (strcmp("badging", option) == 0) {
588            Vector<String8> locales;
589            res.getLocales(&locales);
590
591            Vector<ResTable_config> configs;
592            res.getConfigurations(&configs);
593            SortedVector<int> densities;
594            const size_t NC = configs.size();
595            for (size_t i=0; i<NC; i++) {
596                int dens = configs[i].density;
597                if (dens == 0) dens = 160;
598                densities.add(dens);
599            }
600
601            size_t len;
602            ResXMLTree::event_code_t code;
603            int depth = 0;
604            String8 error;
605            bool withinActivity = false;
606            bool isMainActivity = false;
607            bool isLauncherActivity = false;
608            bool isSearchable = false;
609            bool withinApplication = false;
610            bool withinReceiver = false;
611            bool withinService = false;
612            bool withinIntentFilter = false;
613            bool hasMainActivity = false;
614            bool hasOtherActivities = false;
615            bool hasOtherReceivers = false;
616            bool hasOtherServices = false;
617            bool hasWallpaperService = false;
618            bool hasImeService = false;
619            bool hasWidgetReceivers = false;
620            bool hasIntentFilter = false;
621            bool actMainActivity = false;
622            bool actWidgetReceivers = false;
623            bool actImeService = false;
624            bool actWallpaperService = false;
625
626            // This next group of variables is used to implement a group of
627            // backward-compatibility heuristics necessitated by the addition of
628            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
629            // heuristic is "if an app requests a permission but doesn't explicitly
630            // request the corresponding <uses-feature>, presume it's there anyway".
631            bool specCameraFeature = false; // camera-related
632            bool specCameraAutofocusFeature = false;
633            bool reqCameraAutofocusFeature = false;
634            bool reqCameraFlashFeature = false;
635            bool hasCameraPermission = false;
636            bool specLocationFeature = false; // location-related
637            bool specNetworkLocFeature = false;
638            bool reqNetworkLocFeature = false;
639            bool specGpsFeature = false;
640            bool reqGpsFeature = false;
641            bool hasMockLocPermission = false;
642            bool hasCoarseLocPermission = false;
643            bool hasGpsPermission = false;
644            bool hasGeneralLocPermission = false;
645            bool specBluetoothFeature = false; // Bluetooth API-related
646            bool hasBluetoothPermission = false;
647            bool specMicrophoneFeature = false; // microphone-related
648            bool hasRecordAudioPermission = false;
649            bool specWiFiFeature = false;
650            bool hasWiFiPermission = false;
651            bool specTelephonyFeature = false; // telephony-related
652            bool reqTelephonySubFeature = false;
653            bool hasTelephonyPermission = false;
654            bool specTouchscreenFeature = false; // touchscreen-related
655            bool specMultitouchFeature = false;
656            bool reqDistinctMultitouchFeature = false;
657            bool specScreenPortraitFeature = false;
658            bool specScreenLandscapeFeature = false;
659            bool reqScreenPortraitFeature = false;
660            bool reqScreenLandscapeFeature = false;
661            // 2.2 also added some other features that apps can request, but that
662            // have no corresponding permission, so we cannot implement any
663            // back-compatibility heuristic for them. The below are thus unnecessary
664            // (but are retained here for documentary purposes.)
665            //bool specCompassFeature = false;
666            //bool specAccelerometerFeature = false;
667            //bool specProximityFeature = false;
668            //bool specAmbientLightFeature = false;
669            //bool specLiveWallpaperFeature = false;
670
671            int targetSdk = 0;
672            int smallScreen = 1;
673            int normalScreen = 1;
674            int largeScreen = 1;
675            int xlargeScreen = 1;
676            int anyDensity = 1;
677            int requiresSmallestWidthDp = 0;
678            int compatibleWidthLimitDp = 0;
679            int largestWidthLimitDp = 0;
680            String8 pkg;
681            String8 activityName;
682            String8 activityLabel;
683            String8 activityIcon;
684            String8 receiverName;
685            String8 serviceName;
686            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
687                if (code == ResXMLTree::END_TAG) {
688                    depth--;
689                    if (depth < 2) {
690                        withinApplication = false;
691                    } else if (depth < 3) {
692                        if (withinActivity && isMainActivity && isLauncherActivity) {
693                            const char *aName = getComponentName(pkg, activityName);
694                            printf("launchable-activity:");
695                            if (aName != NULL) {
696                                printf(" name='%s' ", aName);
697                            }
698                            printf(" label='%s' icon='%s'\n",
699                                    activityLabel.string(),
700                                    activityIcon.string());
701                        }
702                        if (!hasIntentFilter) {
703                            hasOtherActivities |= withinActivity;
704                            hasOtherReceivers |= withinReceiver;
705                            hasOtherServices |= withinService;
706                        }
707                        withinActivity = false;
708                        withinService = false;
709                        withinReceiver = false;
710                        hasIntentFilter = false;
711                        isMainActivity = isLauncherActivity = false;
712                    } else if (depth < 4) {
713                        if (withinIntentFilter) {
714                            if (withinActivity) {
715                                hasMainActivity |= actMainActivity;
716                                hasOtherActivities |= !actMainActivity;
717                            } else if (withinReceiver) {
718                                hasWidgetReceivers |= actWidgetReceivers;
719                                hasOtherReceivers |= !actWidgetReceivers;
720                            } else if (withinService) {
721                                hasImeService |= actImeService;
722                                hasWallpaperService |= actWallpaperService;
723                                hasOtherServices |= (!actImeService && !actWallpaperService);
724                            }
725                        }
726                        withinIntentFilter = false;
727                    }
728                    continue;
729                }
730                if (code != ResXMLTree::START_TAG) {
731                    continue;
732                }
733                depth++;
734                String8 tag(tree.getElementName(&len));
735                //printf("Depth %d,  %s\n", depth, tag.string());
736                if (depth == 1) {
737                    if (tag != "manifest") {
738                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
739                        goto bail;
740                    }
741                    pkg = getAttribute(tree, NULL, "package", NULL);
742                    printf("package: name='%s' ", pkg.string());
743                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
744                    if (error != "") {
745                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
746                        goto bail;
747                    }
748                    if (versionCode > 0) {
749                        printf("versionCode='%d' ", versionCode);
750                    } else {
751                        printf("versionCode='' ");
752                    }
753                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
754                    if (error != "") {
755                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
756                        goto bail;
757                    }
758                    printf("versionName='%s'\n", versionName.string());
759                } else if (depth == 2) {
760                    withinApplication = false;
761                    if (tag == "application") {
762                        withinApplication = true;
763
764                        String8 label;
765                        const size_t NL = locales.size();
766                        for (size_t i=0; i<NL; i++) {
767                            const char* localeStr =  locales[i].string();
768                            assets.setLocale(localeStr != NULL ? localeStr : "");
769                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
770                            if (llabel != "") {
771                                if (localeStr == NULL || strlen(localeStr) == 0) {
772                                    label = llabel;
773                                    printf("application-label:'%s'\n", llabel.string());
774                                } else {
775                                    if (label == "") {
776                                        label = llabel;
777                                    }
778                                    printf("application-label-%s:'%s'\n", localeStr,
779                                            llabel.string());
780                                }
781                            }
782                        }
783
784                        ResTable_config tmpConfig = config;
785                        const size_t ND = densities.size();
786                        for (size_t i=0; i<ND; i++) {
787                            tmpConfig.density = densities[i];
788                            assets.setConfiguration(tmpConfig);
789                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
790                            if (icon != "") {
791                                printf("application-icon-%d:'%s'\n", densities[i], icon.string());
792                            }
793                        }
794                        assets.setConfiguration(config);
795
796                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
797                        if (error != "") {
798                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
799                            goto bail;
800                        }
801                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
802                        if (error != "") {
803                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
804                            goto bail;
805                        }
806                        printf("application: label='%s' ", label.string());
807                        printf("icon='%s'\n", icon.string());
808                        if (testOnly != 0) {
809                            printf("testOnly='%d'\n", testOnly);
810                        }
811                    } else if (tag == "uses-sdk") {
812                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
813                        if (error != "") {
814                            error = "";
815                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
816                            if (error != "") {
817                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
818                                        error.string());
819                                goto bail;
820                            }
821                            if (name == "Donut") targetSdk = 4;
822                            printf("sdkVersion:'%s'\n", name.string());
823                        } else if (code != -1) {
824                            targetSdk = code;
825                            printf("sdkVersion:'%d'\n", code);
826                        }
827                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
828                        if (code != -1) {
829                            printf("maxSdkVersion:'%d'\n", code);
830                        }
831                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
832                        if (error != "") {
833                            error = "";
834                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
835                            if (error != "") {
836                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
837                                        error.string());
838                                goto bail;
839                            }
840                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
841                            printf("targetSdkVersion:'%s'\n", name.string());
842                        } else if (code != -1) {
843                            if (targetSdk < code) {
844                                targetSdk = code;
845                            }
846                            printf("targetSdkVersion:'%d'\n", code);
847                        }
848                    } else if (tag == "uses-configuration") {
849                        int32_t reqTouchScreen = getIntegerAttribute(tree,
850                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
851                        int32_t reqKeyboardType = getIntegerAttribute(tree,
852                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
853                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
854                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
855                        int32_t reqNavigation = getIntegerAttribute(tree,
856                                REQ_NAVIGATION_ATTR, NULL, 0);
857                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
858                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
859                        printf("uses-configuration:");
860                        if (reqTouchScreen != 0) {
861                            printf(" reqTouchScreen='%d'", reqTouchScreen);
862                        }
863                        if (reqKeyboardType != 0) {
864                            printf(" reqKeyboardType='%d'", reqKeyboardType);
865                        }
866                        if (reqHardKeyboard != 0) {
867                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
868                        }
869                        if (reqNavigation != 0) {
870                            printf(" reqNavigation='%d'", reqNavigation);
871                        }
872                        if (reqFiveWayNav != 0) {
873                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
874                        }
875                        printf("\n");
876                    } else if (tag == "supports-screens") {
877                        smallScreen = getIntegerAttribute(tree,
878                                SMALL_SCREEN_ATTR, NULL, 1);
879                        normalScreen = getIntegerAttribute(tree,
880                                NORMAL_SCREEN_ATTR, NULL, 1);
881                        largeScreen = getIntegerAttribute(tree,
882                                LARGE_SCREEN_ATTR, NULL, 1);
883                        xlargeScreen = getIntegerAttribute(tree,
884                                XLARGE_SCREEN_ATTR, NULL, 1);
885                        anyDensity = getIntegerAttribute(tree,
886                                ANY_DENSITY_ATTR, NULL, 1);
887                        requiresSmallestWidthDp = getIntegerAttribute(tree,
888                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
889                        compatibleWidthLimitDp = getIntegerAttribute(tree,
890                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
891                        largestWidthLimitDp = getIntegerAttribute(tree,
892                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
893                    } else if (tag == "uses-feature") {
894                        String8 name = getAttribute(tree, NAME_ATTR, &error);
895
896                        if (name != "" && error == "") {
897                            int req = getIntegerAttribute(tree,
898                                    REQUIRED_ATTR, NULL, 1);
899
900                            if (name == "android.hardware.camera") {
901                                specCameraFeature = true;
902                            } else if (name == "android.hardware.camera.autofocus") {
903                                // these have no corresponding permission to check for,
904                                // but should imply the foundational camera permission
905                                reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
906                                specCameraAutofocusFeature = true;
907                            } else if (req && (name == "android.hardware.camera.flash")) {
908                                // these have no corresponding permission to check for,
909                                // but should imply the foundational camera permission
910                                reqCameraFlashFeature = true;
911                            } else if (name == "android.hardware.location") {
912                                specLocationFeature = true;
913                            } else if (name == "android.hardware.location.network") {
914                                specNetworkLocFeature = true;
915                                reqNetworkLocFeature = reqNetworkLocFeature || req;
916                            } else if (name == "android.hardware.location.gps") {
917                                specGpsFeature = true;
918                                reqGpsFeature = reqGpsFeature || req;
919                            } else if (name == "android.hardware.bluetooth") {
920                                specBluetoothFeature = true;
921                            } else if (name == "android.hardware.touchscreen") {
922                                specTouchscreenFeature = true;
923                            } else if (name == "android.hardware.touchscreen.multitouch") {
924                                specMultitouchFeature = true;
925                            } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
926                                reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
927                            } else if (name == "android.hardware.microphone") {
928                                specMicrophoneFeature = true;
929                            } else if (name == "android.hardware.wifi") {
930                                specWiFiFeature = true;
931                            } else if (name == "android.hardware.telephony") {
932                                specTelephonyFeature = true;
933                            } else if (req && (name == "android.hardware.telephony.gsm" ||
934                                               name == "android.hardware.telephony.cdma")) {
935                                // these have no corresponding permission to check for,
936                                // but should imply the foundational telephony permission
937                                reqTelephonySubFeature = true;
938                            } else if (name == "android.hardware.screen.portrait") {
939                                specScreenPortraitFeature = true;
940                            } else if (name == "android.hardware.screen.landscape") {
941                                specScreenLandscapeFeature = true;
942                            }
943                            printf("uses-feature%s:'%s'\n",
944                                    req ? "" : "-not-required", name.string());
945                        } else {
946                            int vers = getIntegerAttribute(tree,
947                                    GL_ES_VERSION_ATTR, &error);
948                            if (error == "") {
949                                printf("uses-gl-es:'0x%x'\n", vers);
950                            }
951                        }
952                    } else if (tag == "uses-permission") {
953                        String8 name = getAttribute(tree, NAME_ATTR, &error);
954                        if (name != "" && error == "") {
955                            if (name == "android.permission.CAMERA") {
956                                hasCameraPermission = true;
957                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
958                                hasGpsPermission = true;
959                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
960                                hasMockLocPermission = true;
961                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
962                                hasCoarseLocPermission = true;
963                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
964                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
965                                hasGeneralLocPermission = true;
966                            } else if (name == "android.permission.BLUETOOTH" ||
967                                       name == "android.permission.BLUETOOTH_ADMIN") {
968                                hasBluetoothPermission = true;
969                            } else if (name == "android.permission.RECORD_AUDIO") {
970                                hasRecordAudioPermission = true;
971                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
972                                       name == "android.permission.CHANGE_WIFI_STATE" ||
973                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
974                                hasWiFiPermission = true;
975                            } else if (name == "android.permission.CALL_PHONE" ||
976                                       name == "android.permission.CALL_PRIVILEGED" ||
977                                       name == "android.permission.MODIFY_PHONE_STATE" ||
978                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
979                                       name == "android.permission.READ_SMS" ||
980                                       name == "android.permission.RECEIVE_SMS" ||
981                                       name == "android.permission.RECEIVE_MMS" ||
982                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
983                                       name == "android.permission.SEND_SMS" ||
984                                       name == "android.permission.WRITE_APN_SETTINGS" ||
985                                       name == "android.permission.WRITE_SMS") {
986                                hasTelephonyPermission = true;
987                            }
988                            printf("uses-permission:'%s'\n", name.string());
989                        } else {
990                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
991                                    error.string());
992                            goto bail;
993                        }
994                    } else if (tag == "uses-package") {
995                        String8 name = getAttribute(tree, NAME_ATTR, &error);
996                        if (name != "" && error == "") {
997                            printf("uses-package:'%s'\n", name.string());
998                        } else {
999                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1000                                    error.string());
1001                                goto bail;
1002                        }
1003                    } else if (tag == "original-package") {
1004                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1005                        if (name != "" && error == "") {
1006                            printf("original-package:'%s'\n", name.string());
1007                        } else {
1008                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1009                                    error.string());
1010                                goto bail;
1011                        }
1012                    } else if (tag == "supports-gl-texture") {
1013                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1014                        if (name != "" && error == "") {
1015                            printf("supports-gl-texture:'%s'\n", name.string());
1016                        } else {
1017                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1018                                    error.string());
1019                                goto bail;
1020                        }
1021                    } else if (tag == "compatible-screens") {
1022                        printCompatibleScreens(tree);
1023                        depth--;
1024                    }
1025                } else if (depth == 3 && withinApplication) {
1026                    withinActivity = false;
1027                    withinReceiver = false;
1028                    withinService = false;
1029                    hasIntentFilter = false;
1030                    if(tag == "activity") {
1031                        withinActivity = true;
1032                        activityName = getAttribute(tree, NAME_ATTR, &error);
1033                        if (error != "") {
1034                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1035                            goto bail;
1036                        }
1037
1038                        activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1039                        if (error != "") {
1040                            fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1041                            goto bail;
1042                        }
1043
1044                        activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1045                        if (error != "") {
1046                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1047                            goto bail;
1048                        }
1049
1050                        int32_t orien = getResolvedIntegerAttribute(&res, tree,
1051                                SCREEN_ORIENTATION_ATTR, &error);
1052                        if (error == "") {
1053                            if (orien == 0 || orien == 6 || orien == 8) {
1054                                // Requests landscape, sensorLandscape, or reverseLandscape.
1055                                reqScreenLandscapeFeature = true;
1056                            } else if (orien == 1 || orien == 7 || orien == 9) {
1057                                // Requests portrait, sensorPortrait, or reversePortrait.
1058                                reqScreenPortraitFeature = true;
1059                            }
1060                        }
1061                    } else if (tag == "uses-library") {
1062                        String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1063                        if (error != "") {
1064                            fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1065                            goto bail;
1066                        }
1067                        int req = getIntegerAttribute(tree,
1068                                REQUIRED_ATTR, NULL, 1);
1069                        printf("uses-library%s:'%s'\n",
1070                                req ? "" : "-not-required", libraryName.string());
1071                    } else if (tag == "receiver") {
1072                        withinReceiver = true;
1073                        receiverName = getAttribute(tree, NAME_ATTR, &error);
1074
1075                        if (error != "") {
1076                            fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1077                            goto bail;
1078                        }
1079                    } else if (tag == "service") {
1080                        withinService = true;
1081                        serviceName = getAttribute(tree, NAME_ATTR, &error);
1082
1083                        if (error != "") {
1084                            fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1085                            goto bail;
1086                        }
1087                    }
1088                } else if ((depth == 4) && (tag == "intent-filter")) {
1089                    hasIntentFilter = true;
1090                    withinIntentFilter = true;
1091                    actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1092                } else if ((depth == 5) && withinIntentFilter){
1093                    String8 action;
1094                    if (tag == "action") {
1095                        action = getAttribute(tree, NAME_ATTR, &error);
1096                        if (error != "") {
1097                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1098                            goto bail;
1099                        }
1100                        if (withinActivity) {
1101                            if (action == "android.intent.action.MAIN") {
1102                                isMainActivity = true;
1103                                actMainActivity = true;
1104                            }
1105                        } else if (withinReceiver) {
1106                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1107                                actWidgetReceivers = true;
1108                            }
1109                        } else if (withinService) {
1110                            if (action == "android.view.InputMethod") {
1111                                actImeService = true;
1112                            } else if (action == "android.service.wallpaper.WallpaperService") {
1113                                actWallpaperService = true;
1114                            }
1115                        }
1116                        if (action == "android.intent.action.SEARCH") {
1117                            isSearchable = true;
1118                        }
1119                    }
1120
1121                    if (tag == "category") {
1122                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1123                        if (error != "") {
1124                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1125                            goto bail;
1126                        }
1127                        if (withinActivity) {
1128                            if (category == "android.intent.category.LAUNCHER") {
1129                                isLauncherActivity = true;
1130                            }
1131                        }
1132                    }
1133                }
1134            }
1135
1136            /* The following blocks handle printing "inferred" uses-features, based
1137             * on whether related features or permissions are used by the app.
1138             * Note that the various spec*Feature variables denote whether the
1139             * relevant tag was *present* in the AndroidManfest, not that it was
1140             * present and set to true.
1141             */
1142            // Camera-related back-compatibility logic
1143            if (!specCameraFeature) {
1144                if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
1145                    // if app requested a sub-feature (autofocus or flash) and didn't
1146                    // request the base camera feature, we infer that it meant to
1147                    printf("uses-feature:'android.hardware.camera'\n");
1148                } else if (hasCameraPermission) {
1149                    // if app wants to use camera but didn't request the feature, we infer
1150                    // that it meant to, and further that it wants autofocus
1151                    // (which was the 1.0 - 1.5 behavior)
1152                    printf("uses-feature:'android.hardware.camera'\n");
1153                    if (!specCameraAutofocusFeature) {
1154                        printf("uses-feature:'android.hardware.camera.autofocus'\n");
1155                    }
1156                }
1157            }
1158
1159            // Location-related back-compatibility logic
1160            if (!specLocationFeature &&
1161                (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1162                 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1163                // if app either takes a location-related permission or requests one of the
1164                // sub-features, we infer that it also meant to request the base location feature
1165                printf("uses-feature:'android.hardware.location'\n");
1166            }
1167            if (!specGpsFeature && hasGpsPermission) {
1168                // if app takes GPS (FINE location) perm but does not request the GPS
1169                // feature, we infer that it meant to
1170                printf("uses-feature:'android.hardware.location.gps'\n");
1171            }
1172            if (!specNetworkLocFeature && hasCoarseLocPermission) {
1173                // if app takes Network location (COARSE location) perm but does not request the
1174                // network location feature, we infer that it meant to
1175                printf("uses-feature:'android.hardware.location.network'\n");
1176            }
1177
1178            // Bluetooth-related compatibility logic
1179            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1180                // if app takes a Bluetooth permission but does not request the Bluetooth
1181                // feature, we infer that it meant to
1182                printf("uses-feature:'android.hardware.bluetooth'\n");
1183            }
1184
1185            // Microphone-related compatibility logic
1186            if (!specMicrophoneFeature && hasRecordAudioPermission) {
1187                // if app takes the record-audio permission but does not request the microphone
1188                // feature, we infer that it meant to
1189                printf("uses-feature:'android.hardware.microphone'\n");
1190            }
1191
1192            // WiFi-related compatibility logic
1193            if (!specWiFiFeature && hasWiFiPermission) {
1194                // if app takes one of the WiFi permissions but does not request the WiFi
1195                // feature, we infer that it meant to
1196                printf("uses-feature:'android.hardware.wifi'\n");
1197            }
1198
1199            // Telephony-related compatibility logic
1200            if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1201                // if app takes one of the telephony permissions or requests a sub-feature but
1202                // does not request the base telephony feature, we infer that it meant to
1203                printf("uses-feature:'android.hardware.telephony'\n");
1204            }
1205
1206            // Touchscreen-related back-compatibility logic
1207            if (!specTouchscreenFeature) { // not a typo!
1208                // all apps are presumed to require a touchscreen, unless they explicitly say
1209                // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1210                // Note that specTouchscreenFeature is true if the tag is present, regardless
1211                // of whether its value is true or false, so this is safe
1212                printf("uses-feature:'android.hardware.touchscreen'\n");
1213            }
1214            if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1215                // if app takes one of the telephony permissions or requests a sub-feature but
1216                // does not request the base telephony feature, we infer that it meant to
1217                printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1218            }
1219
1220            // Landscape/portrait-related compatibility logic
1221            if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1222                // If the app has specified any activities in its manifest
1223                // that request a specific orientation, then assume that
1224                // orientation is required.
1225                if (reqScreenLandscapeFeature) {
1226                    printf("uses-feature:'android.hardware.screen.landscape'\n");
1227                }
1228                if (reqScreenPortraitFeature) {
1229                    printf("uses-feature:'android.hardware.screen.portrait'\n");
1230                }
1231            }
1232
1233            if (hasMainActivity) {
1234                printf("main\n");
1235            }
1236            if (hasWidgetReceivers) {
1237                printf("app-widget\n");
1238            }
1239            if (hasImeService) {
1240                printf("ime\n");
1241            }
1242            if (hasWallpaperService) {
1243                printf("wallpaper\n");
1244            }
1245            if (hasOtherActivities) {
1246                printf("other-activities\n");
1247            }
1248            if (isSearchable) {
1249                printf("search\n");
1250            }
1251            if (hasOtherReceivers) {
1252                printf("other-receivers\n");
1253            }
1254            if (hasOtherServices) {
1255                printf("other-services\n");
1256            }
1257
1258            // For modern apps, if screen size buckets haven't been specified
1259            // but the new width ranges have, then infer the buckets from them.
1260            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1261                    && requiresSmallestWidthDp > 0) {
1262                int compatWidth = compatibleWidthLimitDp;
1263                if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1264                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1265                    smallScreen = -1;
1266                } else {
1267                    smallScreen = 0;
1268                }
1269                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1270                    normalScreen = -1;
1271                } else {
1272                    normalScreen = 0;
1273                }
1274                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1275                    largeScreen = -1;
1276                } else {
1277                    largeScreen = 0;
1278                }
1279                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1280                    xlargeScreen = -1;
1281                } else {
1282                    xlargeScreen = 0;
1283                }
1284            }
1285
1286            // Determine default values for any unspecified screen sizes,
1287            // based on the target SDK of the package.  As of 4 (donut)
1288            // the screen size support was introduced, so all default to
1289            // enabled.
1290            if (smallScreen > 0) {
1291                smallScreen = targetSdk >= 4 ? -1 : 0;
1292            }
1293            if (normalScreen > 0) {
1294                normalScreen = -1;
1295            }
1296            if (largeScreen > 0) {
1297                largeScreen = targetSdk >= 4 ? -1 : 0;
1298            }
1299            if (xlargeScreen > 0) {
1300                // Introduced in Gingerbread.
1301                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1302            }
1303            if (anyDensity > 0) {
1304                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1305                        || compatibleWidthLimitDp > 0) ? -1 : 0;
1306            }
1307            printf("supports-screens:");
1308            if (smallScreen != 0) printf(" 'small'");
1309            if (normalScreen != 0) printf(" 'normal'");
1310            if (largeScreen != 0) printf(" 'large'");
1311            if (xlargeScreen != 0) printf(" 'xlarge'");
1312            printf("\n");
1313            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1314            if (requiresSmallestWidthDp > 0) {
1315                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1316            }
1317            if (compatibleWidthLimitDp > 0) {
1318                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1319            }
1320            if (largestWidthLimitDp > 0) {
1321                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1322            }
1323
1324            printf("locales:");
1325            const size_t NL = locales.size();
1326            for (size_t i=0; i<NL; i++) {
1327                const char* localeStr =  locales[i].string();
1328                if (localeStr == NULL || strlen(localeStr) == 0) {
1329                    localeStr = "--_--";
1330                }
1331                printf(" '%s'", localeStr);
1332            }
1333            printf("\n");
1334
1335            printf("densities:");
1336            const size_t ND = densities.size();
1337            for (size_t i=0; i<ND; i++) {
1338                printf(" '%d'", densities[i]);
1339            }
1340            printf("\n");
1341
1342            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1343            if (dir != NULL) {
1344                if (dir->getFileCount() > 0) {
1345                    printf("native-code:");
1346                    for (size_t i=0; i<dir->getFileCount(); i++) {
1347                        printf(" '%s'", dir->getFileName(i).string());
1348                    }
1349                    printf("\n");
1350                }
1351                delete dir;
1352            }
1353        } else if (strcmp("configurations", option) == 0) {
1354            Vector<ResTable_config> configs;
1355            res.getConfigurations(&configs);
1356            const size_t N = configs.size();
1357            for (size_t i=0; i<N; i++) {
1358                printf("%s\n", configs[i].toString().string());
1359            }
1360        } else {
1361            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1362            goto bail;
1363        }
1364    }
1365
1366    result = NO_ERROR;
1367
1368bail:
1369    if (asset) {
1370        delete asset;
1371    }
1372    return (result != NO_ERROR);
1373}
1374
1375
1376/*
1377 * Handle the "add" command, which wants to add files to a new or
1378 * pre-existing archive.
1379 */
1380int doAdd(Bundle* bundle)
1381{
1382    ZipFile* zip = NULL;
1383    status_t result = UNKNOWN_ERROR;
1384    const char* zipFileName;
1385
1386    if (bundle->getUpdate()) {
1387        /* avoid confusion */
1388        fprintf(stderr, "ERROR: can't use '-u' with add\n");
1389        goto bail;
1390    }
1391
1392    if (bundle->getFileSpecCount() < 1) {
1393        fprintf(stderr, "ERROR: must specify zip file name\n");
1394        goto bail;
1395    }
1396    zipFileName = bundle->getFileSpecEntry(0);
1397
1398    if (bundle->getFileSpecCount() < 2) {
1399        fprintf(stderr, "NOTE: nothing to do\n");
1400        goto bail;
1401    }
1402
1403    zip = openReadWrite(zipFileName, true);
1404    if (zip == NULL) {
1405        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1406        goto bail;
1407    }
1408
1409    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1410        const char* fileName = bundle->getFileSpecEntry(i);
1411
1412        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1413            printf(" '%s'... (from gzip)\n", fileName);
1414            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1415        } else {
1416            if (bundle->getJunkPath()) {
1417                String8 storageName = String8(fileName).getPathLeaf();
1418                printf(" '%s' as '%s'...\n", fileName, storageName.string());
1419                result = zip->add(fileName, storageName.string(),
1420                                  bundle->getCompressionMethod(), NULL);
1421            } else {
1422                printf(" '%s'...\n", fileName);
1423                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1424            }
1425        }
1426        if (result != NO_ERROR) {
1427            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1428            if (result == NAME_NOT_FOUND)
1429                fprintf(stderr, ": file not found\n");
1430            else if (result == ALREADY_EXISTS)
1431                fprintf(stderr, ": already exists in archive\n");
1432            else
1433                fprintf(stderr, "\n");
1434            goto bail;
1435        }
1436    }
1437
1438    result = NO_ERROR;
1439
1440bail:
1441    delete zip;
1442    return (result != NO_ERROR);
1443}
1444
1445
1446/*
1447 * Delete files from an existing archive.
1448 */
1449int doRemove(Bundle* bundle)
1450{
1451    ZipFile* zip = NULL;
1452    status_t result = UNKNOWN_ERROR;
1453    const char* zipFileName;
1454
1455    if (bundle->getFileSpecCount() < 1) {
1456        fprintf(stderr, "ERROR: must specify zip file name\n");
1457        goto bail;
1458    }
1459    zipFileName = bundle->getFileSpecEntry(0);
1460
1461    if (bundle->getFileSpecCount() < 2) {
1462        fprintf(stderr, "NOTE: nothing to do\n");
1463        goto bail;
1464    }
1465
1466    zip = openReadWrite(zipFileName, false);
1467    if (zip == NULL) {
1468        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1469            zipFileName);
1470        goto bail;
1471    }
1472
1473    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1474        const char* fileName = bundle->getFileSpecEntry(i);
1475        ZipEntry* entry;
1476
1477        entry = zip->getEntryByName(fileName);
1478        if (entry == NULL) {
1479            printf(" '%s' NOT FOUND\n", fileName);
1480            continue;
1481        }
1482
1483        result = zip->remove(entry);
1484
1485        if (result != NO_ERROR) {
1486            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1487                bundle->getFileSpecEntry(i), zipFileName);
1488            goto bail;
1489        }
1490    }
1491
1492    /* update the archive */
1493    zip->flush();
1494
1495bail:
1496    delete zip;
1497    return (result != NO_ERROR);
1498}
1499
1500
1501/*
1502 * Package up an asset directory and associated application files.
1503 */
1504int doPackage(Bundle* bundle)
1505{
1506    const char* outputAPKFile;
1507    int retVal = 1;
1508    status_t err;
1509    sp<AaptAssets> assets;
1510    int N;
1511    FILE* fp;
1512    String8 dependencyFile;
1513
1514    // -c zz_ZZ means do pseudolocalization
1515    ResourceFilter filter;
1516    err = filter.parse(bundle->getConfigurations());
1517    if (err != NO_ERROR) {
1518        goto bail;
1519    }
1520    if (filter.containsPseudo()) {
1521        bundle->setPseudolocalize(true);
1522    }
1523
1524    N = bundle->getFileSpecCount();
1525    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1526            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1527        fprintf(stderr, "ERROR: no input files\n");
1528        goto bail;
1529    }
1530
1531    outputAPKFile = bundle->getOutputAPKFile();
1532
1533    // Make sure the filenames provided exist and are of the appropriate type.
1534    if (outputAPKFile) {
1535        FileType type;
1536        type = getFileType(outputAPKFile);
1537        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1538            fprintf(stderr,
1539                "ERROR: output file '%s' exists but is not regular file\n",
1540                outputAPKFile);
1541            goto bail;
1542        }
1543    }
1544
1545    // Load the assets.
1546    assets = new AaptAssets();
1547
1548    // Set up the resource gathering in assets if we're going to generate
1549    // dependency files
1550    if (bundle->getGenDependencies()) {
1551        sp<FilePathStore> resPathStore = new FilePathStore;
1552        assets->setFullResPaths(resPathStore);
1553        sp<FilePathStore> assetPathStore = new FilePathStore;
1554        assets->setFullAssetPaths(assetPathStore);
1555    }
1556
1557    err = assets->slurpFromArgs(bundle);
1558    if (err < 0) {
1559        goto bail;
1560    }
1561
1562    if (bundle->getVerbose()) {
1563        assets->print();
1564    }
1565
1566    // If they asked for any fileAs that need to be compiled, do so.
1567    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1568        err = buildResources(bundle, assets);
1569        if (err != 0) {
1570            goto bail;
1571        }
1572    }
1573
1574    // At this point we've read everything and processed everything.  From here
1575    // on out it's just writing output files.
1576    if (SourcePos::hasErrors()) {
1577        goto bail;
1578    }
1579
1580    if (bundle->getGenDependencies()) {
1581        if (outputAPKFile) {
1582            dependencyFile = String8(outputAPKFile);
1583            // Strip the extension and add new one
1584            dependencyFile = dependencyFile.getBasePath();
1585            dependencyFile.append(".d");
1586        } else {
1587            dependencyFile = String8(bundle->getRClassDir());
1588            dependencyFile.appendPath("R.d");
1589        }
1590        // Make sure we have a clean dependency file to start with
1591        fp = fopen(dependencyFile, "w");
1592        fclose(fp);
1593    }
1594
1595    // Write out R.java constants
1596    if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
1597        if (bundle->getCustomPackage() == NULL) {
1598            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1599            // Copy R.java for libraries
1600            if (bundle->getExtraPackages() != NULL) {
1601                // Split on colon
1602                String8 libs(bundle->getExtraPackages());
1603                char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1604                while (packageString != NULL) {
1605                    err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1606                    packageString = strtok(NULL, ":");
1607                }
1608                libs.unlockBuffer();
1609            }
1610        } else {
1611            const String8 customPkg(bundle->getCustomPackage());
1612            err = writeResourceSymbols(bundle, assets, customPkg, true);
1613        }
1614        if (err < 0) {
1615            goto bail;
1616        }
1617    } else {
1618        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1619        if (err < 0) {
1620            goto bail;
1621        }
1622        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1623        if (err < 0) {
1624            goto bail;
1625        }
1626    }
1627
1628    // Write out the ProGuard file
1629    err = writeProguardFile(bundle, assets);
1630    if (err < 0) {
1631        goto bail;
1632    }
1633
1634    // Write the apk
1635    if (outputAPKFile) {
1636        err = writeAPK(bundle, assets, String8(outputAPKFile));
1637        if (err != NO_ERROR) {
1638            fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1639            goto bail;
1640        }
1641    }
1642
1643    if (bundle->getGenDependencies()) {
1644        // Now that writeResourceSymbols or writeAPK has taken care of writing
1645        // the targets to our dependency file, we'll write the prereqs
1646        fp = fopen(dependencyFile, "a+");
1647        fprintf(fp, " : ");
1648        bool includeRaw = (outputAPKFile != NULL);
1649        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1650        // Also manually add the AndroidManifeset since it's a non-asset
1651        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1652        fclose(fp);
1653    }
1654
1655    retVal = 0;
1656bail:
1657    if (SourcePos::hasErrors()) {
1658        SourcePos::printErrors(stderr);
1659    }
1660    return retVal;
1661}
1662