Command.cpp revision c2dea8daea2ae0001d56689d96ce1066012b7b40
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 "ApkBuilder.h"
7edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "Bundle.h"
8edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "Images.h"
9edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "Main.h"
10edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "ResourceFilter.h"
11edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "ResourceTable.h"
12edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "XMLNode.h"
13edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
14edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/Errors.h>
15edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/KeyedVector.h>
16edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/List.h>
17edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/Log.h>
18edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/SortedVector.h>
19edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/threads.h>
20edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utils/Vector.h>
21edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
22edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <errno.h>
23c5b2c0bf8007562536b822eb060fc54a01f8e08bMathias Agopian#include <fcntl.h>
24c5b2c0bf8007562536b822eb060fc54a01f8e08bMathias Agopian
25c5b2c0bf8007562536b822eb060fc54a01f8e08bMathias Agopianusing namespace android;
26c5b2c0bf8007562536b822eb060fc54a01f8e08bMathias Agopian
27edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project/*
28d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian * Show version info.  All the cool kids do it.
29d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian */
3090ac799241f077a7b7e6c1875fd933864c8dd2a7Mathias Agopianint doVersion(Bundle* bundle)
312adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden{
32d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian    if (bundle->getFileSpecCount() != 0) {
3390ac799241f077a7b7e6c1875fd933864c8dd2a7Mathias Agopian        printf("(ignoring extra arguments)\n");
34edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    }
3590ac799241f077a7b7e6c1875fd933864c8dd2a7Mathias Agopian    printf("Android Asset Packaging Tool, v0.2\n");
3667d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar
37582270d69db94286a248bd829f1ae6f910d45124Jamie Gennis    return 0;
38134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis}
399cce325fae8adcf7560a28eef394489f09bad74dMathias Agopian
40edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
41edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project/*
42edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * Open the file read only.  The call fails if the file doesn't exist.
43edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project *
44d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian * Returns NULL on failure.
45d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian */
46edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source ProjectZipFile* openReadOnly(const char* fileName)
47edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project{
48edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    ZipFile* zip;
49edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    status_t result;
50edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
51edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    zip = new ZipFile;
52edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    result = zip->open(fileName, ZipFile::kOpenReadOnly);
53edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    if (result != NO_ERROR) {
547e27f05739c8a2655cf0f7faea35614ce0a50278Mathias Agopian        if (result == NAME_NOT_FOUND) {
55edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            fprintf(stderr, "ERROR: '%s' not found\n", fileName);
56edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        } else if (result == PERMISSION_DENIED) {
57edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
58edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        } else {
59edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
607e27f05739c8a2655cf0f7faea35614ce0a50278Mathias Agopian                fileName);
61edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        }
62edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        delete zip;
639a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis        return NULL;
649a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis    }
659a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis
669a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis    return zip;
679a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis}
689a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis
699a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis/*
709a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis * Open the file read-write.  The file will be created if it doesn't
719a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis * already exist and "okayToCreate" is set.
728b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian *
738b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian * Returns NULL on failure.
748b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian */
758b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias AgopianZipFile* openReadWrite(const char* fileName, bool okayToCreate)
76edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project{
77edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    ZipFile* zip = NULL;
78edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    status_t result;
798b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian    int flags;
808b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian
818b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian    flags = ZipFile::kOpenReadWrite;
828b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian    if (okayToCreate) {
838b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian        flags |= ZipFile::kOpenCreate;
848b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian    }
858b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian
868b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian    zip = new ZipFile;
878b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian    result = zip->open(fileName, flags);
888b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian    if (result != NO_ERROR) {
898b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian        delete zip;
908b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian        zip = NULL;
918b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian        goto bail;
928b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian    }
938b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian
94698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopianbail:
9528378392fd5aa3e0a392c9eb64634055678c3987Jamie Gennis    return zip;
96b8d69a55f1c187a35ac41e69de63251f5501b6f4Jamie Gennis}
97edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
98edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
99edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project/*
100edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * Return a short string describing the compression method.
101edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project */
102edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectconst char* compressionName(int method)
103edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project{
104edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    if (method == ZipEntry::kCompressStored) {
105edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        return "Stored";
1062a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian    } else if (method == ZipEntry::kCompressDeflated) {
1072a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian        return "Deflated";
108c18790018be5d7ea7061ccbc81f3044e74adc823Dan Stoza    } else {
109c701401f8cec2e5309f8b57e2b97baced5093274Dan Stoza        return "Unknown";
110c3ebe66b49cfba035e1fd0e160a13db38eb81b0eRiley Andrews    }
111c3ebe66b49cfba035e1fd0e160a13db38eb81b0eRiley Andrews}
1122a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian
1132a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian/*
1142a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian * Return the percent reduction in size (0% == no compression).
1152a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian */
1162a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopianint calcPercent(long uncompressedLen, long compressedLen)
117c18790018be5d7ea7061ccbc81f3044e74adc823Dan Stoza{
1182a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian    if (!uncompressedLen) {
1192a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian        return 0;
1202a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian    } else {
1212a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian        return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
122c701401f8cec2e5309f8b57e2b97baced5093274Dan Stoza    }
123c3ebe66b49cfba035e1fd0e160a13db38eb81b0eRiley Andrews}
1242a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian
1252a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian/*
1262a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian * Handle the "list" command, which can be a simple file dump or
1272a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian * a verbose listing.
128582270d69db94286a248bd829f1ae6f910d45124Jamie Gennis *
1292adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden * The verbose listing closely matches the output of the Info-ZIP "unzip"
130134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis * command.
131134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis */
132134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennisint doList(Bundle* bundle)
133134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis{
134134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    int result = 1;
135134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    ZipFile* zip = NULL;
136e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block    const ZipEntry* entry;
137134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    long totalUncLen, totalCompLen;
138134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    const char* zipFileName;
139134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis
1402adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden    if (bundle->getFileSpecCount() != 1) {
141134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis        fprintf(stderr, "ERROR: specify zip file name (only)\n");
142e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block        goto bail;
143582270d69db94286a248bd829f1ae6f910d45124Jamie Gennis    }
144134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    zipFileName = bundle->getFileSpecEntry(0);
145134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis
146134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    zip = openReadOnly(zipFileName);
147134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    if (zip == NULL) {
148134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis        goto bail;
149e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block    }
150582270d69db94286a248bd829f1ae6f910d45124Jamie Gennis
151134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    int count, i;
152134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis
153134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    if (bundle->getVerbose()) {
154134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis        printf("Archive:  %s\n", zipFileName);
155134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis        printf(
156e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block            " Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name\n");
157582270d69db94286a248bd829f1ae6f910d45124Jamie Gennis        printf(
158134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis            "--------  ------  ------- -----  -------      ----  ----  ------    ----\n");
159134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    }
160134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis
161134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    totalUncLen = totalCompLen = 0;
162d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian
163d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian    count = zip->getNumEntries();
164d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian    for (i = 0; i < count; i++) {
165d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian        entry = zip->getEntryByIndex(i);
166d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian        if (bundle->getVerbose()) {
167d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian            char dateBuf[32];
168d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian            time_t when;
169d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian
170d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian            when = entry->getModWhen();
171d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian            strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
172d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian                localtime(&when));
173d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian
174d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian            printf("%8ld  %-7.7s %7ld %3d%%  %8zd  %s  %08lx  %s\n",
175d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian                (long) entry->getUncompressedLen(),
176e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block                compressionName(entry->getCompressionMethod()),
177d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian                (long) entry->getCompressedLen(),
178d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian                calcPercent(entry->getUncompressedLen(),
179d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian                            entry->getCompressedLen()),
180d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian                (size_t) entry->getLFHOffset(),
181d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian                dateBuf,
182d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian                entry->getCRC32(),
1838e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross                entry->getFileName());
184dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis        } else {
185e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian            printf("%s\n", entry->getFileName());
186e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        }
187e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian
1888dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden        totalUncLen += entry->getUncompressedLen();
189dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis        totalCompLen += entry->getCompressedLen();
190e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian    }
191e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian
192e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian    if (bundle->getVerbose()) {
193e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        printf(
1946c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall        "--------          -------  ---                            -------\n");
1956c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall        printf("%8ld          %7ld  %2d%%                            %d files\n",
1966c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall            totalUncLen,
1976c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall            totalCompLen,
1986c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall            calcPercent(totalUncLen, totalCompLen),
1996c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall            zip->getNumEntries());
2006c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    }
2016c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall
202e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian    if (bundle->getAndroidList()) {
203e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        AssetManager assets;
204e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        if (!assets.addAssetPath(String8(zipFileName), NULL)) {
205e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian            fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
206e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian            goto bail;
207e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        }
208e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian
209e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        const ResTable& res = assets.getResources(false);
210e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        if (&res == NULL) {
2112c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani            printf("\nNo resource table found.\n");
2128e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross        } else {
2138e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross#ifndef HAVE_ANDROID_OS
2148e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross            printf("\nResource table:\n");
215c01a79d77b829e7de86ef137694e8ad708470ca1Andy McFadden            res.print(false);
2162c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani#endif
2172c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani        }
2188e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross
2193094df359d1e6e2ae8ca4e935cc093f563804c96Mathias Agopian        Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
2207f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza                                                   Asset::ACCESS_BUFFER);
2217f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza        if (manifestAsset == NULL) {
222c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian            printf("\nNo AndroidManifest.xml found.\n");
223c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian        } else {
224c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian            printf("\nAndroid manifest:\n");
2259d4e3d2f42e93e2d12bacabe97d307d30c3c20ddJeff Brown            ResXMLTree tree;
2267f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza            tree.setTo(manifestAsset->getBuffer(true),
2277f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza                       manifestAsset->getLength());
2287f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza            printXMLBlock(&tree);
2297f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza        }
2307f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza        delete manifestAsset;
2317f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    }
2327f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza
2337f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    result = 0;
2347f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza
2357f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stozabail:
2367f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    delete zip;
2377f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    return result;
2387f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza}
2397f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza
2407f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stozastatic ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
24167d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar{
24267d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    size_t N = tree.getAttributeCount();
24367d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    for (size_t i=0; i<N; i++) {
24467d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar        if (tree.getAttributeNameResID(i) == attrRes) {
24567d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar            return (ssize_t)i;
24667d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar        }
24767d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    }
24867d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    return -1;
24967d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar}
25067d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar
25167d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos MolnarString8 getAttribute(const ResXMLTree& tree, const char* ns,
25267d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar                            const char* attr, String8* outError)
25367d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar{
25467d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    ssize_t idx = tree.indexOfAttribute(ns, attr);
25567d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    if (idx < 0) {
25667d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar        return String8();
2577f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    }
2587f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    Res_value value;
2597f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
2607f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza        if (value.dataType != Res_value::TYPE_STRING) {
2617f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza            if (outError != NULL) {
2627f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza                *outError = "attribute is not a string value";
2637f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza            }
2647f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza            return String8();
2657f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza        }
2667f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    }
2677f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    size_t len;
2687f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
2697f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    return str ? String8(str, len) : String8();
2707f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza}
2717f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza
2727f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stozastatic String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
273c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian{
274c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian    ssize_t idx = indexOfAttribute(tree, attrRes);
275d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    if (idx < 0) {
276d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav        return String8();
277d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    }
278d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    Res_value value;
279d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
280d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav        if (value.dataType != Res_value::TYPE_STRING) {
281d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav            if (outError != NULL) {
282d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav                *outError = "attribute is not a string value";
283d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav            }
284d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav            return String8();
285d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav        }
286d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    }
287d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    size_t len;
288d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
289d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    return str ? String8(str, len) : String8();
290edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project}
291edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
292edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectstatic int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
293edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        String8* outError, int32_t defValue = -1)
294edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project{
295edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    ssize_t idx = indexOfAttribute(tree, attrRes);
296edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    if (idx < 0) {
297edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        return defValue;
298edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    }
299edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    Res_value value;
300edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
30183c0446f27b9542d6c2e724817b2b2d8d1f55085Mathias Agopian        if (value.dataType < Res_value::TYPE_FIRST_INT
302edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project                || value.dataType > Res_value::TYPE_LAST_INT) {
303edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project            if (outError != NULL) {
3046c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall                *outError = "attribute is not an integer value";
3056c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall            }
3069a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis            return defValue;
3079a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis        }
3089a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis    }
3099a78c90cd46b2a3bd637b056873149d3b94384b4Jamie Gennis    return value.data;
3106c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall}
3116c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall
312698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopianstatic int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
31383c0446f27b9542d6c2e724817b2b2d8d1f55085Mathias Agopian        uint32_t attrRes, String8* outError, int32_t defValue = -1)
314698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian{
315698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian    ssize_t idx = indexOfAttribute(tree, attrRes);
316698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian    if (idx < 0) {
317698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian        return defValue;
318698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian    }
319698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian    Res_value value;
320698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
321698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian        if (value.dataType == Res_value::TYPE_REFERENCE) {
3228b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian            resTable->resolveReference(&value, 0);
3238b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian        }
3248b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian        if (value.dataType < Res_value::TYPE_FIRST_INT
3258b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian                || value.dataType > Res_value::TYPE_LAST_INT) {
3268b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian            if (outError != NULL) {
3278b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian                *outError = "attribute is not an integer value";
3288b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian            }
3298b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian            return defValue;
33028378392fd5aa3e0a392c9eb64634055678c3987Jamie Gennis        }
3318b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian    }
3326c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    return value.data;
3336c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall}
334edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project
33583c0446f27b9542d6c2e724817b2b2d8d1f55085Mathias Agopianstatic String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
336edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        uint32_t attrRes, String8* outError)
3376c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall{
3386c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    ssize_t idx = indexOfAttribute(tree, attrRes);
3392a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian    if (idx < 0) {
3402a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian        return String8();
3412a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian    }
3422a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian    Res_value value;
3432a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
344c18790018be5d7ea7061ccbc81f3044e74adc823Dan Stoza        if (value.dataType == Res_value::TYPE_STRING) {
345c18790018be5d7ea7061ccbc81f3044e74adc823Dan Stoza            size_t len;
3462a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
3472a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian            return str ? String8(str, len) : String8();
3482a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian        }
3492a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian        resTable->resolveReference(&value, 0);
350c701401f8cec2e5309f8b57e2b97baced5093274Dan Stoza        if (value.dataType != Res_value::TYPE_STRING) {
351c3ebe66b49cfba035e1fd0e160a13db38eb81b0eRiley Andrews            if (outError != NULL) {
352c701401f8cec2e5309f8b57e2b97baced5093274Dan Stoza                *outError = "attribute is not a string value";
3532a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian            }
354c18790018be5d7ea7061ccbc81f3044e74adc823Dan Stoza            return String8();
355c3ebe66b49cfba035e1fd0e160a13db38eb81b0eRiley Andrews        }
356c3ebe66b49cfba035e1fd0e160a13db38eb81b0eRiley Andrews    }
3572a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian    size_t len;
3586c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    const Res_value* value2 = &value;
3596c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
360134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis    return str ? String8(str, len) : String8();
361134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis}
3622adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden
3632adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFaddenstatic void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
3642adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden        const ResXMLTree& tree, uint32_t attrRes, String8* outError)
365134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis{
3666c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    ssize_t idx = indexOfAttribute(tree, attrRes);
3676c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    if (idx < 0) {
368d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian        if (outError != NULL) {
369d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian            *outError = "attribute could not be found";
370d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian        }
371d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian        return;
372d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian    }
3736c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    if (tree.getAttributeValue(idx, value) != NO_ERROR) {
374e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        if (value->dataType == Res_value::TYPE_REFERENCE) {
375e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian            resTable->resolveReference(value, 0);
3768dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden        }
377dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis        // The attribute was found and was resolved if need be.
378dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis        return;
379e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian    }
380e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian    if (outError != NULL) {
3816c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall        *outError = "error getting resolved resource attribute";
3826c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    }
3836c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall}
3846c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall
3856c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hallstatic void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
3866c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall        uint32_t attrRes, String8 attrLabel, String8* outError)
3876c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall{
388e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian    Res_value value;
389e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian    getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
390e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian    if (*outError != "") {
391e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        *outError = "error print resolved resource attribute";
392e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian        return;
393e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian    }
3946c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    if (value.dataType == Res_value::TYPE_STRING) {
3957f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza        String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
3967f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza        printf("%s='%s'", attrLabel.string(),
3977f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza                ResTable::normalizeForOutput(result.string()).string());
3987f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
3997f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza            value.dataType <= Res_value::TYPE_LAST_INT) {
4007f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza        printf("%s='%d'", attrLabel.string(), value.data);
4017f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    } else {
4027f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza        printf("%s='0x%x'", attrLabel.string(), (int)value.data);
4037f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    }
4047f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza}
4057f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza
4067f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza// These are attribute resource constants for the platform, as found
4077f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza// in android.R.attr
4087f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stozaenum {
4097f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    LABEL_ATTR = 0x01010001,
41067d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    ICON_ATTR = 0x01010002,
41167d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    NAME_ATTR = 0x01010003,
41267d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    PERMISSION_ATTR = 0x01010006,
41367d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    EXPORTED_ATTR = 0x01010010,
41467d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
41567d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    RESOURCE_ATTR = 0x01010025,
41667d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    DEBUGGABLE_ATTR = 0x0101000f,
41767d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    VALUE_ATTR = 0x01010024,
41867d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    VERSION_CODE_ATTR = 0x0101021b,
41967d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    VERSION_NAME_ATTR = 0x0101021c,
42067d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    SCREEN_ORIENTATION_ATTR = 0x0101001e,
42167d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar    MIN_SDK_VERSION_ATTR = 0x0101020c,
4227f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    MAX_SDK_VERSION_ATTR = 0x01010271,
4237f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    REQ_TOUCH_SCREEN_ATTR = 0x01010227,
4247f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
4257f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    REQ_HARD_KEYBOARD_ATTR = 0x01010229,
4267f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    REQ_NAVIGATION_ATTR = 0x0101022a,
4277f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
4287f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    TARGET_SDK_VERSION_ATTR = 0x01010270,
4297f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    TEST_ONLY_ATTR = 0x01010272,
430c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian    ANY_DENSITY_ATTR = 0x0101026c,
4319d4e3d2f42e93e2d12bacabe97d307d30c3c20ddJeff Brown    GL_ES_VERSION_ATTR = 0x01010281,
4327f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    SMALL_SCREEN_ATTR = 0x01010284,
4337f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza    NORMAL_SCREEN_ATTR = 0x01010285,
434c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian    LARGE_SCREEN_ATTR = 0x01010286,
4356c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    XLARGE_SCREEN_ATTR = 0x010102bf,
4366c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    REQUIRED_ATTR = 0x0101028e,
437d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    SCREEN_SIZE_ATTR = 0x010102ca,
438d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    SCREEN_DENSITY_ATTR = 0x010102cb,
439d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
440d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
441d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
442d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    PUBLIC_KEY_ATTR = 0x010103a6,
443d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    CATEGORY_ATTR = 0x010103e8,
444d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    BANNER_ATTR = 0x10103f2,
445d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav};
446d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav
447d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslavString8 getComponentName(String8 &pkgName, String8 &componentName) {
448d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    ssize_t idx = componentName.find(".");
449d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    String8 retStr(pkgName);
450d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav    if (idx == 0) {
4512c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani        retStr += componentName;
4522c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani    } else if (idx < 0) {
4532c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani        retStr += ".";
4542c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani        retStr += componentName;
4552c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani    } else {
4562c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani        return componentName;
4572c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani    }
4586c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    return retStr;
45983c0446f27b9542d6c2e724817b2b2d8d1f55085Mathias Agopian}
4606c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall
461edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectstatic void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
4626c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall    size_t len;
463edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    ResXMLTree::event_code_t code;
464edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    int depth = 0;
465edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    bool first = true;
466edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    printf("compatible-screens:");
467edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
468edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project        if (code == ResXMLTree::END_TAG) {
469            depth--;
470            if (depth < 0) {
471                break;
472            }
473            continue;
474        }
475        if (code != ResXMLTree::START_TAG) {
476            continue;
477        }
478        depth++;
479        const char16_t* ctag16 = tree.getElementName(&len);
480        if (ctag16 == NULL) {
481            *outError = "failed to get XML element name (bad string pool)";
482            return;
483        }
484        String8 tag(ctag16);
485        if (tag == "screen") {
486            int32_t screenSize = getIntegerAttribute(tree,
487                    SCREEN_SIZE_ATTR, NULL, -1);
488            int32_t screenDensity = getIntegerAttribute(tree,
489                    SCREEN_DENSITY_ATTR, NULL, -1);
490            if (screenSize > 0 && screenDensity > 0) {
491                if (!first) {
492                    printf(",");
493                }
494                first = false;
495                printf("'%d/%d'", screenSize, screenDensity);
496            }
497        }
498    }
499    printf("\n");
500}
501
502static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
503    printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
504    if (maxSdkVersion != -1) {
505         printf(" maxSdkVersion='%d'", maxSdkVersion);
506    }
507    printf("\n");
508
509    if (optional) {
510        printf("optional-permission: name='%s'",
511                ResTable::normalizeForOutput(name.string()).string());
512        if (maxSdkVersion != -1) {
513            printf(" maxSdkVersion='%d'", maxSdkVersion);
514        }
515        printf("\n");
516    }
517}
518
519static void printUsesImpliedPermission(const String8& name, const String8& reason) {
520    printf("uses-implied-permission: name='%s' reason='%s'\n",
521            ResTable::normalizeForOutput(name.string()).string(),
522            ResTable::normalizeForOutput(reason.string()).string());
523}
524
525Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
526        String8 *outError = NULL)
527{
528    Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
529    if (aidAsset == NULL) {
530        if (outError != NULL) *outError = "xml resource does not exist";
531        return Vector<String8>();
532    }
533
534    const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
535
536    bool withinApduService = false;
537    Vector<String8> categories;
538
539    String8 error;
540    ResXMLTree tree;
541    tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
542
543    size_t len;
544    int depth = 0;
545    ResXMLTree::event_code_t code;
546    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
547        if (code == ResXMLTree::END_TAG) {
548            depth--;
549            const char16_t* ctag16 = tree.getElementName(&len);
550            if (ctag16 == NULL) {
551                *outError = "failed to get XML element name (bad string pool)";
552                return Vector<String8>();
553            }
554            String8 tag(ctag16);
555
556            if (depth == 0 && tag == serviceTagName) {
557                withinApduService = false;
558            }
559
560        } else if (code == ResXMLTree::START_TAG) {
561            depth++;
562            const char16_t* ctag16 = tree.getElementName(&len);
563            if (ctag16 == NULL) {
564                *outError = "failed to get XML element name (bad string pool)";
565                return Vector<String8>();
566            }
567            String8 tag(ctag16);
568
569            if (depth == 1) {
570                if (tag == serviceTagName) {
571                    withinApduService = true;
572                }
573            } else if (depth == 2 && withinApduService) {
574                if (tag == "aid-group") {
575                    String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
576                    if (error != "") {
577                        if (outError != NULL) *outError = error;
578                        return Vector<String8>();
579                    }
580
581                    categories.add(category);
582                }
583            }
584        }
585    }
586    aidAsset->close();
587    return categories;
588}
589
590static void printComponentPresence(const char* componentName) {
591    printf("provides-component:'%s'\n", componentName);
592}
593
594/**
595 * Represents a feature that has been automatically added due to
596 * a pre-requisite or some other reason.
597 */
598struct ImpliedFeature {
599    /**
600     * Name of the implied feature.
601     */
602    String8 name;
603
604    /**
605     * List of human-readable reasons for why this feature was implied.
606     */
607    SortedVector<String8> reasons;
608};
609
610/**
611 * Represents a <feature-group> tag in the AndroidManifest.xml
612 */
613struct FeatureGroup {
614    FeatureGroup() : openGLESVersion(-1) {}
615
616    /**
617     * Human readable label
618     */
619    String8 label;
620
621    /**
622     * Explicit features defined in the group
623     */
624    KeyedVector<String8, bool> features;
625
626    /**
627     * OpenGL ES version required
628     */
629    int openGLESVersion;
630};
631
632static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
633        const char* name, const char* reason) {
634    String8 name8(name);
635    ssize_t idx = impliedFeatures->indexOfKey(name8);
636    if (idx < 0) {
637        idx = impliedFeatures->add(name8, ImpliedFeature());
638        impliedFeatures->editValueAt(idx).name = name8;
639    }
640    impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
641}
642
643static void printFeatureGroup(const FeatureGroup& grp,
644        const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
645    printf("feature-group: label='%s'\n", grp.label.string());
646
647    if (grp.openGLESVersion > 0) {
648        printf("  uses-gl-es: '0x%x'\n", grp.openGLESVersion);
649    }
650
651    const size_t numFeatures = grp.features.size();
652    for (size_t i = 0; i < numFeatures; i++) {
653        if (!grp.features[i]) {
654            continue;
655        }
656
657        const String8& featureName = grp.features.keyAt(i);
658        printf("  uses-feature: name='%s'\n",
659                ResTable::normalizeForOutput(featureName.string()).string());
660    }
661
662    const size_t numImpliedFeatures =
663        (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
664    for (size_t i = 0; i < numImpliedFeatures; i++) {
665        const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
666        if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
667            // The feature is explicitly set, no need to use implied
668            // definition.
669            continue;
670        }
671
672        String8 printableFeatureName(ResTable::normalizeForOutput(
673                    impliedFeature.name.string()));
674        printf("  uses-feature: name='%s'\n", printableFeatureName.string());
675        printf("  uses-implied-feature: name='%s' reason='",
676                printableFeatureName.string());
677        const size_t numReasons = impliedFeature.reasons.size();
678        for (size_t j = 0; j < numReasons; j++) {
679            printf("%s", impliedFeature.reasons[j].string());
680            if (j + 2 < numReasons) {
681                printf(", ");
682            } else if (j + 1 < numReasons) {
683                printf(", and ");
684            }
685        }
686        printf("'\n");
687    }
688}
689
690static void addParentFeatures(FeatureGroup* grp, const String8& name) {
691    if (name == "android.hardware.camera.autofocus" ||
692            name == "android.hardware.camera.flash") {
693        grp->features.add(String8("android.hardware.camera"), true);
694    } else if (name == "android.hardware.location.gps" ||
695            name == "android.hardware.location.network") {
696        grp->features.add(String8("android.hardware.location"), true);
697    } else if (name == "android.hardware.touchscreen.multitouch") {
698        grp->features.add(String8("android.hardware.touchscreen"), true);
699    } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
700        grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
701        grp->features.add(String8("android.hardware.touchscreen"), true);
702    } else if (name == "android.hardware.opengles.aep") {
703        const int openGLESVersion31 = 0x00030001;
704        if (openGLESVersion31 > grp->openGLESVersion) {
705            grp->openGLESVersion = openGLESVersion31;
706        }
707    }
708}
709
710/*
711 * Handle the "dump" command, to extract select data from an archive.
712 */
713extern char CONSOLE_DATA[2925]; // see EOF
714int doDump(Bundle* bundle)
715{
716    status_t result = UNKNOWN_ERROR;
717
718    if (bundle->getFileSpecCount() < 1) {
719        fprintf(stderr, "ERROR: no dump option specified\n");
720        return 1;
721    }
722
723    if (bundle->getFileSpecCount() < 2) {
724        fprintf(stderr, "ERROR: no dump file specified\n");
725        return 1;
726    }
727
728    const char* option = bundle->getFileSpecEntry(0);
729    const char* filename = bundle->getFileSpecEntry(1);
730
731    AssetManager assets;
732    int32_t assetsCookie;
733    if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
734        fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
735        return 1;
736    }
737
738    // Make a dummy config for retrieving resources...  we need to supply
739    // non-default values for some configs so that we can retrieve resources
740    // in the app that don't have a default.  The most important of these is
741    // the API version because key resources like icons will have an implicit
742    // version if they are using newer config types like density.
743    ResTable_config config;
744    memset(&config, 0, sizeof(ResTable_config));
745    config.language[0] = 'e';
746    config.language[1] = 'n';
747    config.country[0] = 'U';
748    config.country[1] = 'S';
749    config.orientation = ResTable_config::ORIENTATION_PORT;
750    config.density = ResTable_config::DENSITY_MEDIUM;
751    config.sdkVersion = 10000; // Very high.
752    config.screenWidthDp = 320;
753    config.screenHeightDp = 480;
754    config.smallestScreenWidthDp = 320;
755    config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
756    assets.setConfiguration(config);
757
758    const ResTable& res = assets.getResources(false);
759    if (&res == NULL) {
760        fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
761        return 1;
762    } else if (res.getError() != NO_ERROR) {
763        fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
764        return 1;
765    }
766
767    const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
768    if (dynamicRefTable == NULL) {
769        fprintf(stderr, "ERROR: failed to find dynamic reference table for asset cookie %d\n",
770                assetsCookie);
771        return 1;
772    }
773
774    Asset* asset = NULL;
775
776    if (strcmp("resources", option) == 0) {
777#ifndef HAVE_ANDROID_OS
778        res.print(bundle->getValues());
779#endif
780
781    } else if (strcmp("strings", option) == 0) {
782        const ResStringPool* pool = res.getTableStringBlock(0);
783        printStringPool(pool);
784
785    } else if (strcmp("xmltree", option) == 0) {
786        if (bundle->getFileSpecCount() < 3) {
787            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
788            goto bail;
789        }
790
791        for (int i=2; i<bundle->getFileSpecCount(); i++) {
792            const char* resname = bundle->getFileSpecEntry(i);
793            ResXMLTree tree(dynamicRefTable);
794            asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
795            if (asset == NULL) {
796                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
797                goto bail;
798            }
799
800            if (tree.setTo(asset->getBuffer(true),
801                           asset->getLength()) != NO_ERROR) {
802                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
803                goto bail;
804            }
805            tree.restart();
806            printXMLBlock(&tree);
807            tree.uninit();
808            delete asset;
809            asset = NULL;
810        }
811
812    } else if (strcmp("xmlstrings", option) == 0) {
813        if (bundle->getFileSpecCount() < 3) {
814            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
815            goto bail;
816        }
817
818        for (int i=2; i<bundle->getFileSpecCount(); i++) {
819            const char* resname = bundle->getFileSpecEntry(i);
820            asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
821            if (asset == NULL) {
822                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
823                goto bail;
824            }
825
826            ResXMLTree tree(dynamicRefTable);
827            if (tree.setTo(asset->getBuffer(true),
828                           asset->getLength()) != NO_ERROR) {
829                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
830                goto bail;
831            }
832            printStringPool(&tree.getStrings());
833            delete asset;
834            asset = NULL;
835        }
836
837    } else {
838        asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
839        if (asset == NULL) {
840            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
841            goto bail;
842        }
843
844        ResXMLTree tree(dynamicRefTable);
845        if (tree.setTo(asset->getBuffer(true),
846                       asset->getLength()) != NO_ERROR) {
847            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
848            goto bail;
849        }
850        tree.restart();
851
852        if (strcmp("permissions", option) == 0) {
853            size_t len;
854            ResXMLTree::event_code_t code;
855            int depth = 0;
856            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
857                if (code == ResXMLTree::END_TAG) {
858                    depth--;
859                    continue;
860                }
861                if (code != ResXMLTree::START_TAG) {
862                    continue;
863                }
864                depth++;
865                const char16_t* ctag16 = tree.getElementName(&len);
866                if (ctag16 == NULL) {
867                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
868                    goto bail;
869                }
870                String8 tag(ctag16);
871                //printf("Depth %d tag %s\n", depth, tag.string());
872                if (depth == 1) {
873                    if (tag != "manifest") {
874                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
875                        goto bail;
876                    }
877                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
878                    printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
879                } else if (depth == 2 && tag == "permission") {
880                    String8 error;
881                    String8 name = getAttribute(tree, NAME_ATTR, &error);
882                    if (error != "") {
883                        fprintf(stderr, "ERROR: %s\n", error.string());
884                        goto bail;
885                    }
886                    printf("permission: %s\n",
887                            ResTable::normalizeForOutput(name.string()).string());
888                } else if (depth == 2 && tag == "uses-permission") {
889                    String8 error;
890                    String8 name = getAttribute(tree, NAME_ATTR, &error);
891                    if (error != "") {
892                        fprintf(stderr, "ERROR: %s\n", error.string());
893                        goto bail;
894                    }
895                    printUsesPermission(name,
896                            getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
897                            getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
898                }
899            }
900        } else if (strcmp("badging", option) == 0) {
901            Vector<String8> locales;
902            res.getLocales(&locales);
903
904            Vector<ResTable_config> configs;
905            res.getConfigurations(&configs);
906            SortedVector<int> densities;
907            const size_t NC = configs.size();
908            for (size_t i=0; i<NC; i++) {
909                int dens = configs[i].density;
910                if (dens == 0) {
911                    dens = 160;
912                }
913                densities.add(dens);
914            }
915
916            size_t len;
917            ResXMLTree::event_code_t code;
918            int depth = 0;
919            String8 error;
920            bool withinActivity = false;
921            bool isMainActivity = false;
922            bool isLauncherActivity = false;
923            bool isLeanbackLauncherActivity = false;
924            bool isSearchable = false;
925            bool withinApplication = false;
926            bool withinSupportsInput = false;
927            bool withinFeatureGroup = false;
928            bool withinReceiver = false;
929            bool withinService = false;
930            bool withinProvider = false;
931            bool withinIntentFilter = false;
932            bool hasMainActivity = false;
933            bool hasOtherActivities = false;
934            bool hasOtherReceivers = false;
935            bool hasOtherServices = false;
936            bool hasIntentFilter = false;
937
938            bool hasWallpaperService = false;
939            bool hasImeService = false;
940            bool hasAccessibilityService = false;
941            bool hasPrintService = false;
942            bool hasWidgetReceivers = false;
943            bool hasDeviceAdminReceiver = false;
944            bool hasPaymentService = false;
945            bool hasDocumentsProvider = false;
946            bool hasCameraActivity = false;
947            bool hasCameraSecureActivity = false;
948            bool hasLauncher = false;
949            bool hasNotificationListenerService = false;
950            bool hasDreamService = false;
951
952            bool actMainActivity = false;
953            bool actWidgetReceivers = false;
954            bool actDeviceAdminEnabled = false;
955            bool actImeService = false;
956            bool actWallpaperService = false;
957            bool actAccessibilityService = false;
958            bool actPrintService = false;
959            bool actHostApduService = false;
960            bool actOffHostApduService = false;
961            bool actDocumentsProvider = false;
962            bool actNotificationListenerService = false;
963            bool actDreamService = false;
964            bool actCamera = false;
965            bool actCameraSecure = false;
966            bool catLauncher = false;
967            bool hasMetaHostPaymentCategory = false;
968            bool hasMetaOffHostPaymentCategory = false;
969
970            // These permissions are required by services implementing services
971            // the system binds to (IME, Accessibility, PrintServices, etc.)
972            bool hasBindDeviceAdminPermission = false;
973            bool hasBindInputMethodPermission = false;
974            bool hasBindAccessibilityServicePermission = false;
975            bool hasBindPrintServicePermission = false;
976            bool hasBindNfcServicePermission = false;
977            bool hasRequiredSafAttributes = false;
978            bool hasBindNotificationListenerServicePermission = false;
979            bool hasBindDreamServicePermission = false;
980
981            // These two implement the implicit permissions that are granted
982            // to pre-1.6 applications.
983            bool hasWriteExternalStoragePermission = false;
984            bool hasReadPhoneStatePermission = false;
985
986            // If an app requests write storage, they will also get read storage.
987            bool hasReadExternalStoragePermission = false;
988
989            // Implement transition to read and write call log.
990            bool hasReadContactsPermission = false;
991            bool hasWriteContactsPermission = false;
992            bool hasReadCallLogPermission = false;
993            bool hasWriteCallLogPermission = false;
994
995            // This next group of variables is used to implement a group of
996            // backward-compatibility heuristics necessitated by the addition of
997            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
998            // heuristic is "if an app requests a permission but doesn't explicitly
999            // request the corresponding <uses-feature>, presume it's there anyway".
1000
1001            // 2.2 also added some other features that apps can request, but that
1002            // have no corresponding permission, so we cannot implement any
1003            // back-compatibility heuristic for them. The below are thus unnecessary
1004            // (but are retained here for documentary purposes.)
1005            //bool specCompassFeature = false;
1006            //bool specAccelerometerFeature = false;
1007            //bool specProximityFeature = false;
1008            //bool specAmbientLightFeature = false;
1009            //bool specLiveWallpaperFeature = false;
1010
1011            int targetSdk = 0;
1012            int smallScreen = 1;
1013            int normalScreen = 1;
1014            int largeScreen = 1;
1015            int xlargeScreen = 1;
1016            int anyDensity = 1;
1017            int requiresSmallestWidthDp = 0;
1018            int compatibleWidthLimitDp = 0;
1019            int largestWidthLimitDp = 0;
1020            String8 pkg;
1021            String8 activityName;
1022            String8 activityLabel;
1023            String8 activityIcon;
1024            String8 activityBanner;
1025            String8 receiverName;
1026            String8 serviceName;
1027            Vector<String8> supportedInput;
1028
1029            FeatureGroup commonFeatures;
1030            Vector<FeatureGroup> featureGroups;
1031            KeyedVector<String8, ImpliedFeature> impliedFeatures;
1032
1033            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
1034                if (code == ResXMLTree::END_TAG) {
1035                    depth--;
1036                    if (depth < 2) {
1037                        if (withinSupportsInput && !supportedInput.isEmpty()) {
1038                            printf("supports-input: '");
1039                            const size_t N = supportedInput.size();
1040                            for (size_t i=0; i<N; i++) {
1041                                printf("%s", ResTable::normalizeForOutput(
1042                                        supportedInput[i].string()).string());
1043                                if (i != N - 1) {
1044                                    printf("' '");
1045                                } else {
1046                                    printf("'\n");
1047                                }
1048                            }
1049                            supportedInput.clear();
1050                        }
1051                        withinApplication = false;
1052                        withinSupportsInput = false;
1053                        withinFeatureGroup = false;
1054                    } else if (depth < 3) {
1055                        if (withinActivity && isMainActivity) {
1056                            String8 aName(getComponentName(pkg, activityName));
1057                            if (isLauncherActivity) {
1058                                printf("launchable-activity:");
1059                                if (aName.length() > 0) {
1060                                    printf(" name='%s' ",
1061                                            ResTable::normalizeForOutput(aName.string()).string());
1062                                }
1063                                printf(" label='%s' icon='%s'\n",
1064                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
1065                                        ResTable::normalizeForOutput(activityIcon.string()).string());
1066                            }
1067                            if (isLeanbackLauncherActivity) {
1068                                printf("leanback-launchable-activity:");
1069                                if (aName.length() > 0) {
1070                                    printf(" name='%s' ",
1071                                            ResTable::normalizeForOutput(aName.string()).string());
1072                                }
1073                                printf(" label='%s' icon='%s' banner='%s'\n",
1074                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
1075                                        ResTable::normalizeForOutput(activityIcon.string()).string(),
1076                                        ResTable::normalizeForOutput(activityBanner.string()).string());
1077                            }
1078                        }
1079                        if (!hasIntentFilter) {
1080                            hasOtherActivities |= withinActivity;
1081                            hasOtherReceivers |= withinReceiver;
1082                            hasOtherServices |= withinService;
1083                        } else {
1084                            if (withinService) {
1085                                hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
1086                                        hasBindNfcServicePermission);
1087                                hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
1088                                        hasBindNfcServicePermission);
1089                            }
1090                        }
1091                        withinActivity = false;
1092                        withinService = false;
1093                        withinReceiver = false;
1094                        withinProvider = false;
1095                        hasIntentFilter = false;
1096                        isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
1097                    } else if (depth < 4) {
1098                        if (withinIntentFilter) {
1099                            if (withinActivity) {
1100                                hasMainActivity |= actMainActivity;
1101                                hasLauncher |= catLauncher;
1102                                hasCameraActivity |= actCamera;
1103                                hasCameraSecureActivity |= actCameraSecure;
1104                                hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
1105                            } else if (withinReceiver) {
1106                                hasWidgetReceivers |= actWidgetReceivers;
1107                                hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1108                                        hasBindDeviceAdminPermission);
1109                                hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
1110                            } else if (withinService) {
1111                                hasImeService |= actImeService;
1112                                hasWallpaperService |= actWallpaperService;
1113                                hasAccessibilityService |= (actAccessibilityService &&
1114                                        hasBindAccessibilityServicePermission);
1115                                hasPrintService |= (actPrintService && hasBindPrintServicePermission);
1116                                hasNotificationListenerService |= actNotificationListenerService &&
1117                                        hasBindNotificationListenerServicePermission;
1118                                hasDreamService |= actDreamService && hasBindDreamServicePermission;
1119                                hasOtherServices |= (!actImeService && !actWallpaperService &&
1120                                        !actAccessibilityService && !actPrintService &&
1121                                        !actHostApduService && !actOffHostApduService &&
1122                                        !actNotificationListenerService);
1123                            } else if (withinProvider) {
1124                                hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
1125                            }
1126                        }
1127                        withinIntentFilter = false;
1128                    }
1129                    continue;
1130                }
1131                if (code != ResXMLTree::START_TAG) {
1132                    continue;
1133                }
1134                depth++;
1135
1136                const char16_t* ctag16 = tree.getElementName(&len);
1137                if (ctag16 == NULL) {
1138                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1139                    goto bail;
1140                }
1141                String8 tag(ctag16);
1142                //printf("Depth %d,  %s\n", depth, tag.string());
1143                if (depth == 1) {
1144                    if (tag != "manifest") {
1145                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1146                        goto bail;
1147                    }
1148                    pkg = getAttribute(tree, NULL, "package", NULL);
1149                    printf("package: name='%s' ",
1150                            ResTable::normalizeForOutput(pkg.string()).string());
1151                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1152                    if (error != "") {
1153                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1154                        goto bail;
1155                    }
1156                    if (versionCode > 0) {
1157                        printf("versionCode='%d' ", versionCode);
1158                    } else {
1159                        printf("versionCode='' ");
1160                    }
1161                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1162                    if (error != "") {
1163                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1164                        goto bail;
1165                    }
1166                    printf("versionName='%s'\n",
1167                            ResTable::normalizeForOutput(versionName.string()).string());
1168                } else if (depth == 2) {
1169                    withinApplication = false;
1170                    if (tag == "application") {
1171                        withinApplication = true;
1172
1173                        String8 label;
1174                        const size_t NL = locales.size();
1175                        for (size_t i=0; i<NL; i++) {
1176                            const char* localeStr =  locales[i].string();
1177                            assets.setLocale(localeStr != NULL ? localeStr : "");
1178                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1179                            if (llabel != "") {
1180                                if (localeStr == NULL || strlen(localeStr) == 0) {
1181                                    label = llabel;
1182                                    printf("application-label:'%s'\n",
1183                                            ResTable::normalizeForOutput(llabel.string()).string());
1184                                } else {
1185                                    if (label == "") {
1186                                        label = llabel;
1187                                    }
1188                                    printf("application-label-%s:'%s'\n", localeStr,
1189                                           ResTable::normalizeForOutput(llabel.string()).string());
1190                                }
1191                            }
1192                        }
1193
1194                        ResTable_config tmpConfig = config;
1195                        const size_t ND = densities.size();
1196                        for (size_t i=0; i<ND; i++) {
1197                            tmpConfig.density = densities[i];
1198                            assets.setConfiguration(tmpConfig);
1199                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1200                            if (icon != "") {
1201                                printf("application-icon-%d:'%s'\n", densities[i],
1202                                        ResTable::normalizeForOutput(icon.string()).string());
1203                            }
1204                        }
1205                        assets.setConfiguration(config);
1206
1207                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1208                        if (error != "") {
1209                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1210                            goto bail;
1211                        }
1212                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1213                        if (error != "") {
1214                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1215                            goto bail;
1216                        }
1217                        printf("application: label='%s' ",
1218                                ResTable::normalizeForOutput(label.string()).string());
1219                        printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
1220                        if (testOnly != 0) {
1221                            printf("testOnly='%d'\n", testOnly);
1222                        }
1223
1224                        int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1225                        if (error != "") {
1226                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1227                            goto bail;
1228                        }
1229                        if (debuggable != 0) {
1230                            printf("application-debuggable\n");
1231                        }
1232                    } else if (tag == "uses-sdk") {
1233                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1234                        if (error != "") {
1235                            error = "";
1236                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1237                            if (error != "") {
1238                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1239                                        error.string());
1240                                goto bail;
1241                            }
1242                            if (name == "Donut") targetSdk = 4;
1243                            printf("sdkVersion:'%s'\n",
1244                                    ResTable::normalizeForOutput(name.string()).string());
1245                        } else if (code != -1) {
1246                            targetSdk = code;
1247                            printf("sdkVersion:'%d'\n", code);
1248                        }
1249                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1250                        if (code != -1) {
1251                            printf("maxSdkVersion:'%d'\n", code);
1252                        }
1253                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1254                        if (error != "") {
1255                            error = "";
1256                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1257                            if (error != "") {
1258                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1259                                        error.string());
1260                                goto bail;
1261                            }
1262                            if (name == "Donut" && targetSdk < 4) targetSdk = 4;
1263                            printf("targetSdkVersion:'%s'\n",
1264                                    ResTable::normalizeForOutput(name.string()).string());
1265                        } else if (code != -1) {
1266                            if (targetSdk < code) {
1267                                targetSdk = code;
1268                            }
1269                            printf("targetSdkVersion:'%d'\n", code);
1270                        }
1271                    } else if (tag == "uses-configuration") {
1272                        int32_t reqTouchScreen = getIntegerAttribute(tree,
1273                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1274                        int32_t reqKeyboardType = getIntegerAttribute(tree,
1275                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1276                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
1277                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1278                        int32_t reqNavigation = getIntegerAttribute(tree,
1279                                REQ_NAVIGATION_ATTR, NULL, 0);
1280                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
1281                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1282                        printf("uses-configuration:");
1283                        if (reqTouchScreen != 0) {
1284                            printf(" reqTouchScreen='%d'", reqTouchScreen);
1285                        }
1286                        if (reqKeyboardType != 0) {
1287                            printf(" reqKeyboardType='%d'", reqKeyboardType);
1288                        }
1289                        if (reqHardKeyboard != 0) {
1290                            printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1291                        }
1292                        if (reqNavigation != 0) {
1293                            printf(" reqNavigation='%d'", reqNavigation);
1294                        }
1295                        if (reqFiveWayNav != 0) {
1296                            printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1297                        }
1298                        printf("\n");
1299                    } else if (tag == "supports-input") {
1300                        withinSupportsInput = true;
1301                    } else if (tag == "supports-screens") {
1302                        smallScreen = getIntegerAttribute(tree,
1303                                SMALL_SCREEN_ATTR, NULL, 1);
1304                        normalScreen = getIntegerAttribute(tree,
1305                                NORMAL_SCREEN_ATTR, NULL, 1);
1306                        largeScreen = getIntegerAttribute(tree,
1307                                LARGE_SCREEN_ATTR, NULL, 1);
1308                        xlargeScreen = getIntegerAttribute(tree,
1309                                XLARGE_SCREEN_ATTR, NULL, 1);
1310                        anyDensity = getIntegerAttribute(tree,
1311                                ANY_DENSITY_ATTR, NULL, 1);
1312                        requiresSmallestWidthDp = getIntegerAttribute(tree,
1313                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1314                        compatibleWidthLimitDp = getIntegerAttribute(tree,
1315                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1316                        largestWidthLimitDp = getIntegerAttribute(tree,
1317                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1318                    } else if (tag == "feature-group") {
1319                        withinFeatureGroup = true;
1320                        FeatureGroup group;
1321                        group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1322                        if (error != "") {
1323                            fprintf(stderr, "ERROR getting 'android:label' attribute:"
1324                                    " %s\n", error.string());
1325                            goto bail;
1326                        }
1327                        featureGroups.add(group);
1328
1329                    } else if (tag == "uses-feature") {
1330                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1331                        if (name != "" && error == "") {
1332                            int req = getIntegerAttribute(tree,
1333                                    REQUIRED_ATTR, NULL, 1);
1334
1335                            commonFeatures.features.add(name, req);
1336                            if (req) {
1337                                addParentFeatures(&commonFeatures, name);
1338                            }
1339                        } else {
1340                            int vers = getIntegerAttribute(tree,
1341                                    GL_ES_VERSION_ATTR, &error);
1342                            if (error == "") {
1343                                if (vers > commonFeatures.openGLESVersion) {
1344                                    commonFeatures.openGLESVersion = vers;
1345                                }
1346                            }
1347                        }
1348                    } else if (tag == "uses-permission") {
1349                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1350                        if (name != "" && error == "") {
1351                            if (name == "android.permission.CAMERA") {
1352                                addImpliedFeature(&impliedFeatures, "android.hardware.camera",
1353                                        String8::format("requested %s permission", name.string())
1354                                        .string());
1355                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1356                                addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
1357                                        String8::format("requested %s permission", name.string())
1358                                        .string());
1359                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1360                                        String8::format("requested %s permission", name.string())
1361                                        .string());
1362                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1363                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1364                                        String8::format("requested %s permission", name.string())
1365                                        .string());
1366                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1367                                addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
1368                                        String8::format("requested %s permission", name.string())
1369                                        .string());
1370                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1371                                        String8::format("requested %s permission", name.string())
1372                                        .string());
1373                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1374                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1375                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
1376                                        String8::format("requested %s permission", name.string())
1377                                        .string());
1378                            } else if (name == "android.permission.BLUETOOTH" ||
1379                                       name == "android.permission.BLUETOOTH_ADMIN") {
1380                                if (targetSdk > 4) {
1381                                    addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1382                                            String8::format("requested %s permission", name.string())
1383                                            .string());
1384                                    addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
1385                                            "targetSdkVersion > 4");
1386                                }
1387                            } else if (name == "android.permission.RECORD_AUDIO") {
1388                                addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
1389                                        String8::format("requested %s permission", name.string())
1390                                        .string());
1391                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1392                                       name == "android.permission.CHANGE_WIFI_STATE" ||
1393                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1394                                addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
1395                                        String8::format("requested %s permission", name.string())
1396                                        .string());
1397                            } else if (name == "android.permission.CALL_PHONE" ||
1398                                       name == "android.permission.CALL_PRIVILEGED" ||
1399                                       name == "android.permission.MODIFY_PHONE_STATE" ||
1400                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1401                                       name == "android.permission.READ_SMS" ||
1402                                       name == "android.permission.RECEIVE_SMS" ||
1403                                       name == "android.permission.RECEIVE_MMS" ||
1404                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
1405                                       name == "android.permission.SEND_SMS" ||
1406                                       name == "android.permission.WRITE_APN_SETTINGS" ||
1407                                       name == "android.permission.WRITE_SMS") {
1408                                addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
1409                                        String8("requested a telephony permission").string());
1410                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1411                                hasWriteExternalStoragePermission = true;
1412                            } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1413                                hasReadExternalStoragePermission = true;
1414                            } else if (name == "android.permission.READ_PHONE_STATE") {
1415                                hasReadPhoneStatePermission = true;
1416                            } else if (name == "android.permission.READ_CONTACTS") {
1417                                hasReadContactsPermission = true;
1418                            } else if (name == "android.permission.WRITE_CONTACTS") {
1419                                hasWriteContactsPermission = true;
1420                            } else if (name == "android.permission.READ_CALL_LOG") {
1421                                hasReadCallLogPermission = true;
1422                            } else if (name == "android.permission.WRITE_CALL_LOG") {
1423                                hasWriteCallLogPermission = true;
1424                            }
1425
1426                            printUsesPermission(name,
1427                                    getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1428                                    getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1429                       } else {
1430                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1431                                    error.string());
1432                            goto bail;
1433                        }
1434                    } else if (tag == "uses-package") {
1435                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1436                        if (name != "" && error == "") {
1437                            printf("uses-package:'%s'\n",
1438                                    ResTable::normalizeForOutput(name.string()).string());
1439                        } else {
1440                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1441                                    error.string());
1442                                goto bail;
1443                        }
1444                    } else if (tag == "original-package") {
1445                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1446                        if (name != "" && error == "") {
1447                            printf("original-package:'%s'\n",
1448                                    ResTable::normalizeForOutput(name.string()).string());
1449                        } else {
1450                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1451                                    error.string());
1452                                goto bail;
1453                        }
1454                    } else if (tag == "supports-gl-texture") {
1455                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1456                        if (name != "" && error == "") {
1457                            printf("supports-gl-texture:'%s'\n",
1458                                    ResTable::normalizeForOutput(name.string()).string());
1459                        } else {
1460                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1461                                    error.string());
1462                                goto bail;
1463                        }
1464                    } else if (tag == "compatible-screens") {
1465                        printCompatibleScreens(tree, &error);
1466                        if (error != "") {
1467                            fprintf(stderr, "ERROR getting compatible screens: %s\n",
1468                                    error.string());
1469                            goto bail;
1470                        }
1471                        depth--;
1472                    } else if (tag == "package-verifier") {
1473                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1474                        if (name != "" && error == "") {
1475                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1476                            if (publicKey != "" && error == "") {
1477                                printf("package-verifier: name='%s' publicKey='%s'\n",
1478                                        ResTable::normalizeForOutput(name.string()).string(),
1479                                        ResTable::normalizeForOutput(publicKey.string()).string());
1480                            }
1481                        }
1482                    }
1483                } else if (depth == 3) {
1484                    withinActivity = false;
1485                    withinReceiver = false;
1486                    withinService = false;
1487                    withinProvider = false;
1488                    hasIntentFilter = false;
1489                    hasMetaHostPaymentCategory = false;
1490                    hasMetaOffHostPaymentCategory = false;
1491                    hasBindDeviceAdminPermission = false;
1492                    hasBindInputMethodPermission = false;
1493                    hasBindAccessibilityServicePermission = false;
1494                    hasBindPrintServicePermission = false;
1495                    hasBindNfcServicePermission = false;
1496                    hasRequiredSafAttributes = false;
1497                    hasBindNotificationListenerServicePermission = false;
1498                    hasBindDreamServicePermission = false;
1499                    if (withinApplication) {
1500                        if(tag == "activity") {
1501                            withinActivity = true;
1502                            activityName = getAttribute(tree, NAME_ATTR, &error);
1503                            if (error != "") {
1504                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1505                                        error.string());
1506                                goto bail;
1507                            }
1508
1509                            activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1510                            if (error != "") {
1511                                fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1512                                        error.string());
1513                                goto bail;
1514                            }
1515
1516                            activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1517                            if (error != "") {
1518                                fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1519                                        error.string());
1520                                goto bail;
1521                            }
1522
1523                            activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1524                            if (error != "") {
1525                                fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1526                                        error.string());
1527                                goto bail;
1528                            }
1529
1530                            int32_t orien = getResolvedIntegerAttribute(&res, tree,
1531                                    SCREEN_ORIENTATION_ATTR, &error);
1532                            if (error == "") {
1533                                if (orien == 0 || orien == 6 || orien == 8) {
1534                                    // Requests landscape, sensorLandscape, or reverseLandscape.
1535                                    addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
1536                                            "one or more activities have specified a landscape orientation");
1537                                } else if (orien == 1 || orien == 7 || orien == 9) {
1538                                    // Requests portrait, sensorPortrait, or reversePortrait.
1539                                    addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
1540                                            "one or more activities have specified a portrait orientation");
1541                                }
1542                            }
1543                        } else if (tag == "uses-library") {
1544                            String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1545                            if (error != "") {
1546                                fprintf(stderr,
1547                                        "ERROR getting 'android:name' attribute for uses-library"
1548                                        " %s\n", error.string());
1549                                goto bail;
1550                            }
1551                            int req = getIntegerAttribute(tree,
1552                                    REQUIRED_ATTR, NULL, 1);
1553                            printf("uses-library%s:'%s'\n",
1554                                    req ? "" : "-not-required", ResTable::normalizeForOutput(
1555                                            libraryName.string()).string());
1556                        } else if (tag == "receiver") {
1557                            withinReceiver = true;
1558                            receiverName = getAttribute(tree, NAME_ATTR, &error);
1559
1560                            if (error != "") {
1561                                fprintf(stderr,
1562                                        "ERROR getting 'android:name' attribute for receiver:"
1563                                        " %s\n", error.string());
1564                                goto bail;
1565                            }
1566
1567                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1568                            if (error == "") {
1569                                if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1570                                    hasBindDeviceAdminPermission = true;
1571                                }
1572                            } else {
1573                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1574                                        " receiver '%s': %s\n", receiverName.string(), error.string());
1575                            }
1576                        } else if (tag == "service") {
1577                            withinService = true;
1578                            serviceName = getAttribute(tree, NAME_ATTR, &error);
1579
1580                            if (error != "") {
1581                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1582                                        "service:%s\n", error.string());
1583                                goto bail;
1584                            }
1585
1586                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1587                            if (error == "") {
1588                                if (permission == "android.permission.BIND_INPUT_METHOD") {
1589                                    hasBindInputMethodPermission = true;
1590                                } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1591                                    hasBindAccessibilityServicePermission = true;
1592                                } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1593                                    hasBindPrintServicePermission = true;
1594                                } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1595                                    hasBindNfcServicePermission = true;
1596                                } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1597                                    hasBindNotificationListenerServicePermission = true;
1598                                } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
1599                                    hasBindDreamServicePermission = true;
1600                                }
1601                            } else {
1602                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1603                                        " service '%s': %s\n", serviceName.string(), error.string());
1604                            }
1605                        } else if (tag == "provider") {
1606                            withinProvider = true;
1607
1608                            bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
1609                            if (error != "") {
1610                                fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1611                                        " %s\n", error.string());
1612                                goto bail;
1613                            }
1614
1615                            bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
1616                                    GRANT_URI_PERMISSIONS_ATTR, &error);
1617                            if (error != "") {
1618                                fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1619                                        " %s\n", error.string());
1620                                goto bail;
1621                            }
1622
1623                            String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
1624                            if (error != "") {
1625                                fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1626                                        " %s\n", error.string());
1627                                goto bail;
1628                            }
1629
1630                            hasRequiredSafAttributes |= exported && grantUriPermissions &&
1631                                permission == "android.permission.MANAGE_DOCUMENTS";
1632
1633                        } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1634                            String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1635                            if (error != "") {
1636                                fprintf(stderr, "ERROR getting 'android:name' attribute for "
1637                                        "meta-data:%s\n", error.string());
1638                                goto bail;
1639                            }
1640                            printf("meta-data: name='%s' ",
1641                                    ResTable::normalizeForOutput(metaDataName.string()).string());
1642                            printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1643                                    &error);
1644                            if (error != "") {
1645                                // Try looking for a RESOURCE_ATTR
1646                                error = "";
1647                                printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1648                                        String8("resource"), &error);
1649                                if (error != "") {
1650                                    fprintf(stderr, "ERROR getting 'android:value' or "
1651                                            "'android:resource' attribute for "
1652                                            "meta-data:%s\n", error.string());
1653                                    goto bail;
1654                                }
1655                            }
1656                            printf("\n");
1657                        } else if (withinSupportsInput && tag == "input-type") {
1658                            String8 name = getAttribute(tree, NAME_ATTR, &error);
1659                            if (name != "" && error == "") {
1660                                supportedInput.add(name);
1661                            } else {
1662                                fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1663                                        error.string());
1664                                goto bail;
1665                            }
1666                        }
1667                    } else if (withinFeatureGroup && tag == "uses-feature") {
1668                        FeatureGroup& top = featureGroups.editTop();
1669
1670                        String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
1671                        if (name != "" && error == "") {
1672                            int required = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1673                            top.features.add(name, required);
1674                            if (required) {
1675                                addParentFeatures(&top, name);
1676                            }
1677
1678                        } else {
1679                            int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
1680                            if (error == "") {
1681                                if (vers > top.openGLESVersion) {
1682                                    top.openGLESVersion = vers;
1683                                }
1684                            }
1685                        }
1686                    }
1687                } else if (depth == 4) {
1688                    if (tag == "intent-filter") {
1689                        hasIntentFilter = true;
1690                        withinIntentFilter = true;
1691                        actMainActivity = false;
1692                        actWidgetReceivers = false;
1693                        actImeService = false;
1694                        actWallpaperService = false;
1695                        actAccessibilityService = false;
1696                        actPrintService = false;
1697                        actDeviceAdminEnabled = false;
1698                        actHostApduService = false;
1699                        actOffHostApduService = false;
1700                        actDocumentsProvider = false;
1701                        actNotificationListenerService = false;
1702                        actDreamService = false;
1703                        actCamera = false;
1704                        actCameraSecure = false;
1705                        catLauncher = false;
1706                    } else if (withinService && tag == "meta-data") {
1707                        String8 name = getAttribute(tree, NAME_ATTR, &error);
1708                        if (error != "") {
1709                            fprintf(stderr, "ERROR getting 'android:name' attribute for"
1710                                    " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1711                            goto bail;
1712                        }
1713
1714                        if (name == "android.nfc.cardemulation.host_apdu_service" ||
1715                                name == "android.nfc.cardemulation.off_host_apdu_service") {
1716                            bool offHost = true;
1717                            if (name == "android.nfc.cardemulation.host_apdu_service") {
1718                                offHost = false;
1719                            }
1720
1721                            String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1722                            if (error != "") {
1723                                fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1724                                        " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1725                                goto bail;
1726                            }
1727
1728                            Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1729                                    offHost, &error);
1730                            if (error != "") {
1731                                fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1732                                        serviceName.string());
1733                                goto bail;
1734                            }
1735
1736                            const size_t catLen = categories.size();
1737                            for (size_t i = 0; i < catLen; i++) {
1738                                bool paymentCategory = (categories[i] == "payment");
1739                                if (offHost) {
1740                                    hasMetaOffHostPaymentCategory |= paymentCategory;
1741                                } else {
1742                                    hasMetaHostPaymentCategory |= paymentCategory;
1743                                }
1744                            }
1745                        }
1746                    }
1747                } else if ((depth == 5) && withinIntentFilter) {
1748                    String8 action;
1749                    if (tag == "action") {
1750                        action = getAttribute(tree, NAME_ATTR, &error);
1751                        if (error != "") {
1752                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1753                                    error.string());
1754                            goto bail;
1755                        }
1756
1757                        if (withinActivity) {
1758                            if (action == "android.intent.action.MAIN") {
1759                                isMainActivity = true;
1760                                actMainActivity = true;
1761                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1762                                    action == "android.media.action.VIDEO_CAMERA") {
1763                                actCamera = true;
1764                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1765                                actCameraSecure = true;
1766                            }
1767                        } else if (withinReceiver) {
1768                            if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1769                                actWidgetReceivers = true;
1770                            } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1771                                actDeviceAdminEnabled = true;
1772                            }
1773                        } else if (withinService) {
1774                            if (action == "android.view.InputMethod") {
1775                                actImeService = true;
1776                            } else if (action == "android.service.wallpaper.WallpaperService") {
1777                                actWallpaperService = true;
1778                            } else if (action == "android.accessibilityservice.AccessibilityService") {
1779                                actAccessibilityService = true;
1780                            } else if (action == "android.printservice.PrintService") {
1781                                actPrintService = true;
1782                            } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1783                                actHostApduService = true;
1784                            } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1785                                actOffHostApduService = true;
1786                            } else if (action == "android.service.notification.NotificationListenerService") {
1787                                actNotificationListenerService = true;
1788                            } else if (action == "android.service.dreams.DreamService") {
1789                                actDreamService = true;
1790                            }
1791                        } else if (withinProvider) {
1792                            if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1793                                actDocumentsProvider = true;
1794                            }
1795                        }
1796                        if (action == "android.intent.action.SEARCH") {
1797                            isSearchable = true;
1798                        }
1799                    }
1800
1801                    if (tag == "category") {
1802                        String8 category = getAttribute(tree, NAME_ATTR, &error);
1803                        if (error != "") {
1804                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1805                                    error.string());
1806                            goto bail;
1807                        }
1808                        if (withinActivity) {
1809                            if (category == "android.intent.category.LAUNCHER") {
1810                                isLauncherActivity = true;
1811                            } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1812                                isLeanbackLauncherActivity = true;
1813                            } else if (category == "android.intent.category.HOME") {
1814                                catLauncher = true;
1815                            }
1816                        }
1817                    }
1818                }
1819            }
1820
1821            // Pre-1.6 implicitly granted permission compatibility logic
1822            if (targetSdk < 4) {
1823                if (!hasWriteExternalStoragePermission) {
1824                    printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1825                    printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1826                            String8("targetSdkVersion < 4"));
1827                    hasWriteExternalStoragePermission = true;
1828                }
1829                if (!hasReadPhoneStatePermission) {
1830                    printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1831                    printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1832                            String8("targetSdkVersion < 4"));
1833                }
1834            }
1835
1836            // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1837            // force them to always take READ_EXTERNAL_STORAGE as well.  We always
1838            // do this (regardless of target API version) because we can't have
1839            // an app with write permission but not read permission.
1840            if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1841                printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1842                printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1843                        String8("requested WRITE_EXTERNAL_STORAGE"));
1844            }
1845
1846            // Pre-JellyBean call log permission compatibility.
1847            if (targetSdk < 16) {
1848                if (!hasReadCallLogPermission && hasReadContactsPermission) {
1849                    printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1850                    printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1851                            String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
1852                }
1853                if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1854                    printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1855                    printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1856                            String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
1857                }
1858            }
1859
1860            addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
1861                    "default feature for all apps");
1862
1863            const size_t numFeatureGroups = featureGroups.size();
1864            if (numFeatureGroups == 0) {
1865                // If no <feature-group> tags were defined, apply auto-implied features.
1866                printFeatureGroup(commonFeatures, &impliedFeatures);
1867
1868            } else {
1869                // <feature-group> tags are defined, so we ignore implied features and
1870                for (size_t i = 0; i < numFeatureGroups; i++) {
1871                    FeatureGroup& grp = featureGroups.editItemAt(i);
1872
1873                    if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
1874                        grp.openGLESVersion = commonFeatures.openGLESVersion;
1875                    }
1876
1877                    // Merge the features defined in the top level (not inside a <feature-group>)
1878                    // with this feature group.
1879                    const size_t numCommonFeatures = commonFeatures.features.size();
1880                    for (size_t j = 0; j < numCommonFeatures; j++) {
1881                        if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
1882                            grp.features.add(commonFeatures.features.keyAt(j),
1883                                    commonFeatures.features[j]);
1884                        }
1885                    }
1886
1887                   if (!grp.features.isEmpty()) {
1888                        printFeatureGroup(grp);
1889                    }
1890                }
1891            }
1892
1893
1894            if (hasWidgetReceivers) {
1895                printComponentPresence("app-widget");
1896            }
1897            if (hasDeviceAdminReceiver) {
1898                printComponentPresence("device-admin");
1899            }
1900            if (hasImeService) {
1901                printComponentPresence("ime");
1902            }
1903            if (hasWallpaperService) {
1904                printComponentPresence("wallpaper");
1905            }
1906            if (hasAccessibilityService) {
1907                printComponentPresence("accessibility");
1908            }
1909            if (hasPrintService) {
1910                printComponentPresence("print-service");
1911            }
1912            if (hasPaymentService) {
1913                printComponentPresence("payment");
1914            }
1915            if (isSearchable) {
1916                printComponentPresence("search");
1917            }
1918            if (hasDocumentsProvider) {
1919                printComponentPresence("document-provider");
1920            }
1921            if (hasLauncher) {
1922                printComponentPresence("launcher");
1923            }
1924            if (hasNotificationListenerService) {
1925                printComponentPresence("notification-listener");
1926            }
1927            if (hasDreamService) {
1928                printComponentPresence("dream");
1929            }
1930            if (hasCameraActivity) {
1931                printComponentPresence("camera");
1932            }
1933            if (hasCameraSecureActivity) {
1934                printComponentPresence("camera-secure");
1935            }
1936
1937            if (hasMainActivity) {
1938                printf("main\n");
1939            }
1940            if (hasOtherActivities) {
1941                printf("other-activities\n");
1942            }
1943             if (hasOtherReceivers) {
1944                printf("other-receivers\n");
1945            }
1946            if (hasOtherServices) {
1947                printf("other-services\n");
1948            }
1949
1950            // For modern apps, if screen size buckets haven't been specified
1951            // but the new width ranges have, then infer the buckets from them.
1952            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1953                    && requiresSmallestWidthDp > 0) {
1954                int compatWidth = compatibleWidthLimitDp;
1955                if (compatWidth <= 0) {
1956                    compatWidth = requiresSmallestWidthDp;
1957                }
1958                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1959                    smallScreen = -1;
1960                } else {
1961                    smallScreen = 0;
1962                }
1963                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1964                    normalScreen = -1;
1965                } else {
1966                    normalScreen = 0;
1967                }
1968                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1969                    largeScreen = -1;
1970                } else {
1971                    largeScreen = 0;
1972                }
1973                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1974                    xlargeScreen = -1;
1975                } else {
1976                    xlargeScreen = 0;
1977                }
1978            }
1979
1980            // Determine default values for any unspecified screen sizes,
1981            // based on the target SDK of the package.  As of 4 (donut)
1982            // the screen size support was introduced, so all default to
1983            // enabled.
1984            if (smallScreen > 0) {
1985                smallScreen = targetSdk >= 4 ? -1 : 0;
1986            }
1987            if (normalScreen > 0) {
1988                normalScreen = -1;
1989            }
1990            if (largeScreen > 0) {
1991                largeScreen = targetSdk >= 4 ? -1 : 0;
1992            }
1993            if (xlargeScreen > 0) {
1994                // Introduced in Gingerbread.
1995                xlargeScreen = targetSdk >= 9 ? -1 : 0;
1996            }
1997            if (anyDensity > 0) {
1998                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1999                        || compatibleWidthLimitDp > 0) ? -1 : 0;
2000            }
2001            printf("supports-screens:");
2002            if (smallScreen != 0) {
2003                printf(" 'small'");
2004            }
2005            if (normalScreen != 0) {
2006                printf(" 'normal'");
2007            }
2008            if (largeScreen != 0) {
2009                printf(" 'large'");
2010            }
2011            if (xlargeScreen != 0) {
2012                printf(" 'xlarge'");
2013            }
2014            printf("\n");
2015            printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
2016            if (requiresSmallestWidthDp > 0) {
2017                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
2018            }
2019            if (compatibleWidthLimitDp > 0) {
2020                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
2021            }
2022            if (largestWidthLimitDp > 0) {
2023                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
2024            }
2025
2026            printf("locales:");
2027            const size_t NL = locales.size();
2028            for (size_t i=0; i<NL; i++) {
2029                const char* localeStr =  locales[i].string();
2030                if (localeStr == NULL || strlen(localeStr) == 0) {
2031                    localeStr = "--_--";
2032                }
2033                printf(" '%s'", localeStr);
2034            }
2035            printf("\n");
2036
2037            printf("densities:");
2038            const size_t ND = densities.size();
2039            for (size_t i=0; i<ND; i++) {
2040                printf(" '%d'", densities[i]);
2041            }
2042            printf("\n");
2043
2044            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2045            if (dir != NULL) {
2046                if (dir->getFileCount() > 0) {
2047                    printf("native-code:");
2048                    for (size_t i=0; i<dir->getFileCount(); i++) {
2049                        printf(" '%s'", ResTable::normalizeForOutput(
2050                                dir->getFileName(i).string()).string());
2051                    }
2052                    printf("\n");
2053                }
2054                delete dir;
2055            }
2056        } else if (strcmp("badger", option) == 0) {
2057            printf("%s", CONSOLE_DATA);
2058        } else if (strcmp("configurations", option) == 0) {
2059            Vector<ResTable_config> configs;
2060            res.getConfigurations(&configs);
2061            const size_t N = configs.size();
2062            for (size_t i=0; i<N; i++) {
2063                printf("%s\n", configs[i].toString().string());
2064            }
2065        } else {
2066            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2067            goto bail;
2068        }
2069    }
2070
2071    result = NO_ERROR;
2072
2073bail:
2074    if (asset) {
2075        delete asset;
2076    }
2077    return (result != NO_ERROR);
2078}
2079
2080
2081/*
2082 * Handle the "add" command, which wants to add files to a new or
2083 * pre-existing archive.
2084 */
2085int doAdd(Bundle* bundle)
2086{
2087    ZipFile* zip = NULL;
2088    status_t result = UNKNOWN_ERROR;
2089    const char* zipFileName;
2090
2091    if (bundle->getUpdate()) {
2092        /* avoid confusion */
2093        fprintf(stderr, "ERROR: can't use '-u' with add\n");
2094        goto bail;
2095    }
2096
2097    if (bundle->getFileSpecCount() < 1) {
2098        fprintf(stderr, "ERROR: must specify zip file name\n");
2099        goto bail;
2100    }
2101    zipFileName = bundle->getFileSpecEntry(0);
2102
2103    if (bundle->getFileSpecCount() < 2) {
2104        fprintf(stderr, "NOTE: nothing to do\n");
2105        goto bail;
2106    }
2107
2108    zip = openReadWrite(zipFileName, true);
2109    if (zip == NULL) {
2110        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2111        goto bail;
2112    }
2113
2114    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2115        const char* fileName = bundle->getFileSpecEntry(i);
2116
2117        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2118            printf(" '%s'... (from gzip)\n", fileName);
2119            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2120        } else {
2121            if (bundle->getJunkPath()) {
2122                String8 storageName = String8(fileName).getPathLeaf();
2123                printf(" '%s' as '%s'...\n", fileName,
2124                        ResTable::normalizeForOutput(storageName.string()).string());
2125                result = zip->add(fileName, storageName.string(),
2126                                  bundle->getCompressionMethod(), NULL);
2127            } else {
2128                printf(" '%s'...\n", fileName);
2129                result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2130            }
2131        }
2132        if (result != NO_ERROR) {
2133            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2134            if (result == NAME_NOT_FOUND) {
2135                fprintf(stderr, ": file not found\n");
2136            } else if (result == ALREADY_EXISTS) {
2137                fprintf(stderr, ": already exists in archive\n");
2138            } else {
2139                fprintf(stderr, "\n");
2140            }
2141            goto bail;
2142        }
2143    }
2144
2145    result = NO_ERROR;
2146
2147bail:
2148    delete zip;
2149    return (result != NO_ERROR);
2150}
2151
2152
2153/*
2154 * Delete files from an existing archive.
2155 */
2156int doRemove(Bundle* bundle)
2157{
2158    ZipFile* zip = NULL;
2159    status_t result = UNKNOWN_ERROR;
2160    const char* zipFileName;
2161
2162    if (bundle->getFileSpecCount() < 1) {
2163        fprintf(stderr, "ERROR: must specify zip file name\n");
2164        goto bail;
2165    }
2166    zipFileName = bundle->getFileSpecEntry(0);
2167
2168    if (bundle->getFileSpecCount() < 2) {
2169        fprintf(stderr, "NOTE: nothing to do\n");
2170        goto bail;
2171    }
2172
2173    zip = openReadWrite(zipFileName, false);
2174    if (zip == NULL) {
2175        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2176            zipFileName);
2177        goto bail;
2178    }
2179
2180    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2181        const char* fileName = bundle->getFileSpecEntry(i);
2182        ZipEntry* entry;
2183
2184        entry = zip->getEntryByName(fileName);
2185        if (entry == NULL) {
2186            printf(" '%s' NOT FOUND\n", fileName);
2187            continue;
2188        }
2189
2190        result = zip->remove(entry);
2191
2192        if (result != NO_ERROR) {
2193            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2194                bundle->getFileSpecEntry(i), zipFileName);
2195            goto bail;
2196        }
2197    }
2198
2199    /* update the archive */
2200    zip->flush();
2201
2202bail:
2203    delete zip;
2204    return (result != NO_ERROR);
2205}
2206
2207static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
2208    const size_t numDirs = dir->getDirs().size();
2209    for (size_t i = 0; i < numDirs; i++) {
2210        bool ignore = ignoreConfig;
2211        const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2212        const char* dirStr = subDir->getLeaf().string();
2213        if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2214            ignore = true;
2215        }
2216        status_t err = addResourcesToBuilder(subDir, builder, ignore);
2217        if (err != NO_ERROR) {
2218            return err;
2219        }
2220    }
2221
2222    const size_t numFiles = dir->getFiles().size();
2223    for (size_t i = 0; i < numFiles; i++) {
2224        sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2225        const size_t numConfigs = gp->getFiles().size();
2226        for (size_t j = 0; j < numConfigs; j++) {
2227            status_t err = NO_ERROR;
2228            if (ignoreConfig) {
2229                err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2230            } else {
2231                err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2232            }
2233            if (err != NO_ERROR) {
2234                fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2235                        gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2236                return err;
2237            }
2238        }
2239    }
2240    return NO_ERROR;
2241}
2242
2243static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2244    if (split->isBase()) {
2245        return original;
2246    }
2247
2248    String8 ext(original.getPathExtension());
2249    if (ext == String8(".apk")) {
2250        return String8::format("%s_%s%s",
2251                original.getBasePath().string(),
2252                split->getDirectorySafeName().string(),
2253                ext.string());
2254    }
2255
2256    return String8::format("%s_%s", original.string(),
2257            split->getDirectorySafeName().string());
2258}
2259
2260/*
2261 * Package up an asset directory and associated application files.
2262 */
2263int doPackage(Bundle* bundle)
2264{
2265    const char* outputAPKFile;
2266    int retVal = 1;
2267    status_t err;
2268    sp<AaptAssets> assets;
2269    int N;
2270    FILE* fp;
2271    String8 dependencyFile;
2272    sp<ApkBuilder> builder;
2273
2274    // -c en_XA or/and ar_XB means do pseudolocalization
2275    sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2276    err = configFilter->parse(bundle->getConfigurations());
2277    if (err != NO_ERROR) {
2278        goto bail;
2279    }
2280    if (configFilter->containsPseudo()) {
2281        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2282    }
2283    if (configFilter->containsPseudoBidi()) {
2284        bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
2285    }
2286
2287    N = bundle->getFileSpecCount();
2288    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
2289            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
2290        fprintf(stderr, "ERROR: no input files\n");
2291        goto bail;
2292    }
2293
2294    outputAPKFile = bundle->getOutputAPKFile();
2295
2296    // Make sure the filenames provided exist and are of the appropriate type.
2297    if (outputAPKFile) {
2298        FileType type;
2299        type = getFileType(outputAPKFile);
2300        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2301            fprintf(stderr,
2302                "ERROR: output file '%s' exists but is not regular file\n",
2303                outputAPKFile);
2304            goto bail;
2305        }
2306    }
2307
2308    // Load the assets.
2309    assets = new AaptAssets();
2310
2311    // Set up the resource gathering in assets if we're going to generate
2312    // dependency files. Every time we encounter a resource while slurping
2313    // the tree, we'll add it to these stores so we have full resource paths
2314    // to write to a dependency file.
2315    if (bundle->getGenDependencies()) {
2316        sp<FilePathStore> resPathStore = new FilePathStore;
2317        assets->setFullResPaths(resPathStore);
2318        sp<FilePathStore> assetPathStore = new FilePathStore;
2319        assets->setFullAssetPaths(assetPathStore);
2320    }
2321
2322    err = assets->slurpFromArgs(bundle);
2323    if (err < 0) {
2324        goto bail;
2325    }
2326
2327    if (bundle->getVerbose()) {
2328        assets->print(String8());
2329    }
2330
2331    // Create the ApkBuilder, which will collect the compiled files
2332    // to write to the final APK (or sets of APKs if we are building
2333    // a Split APK.
2334    builder = new ApkBuilder(configFilter);
2335
2336    // If we are generating a Split APK, find out which configurations to split on.
2337    if (bundle->getSplitConfigurations().size() > 0) {
2338        const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2339        const size_t numSplits = splitStrs.size();
2340        for (size_t i = 0; i < numSplits; i++) {
2341            std::set<ConfigDescription> configs;
2342            if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2343                fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2344                goto bail;
2345            }
2346
2347            err = builder->createSplitForConfigs(configs);
2348            if (err != NO_ERROR) {
2349                goto bail;
2350            }
2351        }
2352    }
2353
2354    // If they asked for any fileAs that need to be compiled, do so.
2355    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
2356        err = buildResources(bundle, assets, builder);
2357        if (err != 0) {
2358            goto bail;
2359        }
2360    }
2361
2362    // At this point we've read everything and processed everything.  From here
2363    // on out it's just writing output files.
2364    if (SourcePos::hasErrors()) {
2365        goto bail;
2366    }
2367
2368    // Update symbols with information about which ones are needed as Java symbols.
2369    assets->applyJavaSymbols();
2370    if (SourcePos::hasErrors()) {
2371        goto bail;
2372    }
2373
2374    // If we've been asked to generate a dependency file, do that here
2375    if (bundle->getGenDependencies()) {
2376        // If this is the packaging step, generate the dependency file next to
2377        // the output apk (e.g. bin/resources.ap_.d)
2378        if (outputAPKFile) {
2379            dependencyFile = String8(outputAPKFile);
2380            // Add the .d extension to the dependency file.
2381            dependencyFile.append(".d");
2382        } else {
2383            // Else if this is the R.java dependency generation step,
2384            // generate the dependency file in the R.java package subdirectory
2385            // e.g. gen/com/foo/app/R.java.d
2386            dependencyFile = String8(bundle->getRClassDir());
2387            dependencyFile.appendPath("R.java.d");
2388        }
2389        // Make sure we have a clean dependency file to start with
2390        fp = fopen(dependencyFile, "w");
2391        fclose(fp);
2392    }
2393
2394    // Write out R.java constants
2395    if (!assets->havePrivateSymbols()) {
2396        if (bundle->getCustomPackage() == NULL) {
2397            // Write the R.java file into the appropriate class directory
2398            // e.g. gen/com/foo/app/R.java
2399            err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2400        } else {
2401            const String8 customPkg(bundle->getCustomPackage());
2402            err = writeResourceSymbols(bundle, assets, customPkg, true);
2403        }
2404        if (err < 0) {
2405            goto bail;
2406        }
2407        // If we have library files, we're going to write our R.java file into
2408        // the appropriate class directory for those libraries as well.
2409        // e.g. gen/com/foo/app/lib/R.java
2410        if (bundle->getExtraPackages() != NULL) {
2411            // Split on colon
2412            String8 libs(bundle->getExtraPackages());
2413            char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2414            while (packageString != NULL) {
2415                // Write the R.java file out with the correct package name
2416                err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2417                if (err < 0) {
2418                    goto bail;
2419                }
2420                packageString = strtok(NULL, ":");
2421            }
2422            libs.unlockBuffer();
2423        }
2424    } else {
2425        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2426        if (err < 0) {
2427            goto bail;
2428        }
2429        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2430        if (err < 0) {
2431            goto bail;
2432        }
2433    }
2434
2435    // Write out the ProGuard file
2436    err = writeProguardFile(bundle, assets);
2437    if (err < 0) {
2438        goto bail;
2439    }
2440
2441    // Write the apk
2442    if (outputAPKFile) {
2443        // Gather all resources and add them to the APK Builder. The builder will then
2444        // figure out which Split they belong in.
2445        err = addResourcesToBuilder(assets, builder);
2446        if (err != NO_ERROR) {
2447            goto bail;
2448        }
2449
2450        const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2451        const size_t numSplits = splits.size();
2452        for (size_t i = 0; i < numSplits; i++) {
2453            const sp<ApkSplit>& split = splits[i];
2454            String8 outputPath = buildApkName(String8(outputAPKFile), split);
2455            err = writeAPK(bundle, outputPath, split);
2456            if (err != NO_ERROR) {
2457                fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2458                goto bail;
2459            }
2460        }
2461    }
2462
2463    // If we've been asked to generate a dependency file, we need to finish up here.
2464    // the writeResourceSymbols and writeAPK functions have already written the target
2465    // half of the dependency file, now we need to write the prerequisites. (files that
2466    // the R.java file or .ap_ file depend on)
2467    if (bundle->getGenDependencies()) {
2468        // Now that writeResourceSymbols or writeAPK has taken care of writing
2469        // the targets to our dependency file, we'll write the prereqs
2470        fp = fopen(dependencyFile, "a+");
2471        fprintf(fp, " : ");
2472        bool includeRaw = (outputAPKFile != NULL);
2473        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2474        // Also manually add the AndroidManifeset since it's not under res/ or assets/
2475        // and therefore was not added to our pathstores during slurping
2476        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2477        fclose(fp);
2478    }
2479
2480    retVal = 0;
2481bail:
2482    if (SourcePos::hasErrors()) {
2483        SourcePos::printErrors(stderr);
2484    }
2485    return retVal;
2486}
2487
2488/*
2489 * Do PNG Crunching
2490 * PRECONDITIONS
2491 *  -S flag points to a source directory containing drawable* folders
2492 *  -C flag points to destination directory. The folder structure in the
2493 *     source directory will be mirrored to the destination (cache) directory
2494 *
2495 * POSTCONDITIONS
2496 *  Destination directory will be updated to match the PNG files in
2497 *  the source directory.
2498 */
2499int doCrunch(Bundle* bundle)
2500{
2501    fprintf(stdout, "Crunching PNG Files in ");
2502    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2503    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2504
2505    updatePreProcessedCache(bundle);
2506
2507    return NO_ERROR;
2508}
2509
2510/*
2511 * Do PNG Crunching on a single flag
2512 *  -i points to a single png file
2513 *  -o points to a single png output file
2514 */
2515int doSingleCrunch(Bundle* bundle)
2516{
2517    fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2518    fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2519
2520    String8 input(bundle->getSingleCrunchInputFile());
2521    String8 output(bundle->getSingleCrunchOutputFile());
2522
2523    if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2524        // we can't return the status_t as it gets truncate to the lower 8 bits.
2525        return 42;
2526    }
2527
2528    return NO_ERROR;
2529}
2530
2531char CONSOLE_DATA[2925] = {
2532    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2533    32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2534    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2535    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2536    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2537    86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2538    62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2539    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2540    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2541    81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2542    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2543    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2544    32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2545    59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2546    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2547    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2548    59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2549    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2550    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2551    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2552    58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2553    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2554    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2555    47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2556    121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2557    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2558    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2559    81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2560    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2561    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2562    32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2563    59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2564    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2565    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2566    59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2567    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2568    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2569    32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2570    70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2571    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2572    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2573    32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2574    81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2575    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2576    32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2577    81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2578    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2579    32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2580    59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2581    60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2582    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2583    61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2584    61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2585    46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2586    32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2587    59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2588    109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2589    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2590    67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2591    59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2592    61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2593    32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2594    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2595    73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2596    59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2597    32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2598    59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2599    46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2600    97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2601    46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2602    119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2603    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2604    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2605    32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2606    119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2607    59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2608    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2609    32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2610    81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2611    87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2612    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2613    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2614    45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2615    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2616    32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2617    81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2618    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2619    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2620    59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2621    59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2622    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2623    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2624    81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2625    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2626    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2627    32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2628    81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2629    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2630    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2631    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2632    32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2633    32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2634    32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2635    81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2636    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2637    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2638    58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2639    61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2640    32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2641    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2642    81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2643    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2644    32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2645    32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2646    81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2647    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2648    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2649    59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2650    59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2651    32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2652    32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2653    58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2654    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2655    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2656    61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2657    59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2658    32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2659    32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2660    32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2661    61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2662    32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2663    32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2664    59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2665    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2666    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2667    59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2668    59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2669    32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2670    32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2671    32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2672    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2673    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2674    46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2675    46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2676    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2677    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2678    59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2679    59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2680    32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2681    32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2682    32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2683    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2684    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2685    59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2686    59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2687    32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2688    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2689    32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2690    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2691    10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2692    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2693    32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2694    32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2695  };
2696