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