Resource.cpp revision e6b680364dd992907a8d2037685a2e500d188dfb
1a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch//
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2006 The Android Open Source Project
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Build resource files from raw assets.
5a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch//
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "Main.h"
758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "AaptAssets.h"
858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "StringPool.h"
958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "XMLNode.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ResourceTable.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "Images.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "CrunchCache.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "FileFinder.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "CacheUpdater.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#define NOISY(x) // x
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// ==========================================================================
20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// ==========================================================================
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// ==========================================================================
22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
23a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochclass PackageInfo
24010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles){
25010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)public:
26a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    PackageInfo()
277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ~PackageInfo()
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    {
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    status_t parsePackage(const sp<AaptGroup>& grp);
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ==========================================================================
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ==========================================================================
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ==========================================================================
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static String8 parseResourceName(const String8& leaf)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const char* firstDot = strchr(leaf.string(), '.');
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const char* str = leaf.string();
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (firstDot) {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return String8(str, firstDot-str);
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return String8(str);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ResourceTypeSet::ResourceTypeSet()
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    :RefBase(),
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     KeyedVector<String8,sp<AaptGroup> >()
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
560529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch}
570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
580529e5d033099cbfc42635f6f6183833b09dff6eBen MurdochFilePathStore::FilePathStore()
590529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    :RefBase(),
600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     Vector<String8>()
610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch{
620529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch}
630529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ResourceDirIterator
650529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch{
660529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochpublic:
670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    {
700529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
710529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inline const sp<AaptGroup>& getGroup() const { return mGroup; }
730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    inline const sp<AaptFile>& getFile() const { return mFile; }
740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    inline const String8& getBaseName() const { return mBaseName; }
760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    inline const String8& getLeafName() const { return mLeafName; }
770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    inline String8 getPath() const { return mPath; }
780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    inline const ResTable_config& getParams() const { return mParams; }
790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    enum {
810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        EOD = 1
820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    };
830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ssize_t next()
850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    {
860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        while (true) {
870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            sp<AaptGroup> group;
880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            sp<AaptFile> file;
890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            // Try to get next file in this current group.
910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) {
920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                group = mGroup;
930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                file = group->getFiles().valueAt(mGroupPos++);
940529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
950529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            // Try to get the next group/file in this directory
960529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            } else if (mSetPos < mSet->size()) {
970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                mGroup = group = mSet->valueAt(mSetPos++);
980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                if (group->getFiles().size() < 1) {
990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                    continue;
1000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                }
1010529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                file = group->getFiles().valueAt(0);
1020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                mGroupPos = 1;
1030529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1040529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            // All done!
1050529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            } else {
1060529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                return EOD;
1070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            }
1080529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1090529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            mFile = file;
1100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            String8 leaf(group->getLeaf());
1120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            mLeafName = String8(leaf);
1130529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            mParams = file->getGroupEntry().toParams();
1140529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
1150529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                   group->getPath().string(), mParams.mcc, mParams.mnc,
1160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                   mParams.language[0] ? mParams.language[0] : '-',
1170529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                   mParams.language[1] ? mParams.language[1] : '-',
1180529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                   mParams.country[0] ? mParams.country[0] : '-',
1190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                   mParams.country[1] ? mParams.country[1] : '-',
1200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                   mParams.orientation, mParams.uiMode,
1210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                   mParams.density, mParams.touchscreen, mParams.keyboard,
1220529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                   mParams.inputFlags, mParams.navigation));
1230529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            mPath = "res";
1240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            mPath.appendPath(file->getGroupEntry().toDirName(mResType));
1250529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            mPath.appendPath(leaf);
1260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            mBaseName = parseResourceName(leaf);
1270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            if (mBaseName == "") {
1280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                fprintf(stderr, "Error: malformed resource filename %s\n",
1290529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                        file->getPrintableSource().string());
1300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                return UNKNOWN_ERROR;
1310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            }
1320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            NOISY(printf("file name=%s\n", mBaseName.string()));
1340529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            return NO_ERROR;
1360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        }
1370529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
1380529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochprivate:
1400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    String8 mResType;
1410529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1420529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    const sp<ResourceTypeSet> mSet;
1430529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    size_t mSetPos;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1450529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    sp<AaptGroup> mGroup;
1460529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    size_t mGroupPos;
1470529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1480529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    sp<AaptFile> mFile;
1490529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    String8 mBaseName;
1500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    String8 mLeafName;
1510529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    String8 mPath;
1520529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    ResTable_config mParams;
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)};
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ==========================================================================
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ==========================================================================
1570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch// ==========================================================================
1580529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1590529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochbool isValidResourceType(const String8& type)
1600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch{
1610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    return type == "anim" || type == "animator" || type == "interpolator"
1620529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        || type == "drawable" || type == "layout"
1630529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        || type == "values" || type == "xml" || type == "raw"
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        || type == "color" || type == "menu" || type == "mipmap";
1650529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch}
1660529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochstatic sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
1680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch{
1690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
1700529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    sp<AaptFile> file;
1710529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (group != NULL) {
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        file = group->getFiles().valueFor(AaptGroupEntry());
1730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        if (file != NULL) {
1740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            return file;
1750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        }
1760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
1770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (!makeIfNecessary) {
1790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        return NULL;
1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            NULL, String8());
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochstatic status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
1860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    const sp<AaptGroup>& grp)
1870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch{
1880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (grp->getFiles().size() != 1) {
1890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
1900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                grp->getFiles().valueAt(0)->getPrintableSource().string());
1910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    sp<AaptFile> file = grp->getFiles().valueAt(0);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1950529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    ResXMLTree block;
1960529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    status_t err = parseXMLResource(file, &block);
1970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (err != NO_ERROR) {
1980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        return err;
1990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
2000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    //printXMLBlock(&block);
2010529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    ResXMLTree::event_code_t code;
2030529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    while ((code=block.next()) != ResXMLTree::START_TAG
2040529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch           && code != ResXMLTree::END_DOCUMENT
2050529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch           && code != ResXMLTree::BAD_DOCUMENT) {
2060529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
2070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2080529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    size_t len;
2090529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (code != ResXMLTree::START_TAG) {
2100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        fprintf(stderr, "%s:%d: No start tag found\n",
2110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                file->getPrintableSource().string(), block.getLineNumber());
2120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        return UNKNOWN_ERROR;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2140529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) {
2150529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n",
2160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                file->getPrintableSource().string(), block.getLineNumber(),
2170529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                String8(block.getElementName(&len)).string());
2180529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        return UNKNOWN_ERROR;
2190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
2200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    ssize_t nameIndex = block.indexOfAttribute(NULL, "package");
2220529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (nameIndex < 0) {
2230529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n",
2240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                file->getPrintableSource().string(), block.getLineNumber());
2250529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        return UNKNOWN_ERROR;
2260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
2270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len)));
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    String16 uses_sdk16("uses-sdk");
2310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    while ((code=block.next()) != ResXMLTree::END_DOCUMENT
2320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch           && code != ResXMLTree::BAD_DOCUMENT) {
2330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        if (code == ResXMLTree::START_TAG) {
2340529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) {
2350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
2360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                                             "minSdkVersion");
2370529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                if (minSdkIndex >= 0) {
2380529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                    const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
2390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                    const char* minSdk8 = strdup(String8(minSdk16).string());
2400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                    bundle->setManifestMinSdkVersion(minSdk8);
2410529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                }
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2440529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
2450529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2460529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    return NO_ERROR;
2470529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch}
2480529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2490529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch// ==========================================================================
2500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch// ==========================================================================
2510529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch// ==========================================================================
2520529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2530529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochstatic status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,
2540529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                  ResourceTable* table,
2550529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                  const sp<ResourceTypeSet>& set,
2560529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                  const char* resType)
2570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch{
2580529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    String8 type8(resType);
2590529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    String16 type16(resType);
2600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    bool hasErrors = false;
2620529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2630529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    ResourceDirIterator it(set, String8(resType));
2640529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    ssize_t res;
2650529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    while ((res=it.next()) == NO_ERROR) {
2660529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        if (bundle->getVerbose()) {
2670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            printf("    (new resource id %s from %s)\n",
2680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                   it.getBaseName().string(), it.getFile()->getPrintableSource().string());
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
270a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch        String16 baseName(it.getBaseName());
2710529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        const char16_t* str = baseName.string();
2720529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        const char16_t* const end = str + baseName.size();
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        while (str < end) {
2740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            if (!((*str >= 'a' && *str <= 'z')
2750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                    || (*str >= '0' && *str <= '9')
2760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                    || *str == '_' || *str == '.')) {
2770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",
2780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                        it.getPath().string());
2790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                hasErrors = true;
2800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            }
2810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            str++;
2820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        }
2830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        String8 resPath = it.getPath();
2840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        resPath.convertToResPath();
2850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),
2860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                        type16,
2870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                        baseName,
2880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                        String16(resPath),
2890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                        NULL,
2900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                        &it.getParams());
2910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);
2920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
2930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2940529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2960529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
2970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochstatic status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
2980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                          const sp<ResourceTypeSet>& set, const char* type)
2990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch{
3000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    bool hasErrors = false;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ssize_t res = NO_ERROR;
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (bundle->getUseCrunchCache() == false) {
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ResourceDirIterator it(set, String8(type));
304        Vector<sp<AaptFile> > newNameFiles;
305        Vector<String8> newNamePaths;
306        while ((res=it.next()) == NO_ERROR) {
307            res = preProcessImage(bundle, assets, it.getFile(), NULL);
308            if (res < NO_ERROR) {
309                hasErrors = true;
310            }
311        }
312    }
313    return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
314}
315
316status_t postProcessImages(const sp<AaptAssets>& assets,
317                           ResourceTable* table,
318                           const sp<ResourceTypeSet>& set)
319{
320    ResourceDirIterator it(set, String8("drawable"));
321    bool hasErrors = false;
322    ssize_t res;
323    while ((res=it.next()) == NO_ERROR) {
324        res = postProcessImage(assets, table, it.getFile());
325        if (res < NO_ERROR) {
326            hasErrors = true;
327        }
328    }
329
330    return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
331}
332
333static void collect_files(const sp<AaptDir>& dir,
334        KeyedVector<String8, sp<ResourceTypeSet> >* resources)
335{
336    const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles();
337    int N = groups.size();
338    for (int i=0; i<N; i++) {
339        String8 leafName = groups.keyAt(i);
340        const sp<AaptGroup>& group = groups.valueAt(i);
341
342        const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files
343                = group->getFiles();
344
345        if (files.size() == 0) {
346            continue;
347        }
348
349        String8 resType = files.valueAt(0)->getResourceType();
350
351        ssize_t index = resources->indexOfKey(resType);
352
353        if (index < 0) {
354            sp<ResourceTypeSet> set = new ResourceTypeSet();
355            NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n",
356                    leafName.string(), group->getPath().string(), group.get()));
357            set->add(leafName, group);
358            resources->add(resType, set);
359        } else {
360            sp<ResourceTypeSet> set = resources->valueAt(index);
361            index = set->indexOfKey(leafName);
362            if (index < 0) {
363                NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n",
364                        leafName.string(), group->getPath().string(), group.get()));
365                set->add(leafName, group);
366            } else {
367                sp<AaptGroup> existingGroup = set->valueAt(index);
368                NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n",
369                        leafName.string(), group->getPath().string(), group.get()));
370                for (size_t j=0; j<files.size(); j++) {
371                    NOISY(printf("Adding file %s in group %s resType %s\n",
372                        files.valueAt(j)->getSourceFile().string(),
373                        files.keyAt(j).toDirName(String8()).string(),
374                        resType.string()));
375                    status_t err = existingGroup->addFile(files.valueAt(j));
376                }
377            }
378        }
379    }
380}
381
382static void collect_files(const sp<AaptAssets>& ass,
383        KeyedVector<String8, sp<ResourceTypeSet> >* resources)
384{
385    const Vector<sp<AaptDir> >& dirs = ass->resDirs();
386    int N = dirs.size();
387
388    for (int i=0; i<N; i++) {
389        sp<AaptDir> d = dirs.itemAt(i);
390        NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
391                d->getLeaf().string()));
392        collect_files(d, resources);
393
394        // don't try to include the res dir
395        NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string()));
396        ass->removeDir(d->getLeaf());
397    }
398}
399
400enum {
401    ATTR_OKAY = -1,
402    ATTR_NOT_FOUND = -2,
403    ATTR_LEADING_SPACES = -3,
404    ATTR_TRAILING_SPACES = -4
405};
406static int validateAttr(const String8& path, const ResTable& table,
407        const ResXMLParser& parser,
408        const char* ns, const char* attr, const char* validChars, bool required)
409{
410    size_t len;
411
412    ssize_t index = parser.indexOfAttribute(ns, attr);
413    const uint16_t* str;
414    Res_value value;
415    if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) {
416        const ResStringPool* pool = &parser.getStrings();
417        if (value.dataType == Res_value::TYPE_REFERENCE) {
418            uint32_t specFlags = 0;
419            int strIdx;
420            if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) {
421                fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n",
422                        path.string(), parser.getLineNumber(),
423                        String8(parser.getElementName(&len)).string(), attr,
424                        value.data);
425                return ATTR_NOT_FOUND;
426            }
427
428            pool = table.getTableStringBlock(strIdx);
429            #if 0
430            if (pool != NULL) {
431                str = pool->stringAt(value.data, &len);
432            }
433            printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr,
434                    specFlags, strIdx, str != NULL ? String8(str).string() : "???");
435            #endif
436            if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) {
437                fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n",
438                        path.string(), parser.getLineNumber(),
439                        String8(parser.getElementName(&len)).string(), attr,
440                        specFlags);
441                return ATTR_NOT_FOUND;
442            }
443        }
444        if (value.dataType == Res_value::TYPE_STRING) {
445            if (pool == NULL) {
446                fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n",
447                        path.string(), parser.getLineNumber(),
448                        String8(parser.getElementName(&len)).string(), attr);
449                return ATTR_NOT_FOUND;
450            }
451            if ((str=pool->stringAt(value.data, &len)) == NULL) {
452                fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
453                        path.string(), parser.getLineNumber(),
454                        String8(parser.getElementName(&len)).string(), attr);
455                return ATTR_NOT_FOUND;
456            }
457        } else {
458            fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n",
459                    path.string(), parser.getLineNumber(),
460                    String8(parser.getElementName(&len)).string(), attr,
461                    value.dataType);
462            return ATTR_NOT_FOUND;
463        }
464        if (validChars) {
465            for (size_t i=0; i<len; i++) {
466                uint16_t c = str[i];
467                const char* p = validChars;
468                bool okay = false;
469                while (*p) {
470                    if (c == *p) {
471                        okay = true;
472                        break;
473                    }
474                    p++;
475                }
476                if (!okay) {
477                    fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n",
478                            path.string(), parser.getLineNumber(),
479                            String8(parser.getElementName(&len)).string(), attr, (char)str[i]);
480                    return (int)i;
481                }
482            }
483        }
484        if (*str == ' ') {
485            fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n",
486                    path.string(), parser.getLineNumber(),
487                    String8(parser.getElementName(&len)).string(), attr);
488            return ATTR_LEADING_SPACES;
489        }
490        if (str[len-1] == ' ') {
491            fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n",
492                    path.string(), parser.getLineNumber(),
493                    String8(parser.getElementName(&len)).string(), attr);
494            return ATTR_TRAILING_SPACES;
495        }
496        return ATTR_OKAY;
497    }
498    if (required) {
499        fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n",
500                path.string(), parser.getLineNumber(),
501                String8(parser.getElementName(&len)).string(), attr);
502        return ATTR_NOT_FOUND;
503    }
504    return ATTR_OKAY;
505}
506
507static void checkForIds(const String8& path, ResXMLParser& parser)
508{
509    ResXMLTree::event_code_t code;
510    while ((code=parser.next()) != ResXMLTree::END_DOCUMENT
511           && code > ResXMLTree::BAD_DOCUMENT) {
512        if (code == ResXMLTree::START_TAG) {
513            ssize_t index = parser.indexOfAttribute(NULL, "id");
514            if (index >= 0) {
515                fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
516                        path.string(), parser.getLineNumber());
517            }
518        }
519    }
520}
521
522static bool applyFileOverlay(Bundle *bundle,
523                             const sp<AaptAssets>& assets,
524                             sp<ResourceTypeSet> *baseSet,
525                             const char *resType)
526{
527    if (bundle->getVerbose()) {
528        printf("applyFileOverlay for %s\n", resType);
529    }
530
531    // Replace any base level files in this category with any found from the overlay
532    // Also add any found only in the overlay.
533    sp<AaptAssets> overlay = assets->getOverlay();
534    String8 resTypeString(resType);
535
536    // work through the linked list of overlays
537    while (overlay.get()) {
538        KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources();
539
540        // get the overlay resources of the requested type
541        ssize_t index = overlayRes->indexOfKey(resTypeString);
542        if (index >= 0) {
543            sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index);
544
545            // for each of the resources, check for a match in the previously built
546            // non-overlay "baseset".
547            size_t overlayCount = overlaySet->size();
548            for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
549                if (bundle->getVerbose()) {
550                    printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
551                }
552                size_t baseIndex = UNKNOWN_ERROR;
553                if (baseSet->get() != NULL) {
554                    baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
555                }
556                if (baseIndex < UNKNOWN_ERROR) {
557                    // look for same flavor.  For a given file (strings.xml, for example)
558                    // there may be a locale specific or other flavors - we want to match
559                    // the same flavor.
560                    sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
561                    sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
562
563                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
564                            overlayGroup->getFiles();
565                    if (bundle->getVerbose()) {
566                        DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
567                                baseGroup->getFiles();
568                        for (size_t i=0; i < baseFiles.size(); i++) {
569                            printf("baseFile %zd has flavor %s\n", i,
570                                    baseFiles.keyAt(i).toString().string());
571                        }
572                        for (size_t i=0; i < overlayFiles.size(); i++) {
573                            printf("overlayFile %zd has flavor %s\n", i,
574                                    overlayFiles.keyAt(i).toString().string());
575                        }
576                    }
577
578                    size_t overlayGroupSize = overlayFiles.size();
579                    for (size_t overlayGroupIndex = 0;
580                            overlayGroupIndex<overlayGroupSize;
581                            overlayGroupIndex++) {
582                        size_t baseFileIndex =
583                                baseGroup->getFiles().indexOfKey(overlayFiles.
584                                keyAt(overlayGroupIndex));
585                        if (baseFileIndex < UNKNOWN_ERROR) {
586                            if (bundle->getVerbose()) {
587                                printf("found a match (%zd) for overlay file %s, for flavor %s\n",
588                                        baseFileIndex,
589                                        overlayGroup->getLeaf().string(),
590                                        overlayFiles.keyAt(overlayGroupIndex).toString().string());
591                            }
592                            baseGroup->removeFile(baseFileIndex);
593                        } else {
594                            // didn't find a match fall through and add it..
595                            if (true || bundle->getVerbose()) {
596                                printf("nothing matches overlay file %s, for flavor %s\n",
597                                        overlayGroup->getLeaf().string(),
598                                        overlayFiles.keyAt(overlayGroupIndex).toString().string());
599                            }
600                        }
601                        baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
602                        assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
603                    }
604                } else {
605                    if (baseSet->get() == NULL) {
606                        *baseSet = new ResourceTypeSet();
607                        assets->getResources()->add(String8(resType), *baseSet);
608                    }
609                    // this group doesn't exist (a file that's only in the overlay)
610                    (*baseSet)->add(overlaySet->keyAt(overlayIndex),
611                            overlaySet->valueAt(overlayIndex));
612                    // make sure all flavors are defined in the resources.
613                    sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
614                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
615                            overlayGroup->getFiles();
616                    size_t overlayGroupSize = overlayFiles.size();
617                    for (size_t overlayGroupIndex = 0;
618                            overlayGroupIndex<overlayGroupSize;
619                            overlayGroupIndex++) {
620                        assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
621                    }
622                }
623            }
624            // this overlay didn't have resources for this type
625        }
626        // try next overlay
627        overlay = overlay->getOverlay();
628    }
629    return true;
630}
631
632void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
633        const char* attr8, const char* value)
634{
635    if (value == NULL) {
636        return;
637    }
638
639    const String16 ns(ns8);
640    const String16 attr(attr8);
641
642    if (node->getAttribute(ns, attr) != NULL) {
643        fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
644                        " using existing value in manifest.\n",
645                String8(attr).string(), String8(ns).string());
646        return;
647    }
648
649    node->addAttribute(ns, attr, String16(value));
650}
651
652static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
653        const String16& attrName) {
654    XMLNode::attribute_entry* attr = node->editAttribute(
655            String16("http://schemas.android.com/apk/res/android"), attrName);
656    if (attr != NULL) {
657        String8 name(attr->string);
658
659        // asdf     --> package.asdf
660        // .asdf  .a.b  --> package.asdf package.a.b
661        // asdf.adsf --> asdf.asdf
662        String8 className;
663        const char* p = name.string();
664        const char* q = strchr(p, '.');
665        if (p == q) {
666            className += package;
667            className += name;
668        } else if (q == NULL) {
669            className += package;
670            className += ".";
671            className += name;
672        } else {
673            className += name;
674        }
675        NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string()));
676        attr->string.setTo(String16(className));
677    }
678}
679
680status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
681{
682    root = root->searchElement(String16(), String16("manifest"));
683    if (root == NULL) {
684        fprintf(stderr, "No <manifest> tag.\n");
685        return UNKNOWN_ERROR;
686    }
687
688    addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
689            bundle->getVersionCode());
690    addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
691            bundle->getVersionName());
692
693    if (bundle->getMinSdkVersion() != NULL
694            || bundle->getTargetSdkVersion() != NULL
695            || bundle->getMaxSdkVersion() != NULL) {
696        sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk"));
697        if (vers == NULL) {
698            vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk"));
699            root->insertChildAt(vers, 0);
700        }
701
702        addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
703                bundle->getMinSdkVersion());
704        addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
705                bundle->getTargetSdkVersion());
706        addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
707                bundle->getMaxSdkVersion());
708    }
709
710    if (bundle->getDebugMode()) {
711        sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
712        if (application != NULL) {
713            addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true");
714        }
715    }
716
717    // Deal with manifest package name overrides
718    const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
719    if (manifestPackageNameOverride != NULL) {
720        // Update the actual package name
721        XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package"));
722        if (attr == NULL) {
723            fprintf(stderr, "package name is required with --rename-manifest-package.\n");
724            return UNKNOWN_ERROR;
725        }
726        String8 origPackage(attr->string);
727        attr->string.setTo(String16(manifestPackageNameOverride));
728        NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride));
729
730        // Make class names fully qualified
731        sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
732        if (application != NULL) {
733            fullyQualifyClassName(origPackage, application, String16("name"));
734            fullyQualifyClassName(origPackage, application, String16("backupAgent"));
735
736            Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
737            for (size_t i = 0; i < children.size(); i++) {
738                sp<XMLNode> child = children.editItemAt(i);
739                String8 tag(child->getElementName());
740                if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
741                    fullyQualifyClassName(origPackage, child, String16("name"));
742                } else if (tag == "activity-alias") {
743                    fullyQualifyClassName(origPackage, child, String16("name"));
744                    fullyQualifyClassName(origPackage, child, String16("targetActivity"));
745                }
746            }
747        }
748    }
749
750    // Deal with manifest package name overrides
751    const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride();
752    if (instrumentationPackageNameOverride != NULL) {
753        // Fix up instrumentation targets.
754        Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren());
755        for (size_t i = 0; i < children.size(); i++) {
756            sp<XMLNode> child = children.editItemAt(i);
757            String8 tag(child->getElementName());
758            if (tag == "instrumentation") {
759                XMLNode::attribute_entry* attr = child->editAttribute(
760                        String16("http://schemas.android.com/apk/res/android"), String16("targetPackage"));
761                if (attr != NULL) {
762                    attr->string.setTo(String16(instrumentationPackageNameOverride));
763                }
764            }
765        }
766    }
767
768    return NO_ERROR;
769}
770
771#define ASSIGN_IT(n) \
772        do { \
773            ssize_t index = resources->indexOfKey(String8(#n)); \
774            if (index >= 0) { \
775                n ## s = resources->valueAt(index); \
776            } \
777        } while (0)
778
779status_t updatePreProcessedCache(Bundle* bundle)
780{
781    #if BENCHMARK
782    fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n");
783    long startPNGTime = clock();
784    #endif /* BENCHMARK */
785
786    String8 source(bundle->getResourceSourceDirs()[0]);
787    String8 dest(bundle->getCrunchedOutputDir());
788
789    FileFinder* ff = new SystemFileFinder();
790    CrunchCache cc(source,dest,ff);
791
792    CacheUpdater* cu = new SystemCacheUpdater(bundle);
793    size_t numFiles = cc.crunch(cu);
794
795    if (bundle->getVerbose())
796        fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles);
797
798    delete ff;
799    delete cu;
800
801    #if BENCHMARK
802    fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n"
803            ,(clock() - startPNGTime)/1000.0);
804    #endif /* BENCHMARK */
805    return 0;
806}
807
808status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
809{
810    // First, look for a package file to parse.  This is required to
811    // be able to generate the resource information.
812    sp<AaptGroup> androidManifestFile =
813            assets->getFiles().valueFor(String8("AndroidManifest.xml"));
814    if (androidManifestFile == NULL) {
815        fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
816        return UNKNOWN_ERROR;
817    }
818
819    status_t err = parsePackage(bundle, assets, androidManifestFile);
820    if (err != NO_ERROR) {
821        return err;
822    }
823
824    NOISY(printf("Creating resources for package %s\n",
825                 assets->getPackage().string()));
826
827    ResourceTable table(bundle, String16(assets->getPackage()));
828    err = table.addIncludedResources(bundle, assets);
829    if (err != NO_ERROR) {
830        return err;
831    }
832
833    NOISY(printf("Found %d included resource packages\n", (int)table.size()));
834
835    // Standard flags for compiled XML and optional UTF-8 encoding
836    int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
837
838    /* Only enable UTF-8 if the caller of aapt didn't specifically
839     * request UTF-16 encoding and the parameters of this package
840     * allow UTF-8 to be used.
841     */
842    if (!bundle->getWantUTF16()
843            && bundle->isMinSdkAtLeast(SDK_FROYO)) {
844        xmlFlags |= XML_COMPILE_UTF8;
845    }
846
847    // --------------------------------------------------------------
848    // First, gather all resource information.
849    // --------------------------------------------------------------
850
851    // resType -> leafName -> group
852    KeyedVector<String8, sp<ResourceTypeSet> > *resources =
853            new KeyedVector<String8, sp<ResourceTypeSet> >;
854    collect_files(assets, resources);
855
856    sp<ResourceTypeSet> drawables;
857    sp<ResourceTypeSet> layouts;
858    sp<ResourceTypeSet> anims;
859    sp<ResourceTypeSet> animators;
860    sp<ResourceTypeSet> interpolators;
861    sp<ResourceTypeSet> xmls;
862    sp<ResourceTypeSet> raws;
863    sp<ResourceTypeSet> colors;
864    sp<ResourceTypeSet> menus;
865    sp<ResourceTypeSet> mipmaps;
866
867    ASSIGN_IT(drawable);
868    ASSIGN_IT(layout);
869    ASSIGN_IT(anim);
870    ASSIGN_IT(animator);
871    ASSIGN_IT(interpolator);
872    ASSIGN_IT(xml);
873    ASSIGN_IT(raw);
874    ASSIGN_IT(color);
875    ASSIGN_IT(menu);
876    ASSIGN_IT(mipmap);
877
878    assets->setResources(resources);
879    // now go through any resource overlays and collect their files
880    sp<AaptAssets> current = assets->getOverlay();
881    while(current.get()) {
882        KeyedVector<String8, sp<ResourceTypeSet> > *resources =
883                new KeyedVector<String8, sp<ResourceTypeSet> >;
884        current->setResources(resources);
885        collect_files(current, resources);
886        current = current->getOverlay();
887    }
888    // apply the overlay files to the base set
889    if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
890            !applyFileOverlay(bundle, assets, &layouts, "layout") ||
891            !applyFileOverlay(bundle, assets, &anims, "anim") ||
892            !applyFileOverlay(bundle, assets, &animators, "animator") ||
893            !applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
894            !applyFileOverlay(bundle, assets, &xmls, "xml") ||
895            !applyFileOverlay(bundle, assets, &raws, "raw") ||
896            !applyFileOverlay(bundle, assets, &colors, "color") ||
897            !applyFileOverlay(bundle, assets, &menus, "menu") ||
898            !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
899        return UNKNOWN_ERROR;
900    }
901
902    bool hasErrors = false;
903
904    if (drawables != NULL) {
905        if (bundle->getOutputAPKFile() != NULL) {
906            err = preProcessImages(bundle, assets, drawables, "drawable");
907        }
908        if (err == NO_ERROR) {
909            err = makeFileResources(bundle, assets, &table, drawables, "drawable");
910            if (err != NO_ERROR) {
911                hasErrors = true;
912            }
913        } else {
914            hasErrors = true;
915        }
916    }
917
918    if (mipmaps != NULL) {
919        if (bundle->getOutputAPKFile() != NULL) {
920            err = preProcessImages(bundle, assets, mipmaps, "mipmap");
921        }
922        if (err == NO_ERROR) {
923            err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
924            if (err != NO_ERROR) {
925                hasErrors = true;
926            }
927        } else {
928            hasErrors = true;
929        }
930    }
931
932    if (layouts != NULL) {
933        err = makeFileResources(bundle, assets, &table, layouts, "layout");
934        if (err != NO_ERROR) {
935            hasErrors = true;
936        }
937    }
938
939    if (anims != NULL) {
940        err = makeFileResources(bundle, assets, &table, anims, "anim");
941        if (err != NO_ERROR) {
942            hasErrors = true;
943        }
944    }
945
946    if (animators != NULL) {
947        err = makeFileResources(bundle, assets, &table, animators, "animator");
948        if (err != NO_ERROR) {
949            hasErrors = true;
950        }
951    }
952
953    if (interpolators != NULL) {
954        err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
955        if (err != NO_ERROR) {
956            hasErrors = true;
957        }
958    }
959
960    if (xmls != NULL) {
961        err = makeFileResources(bundle, assets, &table, xmls, "xml");
962        if (err != NO_ERROR) {
963            hasErrors = true;
964        }
965    }
966
967    if (raws != NULL) {
968        err = makeFileResources(bundle, assets, &table, raws, "raw");
969        if (err != NO_ERROR) {
970            hasErrors = true;
971        }
972    }
973
974    // compile resources
975    current = assets;
976    while(current.get()) {
977        KeyedVector<String8, sp<ResourceTypeSet> > *resources =
978                current->getResources();
979
980        ssize_t index = resources->indexOfKey(String8("values"));
981        if (index >= 0) {
982            ResourceDirIterator it(resources->valueAt(index), String8("values"));
983            ssize_t res;
984            while ((res=it.next()) == NO_ERROR) {
985                sp<AaptFile> file = it.getFile();
986                res = compileResourceFile(bundle, assets, file, it.getParams(),
987                                          (current!=assets), &table);
988                if (res != NO_ERROR) {
989                    hasErrors = true;
990                }
991            }
992        }
993        current = current->getOverlay();
994    }
995
996    if (colors != NULL) {
997        err = makeFileResources(bundle, assets, &table, colors, "color");
998        if (err != NO_ERROR) {
999            hasErrors = true;
1000        }
1001    }
1002
1003    if (menus != NULL) {
1004        err = makeFileResources(bundle, assets, &table, menus, "menu");
1005        if (err != NO_ERROR) {
1006            hasErrors = true;
1007        }
1008    }
1009
1010    // --------------------------------------------------------------------
1011    // Assignment of resource IDs and initial generation of resource table.
1012    // --------------------------------------------------------------------
1013
1014    if (table.hasResources()) {
1015        sp<AaptFile> resFile(getResourceFile(assets));
1016        if (resFile == NULL) {
1017            fprintf(stderr, "Error: unable to generate entry for resource data\n");
1018            return UNKNOWN_ERROR;
1019        }
1020
1021        err = table.assignResourceIds();
1022        if (err < NO_ERROR) {
1023            return err;
1024        }
1025    }
1026
1027    // --------------------------------------------------------------
1028    // Finally, we can now we can compile XML files, which may reference
1029    // resources.
1030    // --------------------------------------------------------------
1031
1032    if (layouts != NULL) {
1033        ResourceDirIterator it(layouts, String8("layout"));
1034        while ((err=it.next()) == NO_ERROR) {
1035            String8 src = it.getFile()->getPrintableSource();
1036            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1037            if (err == NO_ERROR) {
1038                ResXMLTree block;
1039                block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1040                checkForIds(src, block);
1041            } else {
1042                hasErrors = true;
1043            }
1044        }
1045
1046        if (err < NO_ERROR) {
1047            hasErrors = true;
1048        }
1049        err = NO_ERROR;
1050    }
1051
1052    if (anims != NULL) {
1053        ResourceDirIterator it(anims, String8("anim"));
1054        while ((err=it.next()) == NO_ERROR) {
1055            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1056            if (err != NO_ERROR) {
1057                hasErrors = true;
1058            }
1059        }
1060
1061        if (err < NO_ERROR) {
1062            hasErrors = true;
1063        }
1064        err = NO_ERROR;
1065    }
1066
1067    if (animators != NULL) {
1068        ResourceDirIterator it(animators, String8("animator"));
1069        while ((err=it.next()) == NO_ERROR) {
1070            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1071            if (err != NO_ERROR) {
1072                hasErrors = true;
1073            }
1074        }
1075
1076        if (err < NO_ERROR) {
1077            hasErrors = true;
1078        }
1079        err = NO_ERROR;
1080    }
1081
1082    if (interpolators != NULL) {
1083        ResourceDirIterator it(interpolators, String8("interpolator"));
1084        while ((err=it.next()) == NO_ERROR) {
1085            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1086            if (err != NO_ERROR) {
1087                hasErrors = true;
1088            }
1089        }
1090
1091        if (err < NO_ERROR) {
1092            hasErrors = true;
1093        }
1094        err = NO_ERROR;
1095    }
1096
1097    if (xmls != NULL) {
1098        ResourceDirIterator it(xmls, String8("xml"));
1099        while ((err=it.next()) == NO_ERROR) {
1100            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1101            if (err != NO_ERROR) {
1102                hasErrors = true;
1103            }
1104        }
1105
1106        if (err < NO_ERROR) {
1107            hasErrors = true;
1108        }
1109        err = NO_ERROR;
1110    }
1111
1112    if (drawables != NULL) {
1113        err = postProcessImages(assets, &table, drawables);
1114        if (err != NO_ERROR) {
1115            hasErrors = true;
1116        }
1117    }
1118
1119    if (colors != NULL) {
1120        ResourceDirIterator it(colors, String8("color"));
1121        while ((err=it.next()) == NO_ERROR) {
1122          err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1123            if (err != NO_ERROR) {
1124                hasErrors = true;
1125            }
1126        }
1127
1128        if (err < NO_ERROR) {
1129            hasErrors = true;
1130        }
1131        err = NO_ERROR;
1132    }
1133
1134    if (menus != NULL) {
1135        ResourceDirIterator it(menus, String8("menu"));
1136        while ((err=it.next()) == NO_ERROR) {
1137            String8 src = it.getFile()->getPrintableSource();
1138            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1139            if (err != NO_ERROR) {
1140                hasErrors = true;
1141            }
1142            ResXMLTree block;
1143            block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1144            checkForIds(src, block);
1145        }
1146
1147        if (err < NO_ERROR) {
1148            hasErrors = true;
1149        }
1150        err = NO_ERROR;
1151    }
1152
1153    if (table.validateLocalizations()) {
1154        hasErrors = true;
1155    }
1156
1157    if (hasErrors) {
1158        return UNKNOWN_ERROR;
1159    }
1160
1161    const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
1162    String8 manifestPath(manifestFile->getPrintableSource());
1163
1164    // Generate final compiled manifest file.
1165    manifestFile->clearData();
1166    sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
1167    if (manifestTree == NULL) {
1168        return UNKNOWN_ERROR;
1169    }
1170    err = massageManifest(bundle, manifestTree);
1171    if (err < NO_ERROR) {
1172        return err;
1173    }
1174    err = compileXmlFile(assets, manifestTree, manifestFile, &table);
1175    if (err < NO_ERROR) {
1176        return err;
1177    }
1178
1179    //block.restart();
1180    //printXMLBlock(&block);
1181
1182    // --------------------------------------------------------------
1183    // Generate the final resource table.
1184    // Re-flatten because we may have added new resource IDs
1185    // --------------------------------------------------------------
1186
1187    ResTable finalResTable;
1188    sp<AaptFile> resFile;
1189
1190    if (table.hasResources()) {
1191        sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
1192        err = table.addSymbols(symbols);
1193        if (err < NO_ERROR) {
1194            return err;
1195        }
1196
1197        resFile = getResourceFile(assets);
1198        if (resFile == NULL) {
1199            fprintf(stderr, "Error: unable to generate entry for resource data\n");
1200            return UNKNOWN_ERROR;
1201        }
1202
1203        err = table.flatten(bundle, resFile);
1204        if (err < NO_ERROR) {
1205            return err;
1206        }
1207
1208        if (bundle->getPublicOutputFile()) {
1209            FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
1210            if (fp == NULL) {
1211                fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
1212                        (const char*)bundle->getPublicOutputFile(), strerror(errno));
1213                return UNKNOWN_ERROR;
1214            }
1215            if (bundle->getVerbose()) {
1216                printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
1217            }
1218            table.writePublicDefinitions(String16(assets->getPackage()), fp);
1219            fclose(fp);
1220        }
1221
1222        // Read resources back in,
1223        finalResTable.add(resFile->getData(), resFile->getSize(), NULL);
1224
1225#if 0
1226        NOISY(
1227              printf("Generated resources:\n");
1228              finalResTable.print();
1229        )
1230#endif
1231    }
1232
1233    // Perform a basic validation of the manifest file.  This time we
1234    // parse it with the comments intact, so that we can use them to
1235    // generate java docs...  so we are not going to write this one
1236    // back out to the final manifest data.
1237    sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(),
1238            manifestFile->getGroupEntry(),
1239            manifestFile->getResourceType());
1240    err = compileXmlFile(assets, manifestFile,
1241            outManifestFile, &table,
1242            XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
1243            | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
1244    if (err < NO_ERROR) {
1245        return err;
1246    }
1247    ResXMLTree block;
1248    block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true);
1249    String16 manifest16("manifest");
1250    String16 permission16("permission");
1251    String16 permission_group16("permission-group");
1252    String16 uses_permission16("uses-permission");
1253    String16 instrumentation16("instrumentation");
1254    String16 application16("application");
1255    String16 provider16("provider");
1256    String16 service16("service");
1257    String16 receiver16("receiver");
1258    String16 activity16("activity");
1259    String16 action16("action");
1260    String16 category16("category");
1261    String16 data16("scheme");
1262    const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
1263        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
1264    const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
1265        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1266    const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz"
1267        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$";
1268    const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz"
1269        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:";
1270    const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz"
1271        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;";
1272    const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1273        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+";
1274    const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1275        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1276    ResXMLTree::event_code_t code;
1277    sp<AaptSymbols> permissionSymbols;
1278    sp<AaptSymbols> permissionGroupSymbols;
1279    while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1280           && code > ResXMLTree::BAD_DOCUMENT) {
1281        if (code == ResXMLTree::START_TAG) {
1282            size_t len;
1283            if (block.getElementNamespace(&len) != NULL) {
1284                continue;
1285            }
1286            if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
1287                if (validateAttr(manifestPath, finalResTable, block, NULL, "package",
1288                                 packageIdentChars, true) != ATTR_OKAY) {
1289                    hasErrors = true;
1290                }
1291                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1292                                 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) {
1293                    hasErrors = true;
1294                }
1295            } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
1296                    || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
1297                const bool isGroup = strcmp16(block.getElementName(&len),
1298                        permission_group16.string()) == 0;
1299                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1300                                 "name", isGroup ? packageIdentCharsWithTheStupid
1301                                 : packageIdentChars, true) != ATTR_OKAY) {
1302                    hasErrors = true;
1303                }
1304                SourcePos srcPos(manifestPath, block.getLineNumber());
1305                sp<AaptSymbols> syms;
1306                if (!isGroup) {
1307                    syms = permissionSymbols;
1308                    if (syms == NULL) {
1309                        sp<AaptSymbols> symbols =
1310                                assets->getSymbolsFor(String8("Manifest"));
1311                        syms = permissionSymbols = symbols->addNestedSymbol(
1312                                String8("permission"), srcPos);
1313                    }
1314                } else {
1315                    syms = permissionGroupSymbols;
1316                    if (syms == NULL) {
1317                        sp<AaptSymbols> symbols =
1318                                assets->getSymbolsFor(String8("Manifest"));
1319                        syms = permissionGroupSymbols = symbols->addNestedSymbol(
1320                                String8("permission_group"), srcPos);
1321                    }
1322                }
1323                size_t len;
1324                ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name");
1325                const uint16_t* id = block.getAttributeStringValue(index, &len);
1326                if (id == NULL) {
1327                    fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
1328                            manifestPath.string(), block.getLineNumber(),
1329                            String8(block.getElementName(&len)).string());
1330                    hasErrors = true;
1331                    break;
1332                }
1333                String8 idStr(id);
1334                char* p = idStr.lockBuffer(idStr.size());
1335                char* e = p + idStr.size();
1336                bool begins_with_digit = true;  // init to true so an empty string fails
1337                while (e > p) {
1338                    e--;
1339                    if (*e >= '0' && *e <= '9') {
1340                      begins_with_digit = true;
1341                      continue;
1342                    }
1343                    if ((*e >= 'a' && *e <= 'z') ||
1344                        (*e >= 'A' && *e <= 'Z') ||
1345                        (*e == '_')) {
1346                      begins_with_digit = false;
1347                      continue;
1348                    }
1349                    if (isGroup && (*e == '-')) {
1350                        *e = '_';
1351                        begins_with_digit = false;
1352                        continue;
1353                    }
1354                    e++;
1355                    break;
1356                }
1357                idStr.unlockBuffer();
1358                // verify that we stopped because we hit a period or
1359                // the beginning of the string, and that the
1360                // identifier didn't begin with a digit.
1361                if (begins_with_digit || (e != p && *(e-1) != '.')) {
1362                  fprintf(stderr,
1363                          "%s:%d: Permission name <%s> is not a valid Java symbol\n",
1364                          manifestPath.string(), block.getLineNumber(), idStr.string());
1365                  hasErrors = true;
1366                }
1367                syms->addStringSymbol(String8(e), idStr, srcPos);
1368                const uint16_t* cmt = block.getComment(&len);
1369                if (cmt != NULL && *cmt != 0) {
1370                    //printf("Comment of %s: %s\n", String8(e).string(),
1371                    //        String8(cmt).string());
1372                    syms->appendComment(String8(e), String16(cmt), srcPos);
1373                } else {
1374                    //printf("No comment for %s\n", String8(e).string());
1375                }
1376                syms->makeSymbolPublic(String8(e), srcPos);
1377            } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
1378                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1379                                 "name", packageIdentChars, true) != ATTR_OKAY) {
1380                    hasErrors = true;
1381                }
1382            } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
1383                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1384                                 "name", classIdentChars, true) != ATTR_OKAY) {
1385                    hasErrors = true;
1386                }
1387                if (validateAttr(manifestPath, finalResTable, block,
1388                                 RESOURCES_ANDROID_NAMESPACE, "targetPackage",
1389                                 packageIdentChars, true) != ATTR_OKAY) {
1390                    hasErrors = true;
1391                }
1392            } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
1393                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1394                                 "name", classIdentChars, false) != ATTR_OKAY) {
1395                    hasErrors = true;
1396                }
1397                if (validateAttr(manifestPath, finalResTable, block,
1398                                 RESOURCES_ANDROID_NAMESPACE, "permission",
1399                                 packageIdentChars, false) != ATTR_OKAY) {
1400                    hasErrors = true;
1401                }
1402                if (validateAttr(manifestPath, finalResTable, block,
1403                                 RESOURCES_ANDROID_NAMESPACE, "process",
1404                                 processIdentChars, false) != ATTR_OKAY) {
1405                    hasErrors = true;
1406                }
1407                if (validateAttr(manifestPath, finalResTable, block,
1408                                 RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1409                                 processIdentChars, false) != ATTR_OKAY) {
1410                    hasErrors = true;
1411                }
1412            } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
1413                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1414                                 "name", classIdentChars, true) != ATTR_OKAY) {
1415                    hasErrors = true;
1416                }
1417                if (validateAttr(manifestPath, finalResTable, block,
1418                                 RESOURCES_ANDROID_NAMESPACE, "authorities",
1419                                 authoritiesIdentChars, true) != ATTR_OKAY) {
1420                    hasErrors = true;
1421                }
1422                if (validateAttr(manifestPath, finalResTable, block,
1423                                 RESOURCES_ANDROID_NAMESPACE, "permission",
1424                                 packageIdentChars, false) != ATTR_OKAY) {
1425                    hasErrors = true;
1426                }
1427                if (validateAttr(manifestPath, finalResTable, block,
1428                                 RESOURCES_ANDROID_NAMESPACE, "process",
1429                                 processIdentChars, false) != ATTR_OKAY) {
1430                    hasErrors = true;
1431                }
1432            } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
1433                       || strcmp16(block.getElementName(&len), receiver16.string()) == 0
1434                       || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
1435                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1436                                 "name", classIdentChars, true) != ATTR_OKAY) {
1437                    hasErrors = true;
1438                }
1439                if (validateAttr(manifestPath, finalResTable, block,
1440                                 RESOURCES_ANDROID_NAMESPACE, "permission",
1441                                 packageIdentChars, false) != ATTR_OKAY) {
1442                    hasErrors = true;
1443                }
1444                if (validateAttr(manifestPath, finalResTable, block,
1445                                 RESOURCES_ANDROID_NAMESPACE, "process",
1446                                 processIdentChars, false) != ATTR_OKAY) {
1447                    hasErrors = true;
1448                }
1449                if (validateAttr(manifestPath, finalResTable, block,
1450                                 RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1451                                 processIdentChars, false) != ATTR_OKAY) {
1452                    hasErrors = true;
1453                }
1454            } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
1455                       || strcmp16(block.getElementName(&len), category16.string()) == 0) {
1456                if (validateAttr(manifestPath, finalResTable, block,
1457                                 RESOURCES_ANDROID_NAMESPACE, "name",
1458                                 packageIdentChars, true) != ATTR_OKAY) {
1459                    hasErrors = true;
1460                }
1461            } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
1462                if (validateAttr(manifestPath, finalResTable, block,
1463                                 RESOURCES_ANDROID_NAMESPACE, "mimeType",
1464                                 typeIdentChars, true) != ATTR_OKAY) {
1465                    hasErrors = true;
1466                }
1467                if (validateAttr(manifestPath, finalResTable, block,
1468                                 RESOURCES_ANDROID_NAMESPACE, "scheme",
1469                                 schemeIdentChars, true) != ATTR_OKAY) {
1470                    hasErrors = true;
1471                }
1472            }
1473        }
1474    }
1475
1476    if (resFile != NULL) {
1477        // These resources are now considered to be a part of the included
1478        // resources, for others to reference.
1479        err = assets->addIncludedResources(resFile);
1480        if (err < NO_ERROR) {
1481            fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n");
1482            return err;
1483        }
1484    }
1485
1486    return err;
1487}
1488
1489static const char* getIndentSpace(int indent)
1490{
1491static const char whitespace[] =
1492"                                                                                       ";
1493
1494    return whitespace + sizeof(whitespace) - 1 - indent*4;
1495}
1496
1497static status_t fixupSymbol(String16* inoutSymbol)
1498{
1499    inoutSymbol->replaceAll('.', '_');
1500    inoutSymbol->replaceAll(':', '_');
1501    return NO_ERROR;
1502}
1503
1504static String16 getAttributeComment(const sp<AaptAssets>& assets,
1505                                    const String8& name,
1506                                    String16* outTypeComment = NULL)
1507{
1508    sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R"));
1509    if (asym != NULL) {
1510        //printf("Got R symbols!\n");
1511        asym = asym->getNestedSymbols().valueFor(String8("attr"));
1512        if (asym != NULL) {
1513            //printf("Got attrs symbols! comment %s=%s\n",
1514            //     name.string(), String8(asym->getComment(name)).string());
1515            if (outTypeComment != NULL) {
1516                *outTypeComment = asym->getTypeComment(name);
1517            }
1518            return asym->getComment(name);
1519        }
1520    }
1521    return String16();
1522}
1523
1524static status_t writeLayoutClasses(
1525    FILE* fp, const sp<AaptAssets>& assets,
1526    const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
1527{
1528    const char* indentStr = getIndentSpace(indent);
1529    if (!includePrivate) {
1530        fprintf(fp, "%s/** @doconly */\n", indentStr);
1531    }
1532    fprintf(fp, "%spublic static final class styleable {\n", indentStr);
1533    indent++;
1534
1535    String16 attr16("attr");
1536    String16 package16(assets->getPackage());
1537
1538    indentStr = getIndentSpace(indent);
1539    bool hasErrors = false;
1540
1541    size_t i;
1542    size_t N = symbols->getNestedSymbols().size();
1543    for (i=0; i<N; i++) {
1544        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
1545        String16 nclassName16(symbols->getNestedSymbols().keyAt(i));
1546        String8 realClassName(nclassName16);
1547        if (fixupSymbol(&nclassName16) != NO_ERROR) {
1548            hasErrors = true;
1549        }
1550        String8 nclassName(nclassName16);
1551
1552        SortedVector<uint32_t> idents;
1553        Vector<uint32_t> origOrder;
1554        Vector<bool> publicFlags;
1555
1556        size_t a;
1557        size_t NA = nsymbols->getSymbols().size();
1558        for (a=0; a<NA; a++) {
1559            const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
1560            int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
1561                    ? sym.int32Val : 0;
1562            bool isPublic = true;
1563            if (code == 0) {
1564                String16 name16(sym.name);
1565                uint32_t typeSpecFlags;
1566                code = assets->getIncludedResources().identifierForName(
1567                    name16.string(), name16.size(),
1568                    attr16.string(), attr16.size(),
1569                    package16.string(), package16.size(), &typeSpecFlags);
1570                if (code == 0) {
1571                    fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
1572                            nclassName.string(), sym.name.string());
1573                    hasErrors = true;
1574                }
1575                isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
1576            }
1577            idents.add(code);
1578            origOrder.add(code);
1579            publicFlags.add(isPublic);
1580        }
1581
1582        NA = idents.size();
1583
1584        bool deprecated = false;
1585
1586        String16 comment = symbols->getComment(realClassName);
1587        fprintf(fp, "%s/** ", indentStr);
1588        if (comment.size() > 0) {
1589            String8 cmt(comment);
1590            fprintf(fp, "%s\n", cmt.string());
1591            if (strstr(cmt.string(), "@deprecated") != NULL) {
1592                deprecated = true;
1593            }
1594        } else {
1595            fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
1596        }
1597        bool hasTable = false;
1598        for (a=0; a<NA; a++) {
1599            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1600            if (pos >= 0) {
1601                if (!hasTable) {
1602                    hasTable = true;
1603                    fprintf(fp,
1604                            "%s   <p>Includes the following attributes:</p>\n"
1605                            "%s   <table>\n"
1606                            "%s   <colgroup align=\"left\" />\n"
1607                            "%s   <colgroup align=\"left\" />\n"
1608                            "%s   <tr><th>Attribute</th><th>Description</th></tr>\n",
1609                            indentStr,
1610                            indentStr,
1611                            indentStr,
1612                            indentStr,
1613                            indentStr);
1614                }
1615                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1616                if (!publicFlags.itemAt(a) && !includePrivate) {
1617                    continue;
1618                }
1619                String8 name8(sym.name);
1620                String16 comment(sym.comment);
1621                if (comment.size() <= 0) {
1622                    comment = getAttributeComment(assets, name8);
1623                }
1624                if (comment.size() > 0) {
1625                    const char16_t* p = comment.string();
1626                    while (*p != 0 && *p != '.') {
1627                        if (*p == '{') {
1628                            while (*p != 0 && *p != '}') {
1629                                p++;
1630                            }
1631                        } else {
1632                            p++;
1633                        }
1634                    }
1635                    if (*p == '.') {
1636                        p++;
1637                    }
1638                    comment = String16(comment.string(), p-comment.string());
1639                }
1640                String16 name(name8);
1641                fixupSymbol(&name);
1642                fprintf(fp, "%s   <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n",
1643                        indentStr, nclassName.string(),
1644                        String8(name).string(),
1645                        assets->getPackage().string(),
1646                        String8(name).string(),
1647                        String8(comment).string());
1648            }
1649        }
1650        if (hasTable) {
1651            fprintf(fp, "%s   </table>\n", indentStr);
1652        }
1653        for (a=0; a<NA; a++) {
1654            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1655            if (pos >= 0) {
1656                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1657                if (!publicFlags.itemAt(a) && !includePrivate) {
1658                    continue;
1659                }
1660                String16 name(sym.name);
1661                fixupSymbol(&name);
1662                fprintf(fp, "%s   @see #%s_%s\n",
1663                        indentStr, nclassName.string(),
1664                        String8(name).string());
1665            }
1666        }
1667        fprintf(fp, "%s */\n", getIndentSpace(indent));
1668
1669        if (deprecated) {
1670            fprintf(fp, "%s@Deprecated\n", indentStr);
1671        }
1672
1673        fprintf(fp,
1674                "%spublic static final int[] %s = {\n"
1675                "%s",
1676                indentStr, nclassName.string(),
1677                getIndentSpace(indent+1));
1678
1679        for (a=0; a<NA; a++) {
1680            if (a != 0) {
1681                if ((a&3) == 0) {
1682                    fprintf(fp, ",\n%s", getIndentSpace(indent+1));
1683                } else {
1684                    fprintf(fp, ", ");
1685                }
1686            }
1687            fprintf(fp, "0x%08x", idents[a]);
1688        }
1689
1690        fprintf(fp, "\n%s};\n", indentStr);
1691
1692        for (a=0; a<NA; a++) {
1693            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1694            if (pos >= 0) {
1695                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1696                if (!publicFlags.itemAt(a) && !includePrivate) {
1697                    continue;
1698                }
1699                String8 name8(sym.name);
1700                String16 comment(sym.comment);
1701                String16 typeComment;
1702                if (comment.size() <= 0) {
1703                    comment = getAttributeComment(assets, name8, &typeComment);
1704                } else {
1705                    getAttributeComment(assets, name8, &typeComment);
1706                }
1707                String16 name(name8);
1708                if (fixupSymbol(&name) != NO_ERROR) {
1709                    hasErrors = true;
1710                }
1711
1712                uint32_t typeSpecFlags = 0;
1713                String16 name16(sym.name);
1714                assets->getIncludedResources().identifierForName(
1715                    name16.string(), name16.size(),
1716                    attr16.string(), attr16.size(),
1717                    package16.string(), package16.size(), &typeSpecFlags);
1718                //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
1719                //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
1720                const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
1721
1722                bool deprecated = false;
1723
1724                fprintf(fp, "%s/**\n", indentStr);
1725                if (comment.size() > 0) {
1726                    String8 cmt(comment);
1727                    fprintf(fp, "%s  <p>\n%s  @attr description\n", indentStr, indentStr);
1728                    fprintf(fp, "%s  %s\n", indentStr, cmt.string());
1729                    if (strstr(cmt.string(), "@deprecated") != NULL) {
1730                        deprecated = true;
1731                    }
1732                } else {
1733                    fprintf(fp,
1734                            "%s  <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
1735                            "%s  attribute's value can be found in the {@link #%s} array.\n",
1736                            indentStr,
1737                            pub ? assets->getPackage().string()
1738                                : assets->getSymbolsPrivatePackage().string(),
1739                            String8(name).string(),
1740                            indentStr, nclassName.string());
1741                }
1742                if (typeComment.size() > 0) {
1743                    String8 cmt(typeComment);
1744                    fprintf(fp, "\n\n%s  %s\n", indentStr, cmt.string());
1745                    if (strstr(cmt.string(), "@deprecated") != NULL) {
1746                        deprecated = true;
1747                    }
1748                }
1749                if (comment.size() > 0) {
1750                    if (pub) {
1751                        fprintf(fp,
1752                                "%s  <p>This corresponds to the global attribute"
1753                                "%s  resource symbol {@link %s.R.attr#%s}.\n",
1754                                indentStr, indentStr,
1755                                assets->getPackage().string(),
1756                                String8(name).string());
1757                    } else {
1758                        fprintf(fp,
1759                                "%s  <p>This is a private symbol.\n", indentStr);
1760                    }
1761                }
1762                fprintf(fp, "%s  @attr name %s:%s\n", indentStr,
1763                        "android", String8(name).string());
1764                fprintf(fp, "%s*/\n", indentStr);
1765                if (deprecated) {
1766                    fprintf(fp, "%s@Deprecated\n", indentStr);
1767                }
1768                fprintf(fp,
1769                        "%spublic static final int %s_%s = %d;\n",
1770                        indentStr, nclassName.string(),
1771                        String8(name).string(), (int)pos);
1772            }
1773        }
1774    }
1775
1776    indent--;
1777    fprintf(fp, "%s};\n", getIndentSpace(indent));
1778    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
1779}
1780
1781static status_t writeSymbolClass(
1782    FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
1783    const sp<AaptSymbols>& symbols, const String8& className, int indent,
1784    bool nonConstantId)
1785{
1786    fprintf(fp, "%spublic %sfinal class %s {\n",
1787            getIndentSpace(indent),
1788            indent != 0 ? "static " : "", className.string());
1789    indent++;
1790
1791    size_t i;
1792    status_t err = NO_ERROR;
1793
1794    const char * id_format = nonConstantId ?
1795            "%spublic static int %s=0x%08x;\n" :
1796            "%spublic static final int %s=0x%08x;\n";
1797
1798    size_t N = symbols->getSymbols().size();
1799    for (i=0; i<N; i++) {
1800        const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
1801        if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
1802            continue;
1803        }
1804        if (!includePrivate && !sym.isPublic) {
1805            continue;
1806        }
1807        String16 name(sym.name);
1808        String8 realName(name);
1809        if (fixupSymbol(&name) != NO_ERROR) {
1810            return UNKNOWN_ERROR;
1811        }
1812        String16 comment(sym.comment);
1813        bool haveComment = false;
1814        bool deprecated = false;
1815        if (comment.size() > 0) {
1816            haveComment = true;
1817            String8 cmt(comment);
1818            fprintf(fp,
1819                    "%s/** %s\n",
1820                    getIndentSpace(indent), cmt.string());
1821            if (strstr(cmt.string(), "@deprecated") != NULL) {
1822                deprecated = true;
1823            }
1824        } else if (sym.isPublic && !includePrivate) {
1825            sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
1826                assets->getPackage().string(), className.string(),
1827                String8(sym.name).string());
1828        }
1829        String16 typeComment(sym.typeComment);
1830        if (typeComment.size() > 0) {
1831            String8 cmt(typeComment);
1832            if (!haveComment) {
1833                haveComment = true;
1834                fprintf(fp,
1835                        "%s/** %s\n", getIndentSpace(indent), cmt.string());
1836            } else {
1837                fprintf(fp,
1838                        "%s %s\n", getIndentSpace(indent), cmt.string());
1839            }
1840            if (strstr(cmt.string(), "@deprecated") != NULL) {
1841                deprecated = true;
1842            }
1843        }
1844        if (haveComment) {
1845            fprintf(fp,"%s */\n", getIndentSpace(indent));
1846        }
1847        if (deprecated) {
1848            fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
1849        }
1850        fprintf(fp, id_format,
1851                getIndentSpace(indent),
1852                String8(name).string(), (int)sym.int32Val);
1853    }
1854
1855    for (i=0; i<N; i++) {
1856        const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
1857        if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) {
1858            continue;
1859        }
1860        if (!includePrivate && !sym.isPublic) {
1861            continue;
1862        }
1863        String16 name(sym.name);
1864        if (fixupSymbol(&name) != NO_ERROR) {
1865            return UNKNOWN_ERROR;
1866        }
1867        String16 comment(sym.comment);
1868        bool deprecated = false;
1869        if (comment.size() > 0) {
1870            String8 cmt(comment);
1871            fprintf(fp,
1872                    "%s/** %s\n"
1873                     "%s */\n",
1874                    getIndentSpace(indent), cmt.string(),
1875                    getIndentSpace(indent));
1876            if (strstr(cmt.string(), "@deprecated") != NULL) {
1877                deprecated = true;
1878            }
1879        } else if (sym.isPublic && !includePrivate) {
1880            sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
1881                assets->getPackage().string(), className.string(),
1882                String8(sym.name).string());
1883        }
1884        if (deprecated) {
1885            fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
1886        }
1887        fprintf(fp, "%spublic static final String %s=\"%s\";\n",
1888                getIndentSpace(indent),
1889                String8(name).string(), sym.stringVal.string());
1890    }
1891
1892    sp<AaptSymbols> styleableSymbols;
1893
1894    N = symbols->getNestedSymbols().size();
1895    for (i=0; i<N; i++) {
1896        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
1897        String8 nclassName(symbols->getNestedSymbols().keyAt(i));
1898        if (nclassName == "styleable") {
1899            styleableSymbols = nsymbols;
1900        } else {
1901            err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
1902        }
1903        if (err != NO_ERROR) {
1904            return err;
1905        }
1906    }
1907
1908    if (styleableSymbols != NULL) {
1909        err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
1910        if (err != NO_ERROR) {
1911            return err;
1912        }
1913    }
1914
1915    indent--;
1916    fprintf(fp, "%s}\n", getIndentSpace(indent));
1917    return NO_ERROR;
1918}
1919
1920status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
1921    const String8& package, bool includePrivate)
1922{
1923    if (!bundle->getRClassDir()) {
1924        return NO_ERROR;
1925    }
1926
1927    const size_t N = assets->getSymbols().size();
1928    for (size_t i=0; i<N; i++) {
1929        sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
1930        String8 className(assets->getSymbols().keyAt(i));
1931        String8 dest(bundle->getRClassDir());
1932        if (bundle->getMakePackageDirs()) {
1933            String8 pkg(package);
1934            const char* last = pkg.string();
1935            const char* s = last-1;
1936            do {
1937                s++;
1938                if (s > last && (*s == '.' || *s == 0)) {
1939                    String8 part(last, s-last);
1940                    dest.appendPath(part);
1941#ifdef HAVE_MS_C_RUNTIME
1942                    _mkdir(dest.string());
1943#else
1944                    mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
1945#endif
1946                    last = s+1;
1947                }
1948            } while (*s);
1949        }
1950        dest.appendPath(className);
1951        dest.append(".java");
1952        FILE* fp = fopen(dest.string(), "w+");
1953        if (fp == NULL) {
1954            fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
1955                    dest.string(), strerror(errno));
1956            return UNKNOWN_ERROR;
1957        }
1958        if (bundle->getVerbose()) {
1959            printf("  Writing symbols for class %s.\n", className.string());
1960        }
1961
1962        fprintf(fp,
1963        "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
1964        " *\n"
1965        " * This class was automatically generated by the\n"
1966        " * aapt tool from the resource data it found.  It\n"
1967        " * should not be modified by hand.\n"
1968        " */\n"
1969        "\n"
1970        "package %s;\n\n", package.string());
1971
1972        status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId());
1973        if (err != NO_ERROR) {
1974            return err;
1975        }
1976        fclose(fp);
1977
1978        // If we were asked to generate a dependency file, we'll go ahead and add this R.java
1979        // as a target in the dependency file right next to it.
1980        if (bundle->getGenDependencies()) {
1981            // Add this R.java to the dependency file
1982            String8 dependencyFile(bundle->getRClassDir());
1983            dependencyFile.appendPath("R.java.d");
1984
1985            fp = fopen(dependencyFile.string(), "a");
1986            fprintf(fp,"%s \\\n", dest.string());
1987            fclose(fp);
1988        }
1989    }
1990
1991    return NO_ERROR;
1992}
1993
1994
1995
1996class ProguardKeepSet
1997{
1998public:
1999    // { rule --> { file locations } }
2000    KeyedVector<String8, SortedVector<String8> > rules;
2001
2002    void add(const String8& rule, const String8& where);
2003};
2004
2005void ProguardKeepSet::add(const String8& rule, const String8& where)
2006{
2007    ssize_t index = rules.indexOfKey(rule);
2008    if (index < 0) {
2009        index = rules.add(rule, SortedVector<String8>());
2010    }
2011    rules.editValueAt(index).add(where);
2012}
2013
2014void
2015addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
2016        const char* pkg, const String8& srcName, int line)
2017{
2018    String8 className(inClassName);
2019    if (pkg != NULL) {
2020        // asdf     --> package.asdf
2021        // .asdf  .a.b  --> package.asdf package.a.b
2022        // asdf.adsf --> asdf.asdf
2023        const char* p = className.string();
2024        const char* q = strchr(p, '.');
2025        if (p == q) {
2026            className = pkg;
2027            className.append(inClassName);
2028        } else if (q == NULL) {
2029            className = pkg;
2030            className.append(".");
2031            className.append(inClassName);
2032        }
2033    }
2034
2035    String8 rule("-keep class ");
2036    rule += className;
2037    rule += " { <init>(...); }";
2038
2039    String8 location("view ");
2040    location += srcName;
2041    char lineno[20];
2042    sprintf(lineno, ":%d", line);
2043    location += lineno;
2044
2045    keep->add(rule, location);
2046}
2047
2048status_t
2049writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2050{
2051    status_t err;
2052    ResXMLTree tree;
2053    size_t len;
2054    ResXMLTree::event_code_t code;
2055    int depth = 0;
2056    bool inApplication = false;
2057    String8 error;
2058    sp<AaptGroup> assGroup;
2059    sp<AaptFile> assFile;
2060    String8 pkg;
2061
2062    // First, look for a package file to parse.  This is required to
2063    // be able to generate the resource information.
2064    assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
2065    if (assGroup == NULL) {
2066        fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
2067        return -1;
2068    }
2069
2070    if (assGroup->getFiles().size() != 1) {
2071        fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
2072                assGroup->getFiles().valueAt(0)->getPrintableSource().string());
2073    }
2074
2075    assFile = assGroup->getFiles().valueAt(0);
2076
2077    err = parseXMLResource(assFile, &tree);
2078    if (err != NO_ERROR) {
2079        return err;
2080    }
2081
2082    tree.restart();
2083
2084    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2085        if (code == ResXMLTree::END_TAG) {
2086            if (/* name == "Application" && */ depth == 2) {
2087                inApplication = false;
2088            }
2089            depth--;
2090            continue;
2091        }
2092        if (code != ResXMLTree::START_TAG) {
2093            continue;
2094        }
2095        depth++;
2096        String8 tag(tree.getElementName(&len));
2097        // printf("Depth %d tag %s\n", depth, tag.string());
2098        bool keepTag = false;
2099        if (depth == 1) {
2100            if (tag != "manifest") {
2101                fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
2102                return -1;
2103            }
2104            pkg = getAttribute(tree, NULL, "package", NULL);
2105        } else if (depth == 2) {
2106            if (tag == "application") {
2107                inApplication = true;
2108                keepTag = true;
2109
2110                String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
2111                        "backupAgent", &error);
2112                if (agent.length() > 0) {
2113                    addProguardKeepRule(keep, agent, pkg.string(),
2114                            assFile->getPrintableSource(), tree.getLineNumber());
2115                }
2116            } else if (tag == "instrumentation") {
2117                keepTag = true;
2118            }
2119        }
2120        if (!keepTag && inApplication && depth == 3) {
2121            if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
2122                keepTag = true;
2123            }
2124        }
2125        if (keepTag) {
2126            String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
2127                    "name", &error);
2128            if (error != "") {
2129                fprintf(stderr, "ERROR: %s\n", error.string());
2130                return -1;
2131            }
2132            if (name.length() > 0) {
2133                addProguardKeepRule(keep, name, pkg.string(),
2134                        assFile->getPrintableSource(), tree.getLineNumber());
2135            }
2136        }
2137    }
2138
2139    return NO_ERROR;
2140}
2141
2142struct NamespaceAttributePair {
2143    const char* ns;
2144    const char* attr;
2145
2146    NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
2147    NamespaceAttributePair() : ns(NULL), attr(NULL) {}
2148};
2149
2150status_t
2151writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
2152        const char* startTag, const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs)
2153{
2154    status_t err;
2155    ResXMLTree tree;
2156    size_t len;
2157    ResXMLTree::event_code_t code;
2158
2159    err = parseXMLResource(layoutFile, &tree);
2160    if (err != NO_ERROR) {
2161        return err;
2162    }
2163
2164    tree.restart();
2165
2166    if (startTag != NULL) {
2167        bool haveStart = false;
2168        while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2169            if (code != ResXMLTree::START_TAG) {
2170                continue;
2171            }
2172            String8 tag(tree.getElementName(&len));
2173            if (tag == startTag) {
2174                haveStart = true;
2175            }
2176            break;
2177        }
2178        if (!haveStart) {
2179            return NO_ERROR;
2180        }
2181    }
2182
2183    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2184        if (code != ResXMLTree::START_TAG) {
2185            continue;
2186        }
2187        String8 tag(tree.getElementName(&len));
2188
2189        // If there is no '.', we'll assume that it's one of the built in names.
2190        if (strchr(tag.string(), '.')) {
2191            addProguardKeepRule(keep, tag, NULL,
2192                    layoutFile->getPrintableSource(), tree.getLineNumber());
2193        } else if (tagAttrPairs != NULL) {
2194            ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
2195            if (tagIndex >= 0) {
2196                const NamespaceAttributePair& nsAttr = tagAttrPairs->valueAt(tagIndex);
2197                ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
2198                if (attrIndex < 0) {
2199                    // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
2200                    //        layoutFile->getPrintableSource().string(), tree.getLineNumber(),
2201                    //        tag.string(), nsAttr.ns, nsAttr.attr);
2202                } else {
2203                    size_t len;
2204                    addProguardKeepRule(keep,
2205                                        String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
2206                                        layoutFile->getPrintableSource(), tree.getLineNumber());
2207                }
2208            }
2209        }
2210    }
2211
2212    return NO_ERROR;
2213}
2214
2215static void addTagAttrPair(KeyedVector<String8, NamespaceAttributePair>* dest,
2216        const char* tag, const char* ns, const char* attr) {
2217    dest->add(String8(tag), NamespaceAttributePair(ns, attr));
2218}
2219
2220status_t
2221writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2222{
2223    status_t err;
2224
2225    // tag:attribute pairs that should be checked in layout files.
2226    KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
2227    addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
2228    addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
2229    addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
2230
2231    // tag:attribute pairs that should be checked in xml files.
2232    KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
2233    addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
2234    addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
2235
2236    const Vector<sp<AaptDir> >& dirs = assets->resDirs();
2237    const size_t K = dirs.size();
2238    for (size_t k=0; k<K; k++) {
2239        const sp<AaptDir>& d = dirs.itemAt(k);
2240        const String8& dirName = d->getLeaf();
2241        const char* startTag = NULL;
2242        const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs = NULL;
2243        if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
2244            tagAttrPairs = &kLayoutTagAttrPairs;
2245        } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
2246            startTag = "PreferenceScreen";
2247            tagAttrPairs = &kXmlTagAttrPairs;
2248        } else {
2249            continue;
2250        }
2251
2252        const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
2253        const size_t N = groups.size();
2254        for (size_t i=0; i<N; i++) {
2255            const sp<AaptGroup>& group = groups.valueAt(i);
2256            const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
2257            const size_t M = files.size();
2258            for (size_t j=0; j<M; j++) {
2259                err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
2260                if (err < 0) {
2261                    return err;
2262                }
2263            }
2264        }
2265    }
2266    // Handle the overlays
2267    sp<AaptAssets> overlay = assets->getOverlay();
2268    if (overlay.get()) {
2269        return writeProguardForLayouts(keep, overlay);
2270    }
2271    return NO_ERROR;
2272}
2273
2274status_t
2275writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
2276{
2277    status_t err = -1;
2278
2279    if (!bundle->getProguardFile()) {
2280        return NO_ERROR;
2281    }
2282
2283    ProguardKeepSet keep;
2284
2285    err = writeProguardForAndroidManifest(&keep, assets);
2286    if (err < 0) {
2287        return err;
2288    }
2289
2290    err = writeProguardForLayouts(&keep, assets);
2291    if (err < 0) {
2292        return err;
2293    }
2294
2295    FILE* fp = fopen(bundle->getProguardFile(), "w+");
2296    if (fp == NULL) {
2297        fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
2298                bundle->getProguardFile(), strerror(errno));
2299        return UNKNOWN_ERROR;
2300    }
2301
2302    const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
2303    const size_t N = rules.size();
2304    for (size_t i=0; i<N; i++) {
2305        const SortedVector<String8>& locations = rules.valueAt(i);
2306        const size_t M = locations.size();
2307        for (size_t j=0; j<M; j++) {
2308            fprintf(fp, "# %s\n", locations.itemAt(j).string());
2309        }
2310        fprintf(fp, "%s\n\n", rules.keyAt(i).string());
2311    }
2312    fclose(fp);
2313
2314    return err;
2315}
2316
2317// Loops through the string paths and writes them to the file pointer
2318// Each file path is written on its own line with a terminating backslash.
2319status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
2320{
2321    status_t deps = -1;
2322    for (size_t file_i = 0; file_i < files->size(); ++file_i) {
2323        // Add the full file path to the dependency file
2324        fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
2325        deps++;
2326    }
2327    return deps;
2328}
2329
2330status_t
2331writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw)
2332{
2333    status_t deps = -1;
2334    deps += writePathsToFile(assets->getFullResPaths(), fp);
2335    if (includeRaw) {
2336        deps += writePathsToFile(assets->getFullAssetPaths(), fp);
2337    }
2338    return deps;
2339}
2340