Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f90f2f8d authored by Adam Lesinski's avatar Adam Lesinski
Browse files

Support multiple resource tables with same package

In order to support APK split features, the resource
table needs to support loading multiple resource
tables with the same package but potentially new set
of type IDs.

This adds some complexity as the type ID space changes
from dense and ordered to potentially sparse.

A ByteBucketArray is used to store the type IDs in
a memory efficient way that allows for fast retrieval.

In addition, the IDMAP format has changed. We no longer
need random access to the type data, since we store the
types differently. However, random access to entries of
a given type is still required.

Change-Id: If6f5be680b405b368941d9c1f2b5d2ddca964160
parent c802c8cd
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -105,7 +105,7 @@ fail:

        uint32_t cached_target_crc, cached_overlay_crc;
        String8 cached_target_path, cached_overlay_path;
        if (!ResTable::getIdmapInfo(buf, N, &cached_target_crc, &cached_overlay_crc,
        if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc,
                    &cached_target_path, &cached_overlay_path)) {
            return true;
        }
+24 −18
Original line number Diff line number Diff line
@@ -66,26 +66,32 @@ EXAMPLES \n\
      Display an idmap file: \n\
\n\
      $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\
      SECTION      ENTRY        VALUE      OFFSET    COMMENT \n\
      IDMAP HEADER magic        0x706d6469 0x0 \n\
                   base crc     0x484aa77f 0x1 \n\
                   overlay crc  0x03c66fa5 0x2 \n\
                   base path    .......... 0x03-0x42 /system/app/target.apk \n\
                   overlay path .......... 0x43-0x82 /vendor/overlay/overlay.apk \n\
      DATA HEADER  types count  0x00000003 0x83 \n\
                   padding      0x00000000 0x84 \n\
                   type offset  0x00000004 0x85      absolute offset 0x87, xml \n\
                   type offset  0x00000007 0x86      absolute offset 0x8a, string \n\
      DATA BLOCK   entry count  0x00000001 0x87 \n\
                   entry offset 0x00000000 0x88 \n\
                   entry        0x7f020000 0x89      xml/integer \n\
      DATA BLOCK   entry count  0x00000002 0x8a \n\
                   entry offset 0x00000000 0x8b \n\
                   entry        0x7f030000 0x8c      string/str \n\
                   entry        0x7f030001 0x8d      string/str2 \n\
      SECTION      ENTRY        VALUE      COMMENT \n\
      IDMAP HEADER magic        0x706d6469 \n\
                   base crc     0xb65a383f \n\
                   overlay crc  0x7b9675e8 \n\
                   base path    .......... /path/to/target.apk \n\
                   overlay path .......... /path/to/overlay.apk \n\
      DATA HEADER  target pkg   0x0000007f \n\
                   types count  0x00000003 \n\
      DATA BLOCK   target type  0x00000002 \n\
                   overlay type 0x00000002 \n\
                   entry count  0x00000001 \n\
                   entry offset 0x00000000 \n\
                   entry        0x00000000 drawable/drawable \n\
      DATA BLOCK   target type  0x00000003 \n\
                   overlay type 0x00000003 \n\
                   entry count  0x00000001 \n\
                   entry offset 0x00000000 \n\
                   entry        0x00000000 xml/integer \n\
      DATA BLOCK   target type  0x00000004 \n\
                   overlay type 0x00000004 \n\
                   entry count  0x00000001 \n\
                   entry offset 0x00000000 \n\
                   entry        0x00000000 raw/lorem_ipsum \n\
\n\
      In this example, the overlay package provides three alternative resource values:\n\
      xml/integer, string/str and string/str2.\n\
      drawable/drawable, xml/integer, and raw/lorem_ipsum \n\
\n\
NOTES \n\
      This tool and its expected invocation from installd is modelled on dexopt.";
+159 −140
Original line number Diff line number Diff line
@@ -10,92 +10,108 @@

using namespace android;

#define NEXT(b, i, o) do { if (buf.next(&i, &o) < 0) { return -1; } } while (0)

namespace {
    static const uint32_t IDMAP_MAGIC = 0x706d6469;
    static const uint32_t IDMAP_MAGIC = 0x504D4449;
    static const size_t PATH_LENGTH = 256;
    static const uint32_t IDMAP_HEADER_SIZE = (3 + 2 * (PATH_LENGTH / sizeof(uint32_t)));

    void printe(const char *fmt, ...);

    class IdmapBuffer {
        private:
            char *buf_;
            const char* buf_;
            size_t len_;
            mutable size_t pos_;
            size_t pos_;
        public:
            IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {}
            IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {}

            ~IdmapBuffer() {
                if (buf_ != MAP_FAILED) {
                    munmap(buf_, len_);
                    munmap(const_cast<char*>(buf_), len_);
                }
            }

            int init(const char *idmap_path)
            {
            status_t init(const char *idmap_path) {
                struct stat st;
                int fd;

                if (stat(idmap_path, &st) < 0) {
                    printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno));
                    return -1;
                    return UNKNOWN_ERROR;
                }
                len_ = st.st_size;
                if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) {
                    printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno));
                    return -1;
                    return UNKNOWN_ERROR;
                }
                if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
                if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
                    close(fd);
                    printe("failed to mmap idmap: %s\n", strerror(errno));
                    return -1;
                    return UNKNOWN_ERROR;
                }
                close(fd);
                return 0;
                return NO_ERROR;
            }

            int next(uint32_t *i, uint32_t *offset) const
            {
            status_t nextUint32(uint32_t* i) {
                if (!buf_) {
                    printe("failed to read next uint32_t: buffer not initialized\n");
                    return -1;
                    return UNKNOWN_ERROR;
                }
                if (pos_ + 4 > len_) {

                if (pos_ + sizeof(uint32_t) > len_) {
                    printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n",
                            pos_);
                    return -1;
                    return UNKNOWN_ERROR;
                }
                *offset = pos_ / sizeof(uint32_t);
                char a = buf_[pos_++];
                char b = buf_[pos_++];
                char c = buf_[pos_++];
                char d = buf_[pos_++];
                *i = (d << 24) | (c << 16) | (b << 8) | a;
                return 0;

                if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) {
                    printe("failed to read next uint32_t: not aligned on 4-byte boundary\n");
                    return UNKNOWN_ERROR;
                }

                *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_));
                pos_ += sizeof(uint32_t);
                return NO_ERROR;
            }

            int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const
            {
            status_t nextUint16(uint16_t* i) {
                if (!buf_) {
                    printe("failed to read next uint16_t: buffer not initialized\n");
                    return UNKNOWN_ERROR;
                }

                if (pos_ + sizeof(uint16_t) > len_) {
                    printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n",
                            pos_);
                    return UNKNOWN_ERROR;
                }

                if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) {
                    printe("failed to read next uint32_t: not aligned on 2-byte boundary\n");
                    return UNKNOWN_ERROR;
                }

                *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_));
                pos_ += sizeof(uint16_t);
                return NO_ERROR;
            }

            status_t nextPath(char *b) {
                if (!buf_) {
                    printe("failed to read next path: buffer not initialized\n");
                    return -1;
                    return UNKNOWN_ERROR;
                }
                if (pos_ + PATH_LENGTH > len_) {
                    printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_);
                    return -1;
                    return UNKNOWN_ERROR;
                }
                memcpy(b, buf_ + pos_, PATH_LENGTH);
                *offset_start = pos_ / sizeof(uint32_t);
                pos_ += PATH_LENGTH;
                *offset_end = pos_ / sizeof(uint32_t) - 1;
                return 0;
                return NO_ERROR;
            }
    };

    void printe(const char *fmt, ...)
    {
    void printe(const char *fmt, ...) {
        va_list ap;

        va_start(ap, fmt);
@@ -104,44 +120,37 @@ namespace {
        va_end(ap);
    }

    void print_header()
    {
        printf("SECTION      ENTRY        VALUE      OFFSET    COMMENT\n");
    void print_header() {
        printf("SECTION      ENTRY        VALUE      COMMENT\n");
    }

    void print(const char *section, const char *subsection, uint32_t value, uint32_t offset,
            const char *fmt, ...)
    {
    void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) {
        va_list ap;

        va_start(ap, fmt);
        printf("%-12s %-12s 0x%08x 0x%-4x    ", section, subsection, value, offset);
        printf("%-12s %-12s 0x%08x ", section, subsection, value);
        vprintf(fmt, ap);
        printf("\n");
        va_end(ap);
    }

    void print_path(const char *section, const char *subsection, uint32_t offset_start,
            uint32_t offset_end, const char *fmt, ...)
    {
    void print_path(const char *section, const char *subsection, const char *fmt, ...) {
        va_list ap;

        va_start(ap, fmt);
        printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start,
                offset_end);
        printf("%-12s %-12s .......... ", section, subsection);
        vprintf(fmt, ap);
        printf("\n");
        va_end(ap);
    }

    int resource_metadata(const AssetManager& am, uint32_t res_id,
            String8 *package, String8 *type, String8 *name)
    {
    status_t resource_metadata(const AssetManager& am, uint32_t res_id,
            String8 *package, String8 *type, String8 *name) {
        const ResTable& rt = am.getResources();
        struct ResTable::resource_name data;
        if (!rt.getResourceName(res_id, false, &data)) {
            printe("failed to get resource name id=0x%08x\n", res_id);
            return -1;
            return UNKNOWN_ERROR;
        }
        if (package) {
            *package = String8(String16(data.package, data.packageLen));
@@ -152,140 +161,150 @@ namespace {
        if (name) {
            *name = String8(String16(data.name, data.nameLen));
        }
        return 0;
        return NO_ERROR;
    }

    int package_id(const AssetManager& am)
    {
        return (am.getResources().getBasePackageId(0)) << 24;
    }

    int parse_idmap_header(const IdmapBuffer& buf, AssetManager& am)
    {
        uint32_t i, o, e;
    status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) {
        uint32_t i;
        char path[PATH_LENGTH];

        NEXT(buf, i, o);
        status_t err = buf.nextUint32(&i);
        if (err != NO_ERROR) {
            return err;
        }

        if (i != IDMAP_MAGIC) {
            printe("not an idmap file: actual magic constant 0x%08x does not match expected magic "
                    "constant 0x%08x\n", i, IDMAP_MAGIC);
            return -1;
            return UNKNOWN_ERROR;
        }

        print_header();
        print("IDMAP HEADER", "magic", i, o, "");
        print("IDMAP HEADER", "magic", i, "");

        NEXT(buf, i, o);
        print("", "base crc", i, o, "");
        err = buf.nextUint32(&i);
        if (err != NO_ERROR) {
            return err;
        }
        print("", "version", i, "");

        NEXT(buf, i, o);
        print("", "overlay crc", i, o, "");
        err = buf.nextUint32(&i);
        if (err != NO_ERROR) {
            return err;
        }
        print("", "base crc", i, "");

        err = buf.nextUint32(&i);
        if (err != NO_ERROR) {
            return err;
        }
        print("", "overlay crc", i, "");

        if (buf.nextPath(path, &o, &e) < 0) {
        err = buf.nextPath(path);
        if (err != NO_ERROR) {
            // printe done from IdmapBuffer::nextPath
            return -1;
            return err;
        }
        print_path("", "base path", o, e, "%s", path);
        print_path("", "base path", "%s", path);

        if (!am.addAssetPath(String8(path), NULL)) {
            printe("failed to add '%s' as asset path\n", path);
            return -1;
            return UNKNOWN_ERROR;
        }

        if (buf.nextPath(path, &o, &e) < 0) {
        err = buf.nextPath(path);
        if (err != NO_ERROR) {
            // printe done from IdmapBuffer::nextPath
            return -1;
            return err;
        }
        print_path("", "overlay path", o, e, "%s", path);
        print_path("", "overlay path", "%s", path);

        return 0;
        return NO_ERROR;
    }

    int parse_data_header(const IdmapBuffer& buf, const AssetManager& am, Vector<uint32_t>& types)
    {
        uint32_t i, o;
        const uint32_t numeric_package = package_id(am);

        NEXT(buf, i, o);
        print("DATA HEADER", "types count", i, o, "");
        const uint32_t N = i;
    status_t parse_data(IdmapBuffer& buf, const AssetManager& am) {
        const uint32_t packageId = am.getResources().getBasePackageId(0);

        for (uint32_t j = 0; j < N; ++j) {
            NEXT(buf, i, o);
            if (i == 0) {
                print("", "padding", i, o, "");
            } else {
                String8 type;
                const uint32_t numeric_type = (j + 1) << 16;
                const uint32_t res_id = numeric_package | numeric_type;
                if (resource_metadata(am, res_id, NULL, &type, NULL) < 0) {
                    // printe done from resource_metadata
                    return -1;
                }
                print("", "type offset", i, o, "absolute offset 0x%02x, %s",
                        i + IDMAP_HEADER_SIZE, type.string());
                types.add(numeric_type);
        uint16_t data16;
        status_t err = buf.nextUint16(&data16);
        if (err != NO_ERROR) {
            return err;
        }
        print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), "");

        err = buf.nextUint16(&data16);
        if (err != NO_ERROR) {
            return err;
        }
        print("", "types count", static_cast<uint32_t>(data16), "");

        return 0;
        uint32_t typeCount = static_cast<uint32_t>(data16);
        while (typeCount > 0) {
            typeCount--;

            err = buf.nextUint16(&data16);
            if (err != NO_ERROR) {
                return err;
            }
            const uint32_t targetTypeId = static_cast<uint32_t>(data16);
            print("DATA BLOCK", "target type", targetTypeId, "");

    int parse_data_block(const IdmapBuffer& buf, const AssetManager& am, size_t numeric_type)
    {
        uint32_t i, o, n, id_offset;
        const uint32_t numeric_package = package_id(am);
            err = buf.nextUint16(&data16);
            if (err != NO_ERROR) {
                return err;
            }
            print("", "overlay type", static_cast<uint32_t>(data16), "");

        NEXT(buf, i, o);
        print("DATA BLOCK", "entry count", i, o, "");
        n = i;
            err = buf.nextUint16(&data16);
            if (err != NO_ERROR) {
                return err;
            }
            const uint32_t entryCount = static_cast<uint32_t>(data16);
            print("", "entry count", entryCount, "");

        NEXT(buf, i, o);
        print("", "entry offset", i, o, "");
        id_offset = i;
            err = buf.nextUint16(&data16);
            if (err != NO_ERROR) {
                return err;
            }
            const uint32_t entryOffset = static_cast<uint32_t>(data16);
            print("", "entry offset", entryOffset, "");

        for ( ; n > 0; --n) {
            String8 type, name;
            for (uint32_t i = 0; i < entryCount; i++) {
                uint32_t data32;
                err = buf.nextUint32(&data32);
                if (err != NO_ERROR) {
                    return err;
                }

            NEXT(buf, i, o);
            if (i == 0) {
                print("", "padding", i, o, "");
            } else {
                uint32_t res_id = numeric_package | numeric_type | id_offset;
                if (resource_metadata(am, res_id, NULL, &type, &name) < 0) {
                    // printe done from resource_metadata
                    return -1;
                uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i);
                String8 type;
                String8 name;
                err = resource_metadata(am, resID, NULL, &type, &name);
                if (err != NO_ERROR) {
                    return err;
                }
                print("", "entry", i, o, "%s/%s", type.string(), name.string());
                print("", "entry", data32, "%s/%s", type.string(), name.string());
            }
            ++id_offset;
        }

        return 0;
        return NO_ERROR;
    }
}

int idmap_inspect(const char *idmap_path)
{
int idmap_inspect(const char *idmap_path) {
    IdmapBuffer buf;
    if (buf.init(idmap_path) < 0) {
        // printe done from IdmapBuffer::init
        return EXIT_FAILURE;
    }
    AssetManager am;
    if (parse_idmap_header(buf, am) < 0) {
    if (parse_idmap_header(buf, am) != NO_ERROR) {
        // printe done from parse_idmap_header
        return EXIT_FAILURE;
    }
    Vector<uint32_t> types;
    if (parse_data_header(buf, am, types) < 0) {
    if (parse_data(buf, am) != NO_ERROR) {
        // printe done from parse_data_header
        return EXIT_FAILURE;
    }
    const size_t N = types.size();
    for (size_t i = 0; i < N; ++i) {
        if (parse_data_block(buf, am, types.itemAt(i)) < 0) {
            // printe done from parse_data_block
            return EXIT_FAILURE;
        }
    }
    return EXIT_SUCCESS;
}
+97 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __BYTE_BUCKET_ARRAY_H
#define __BYTE_BUCKET_ARRAY_H

#include <utils/Log.h>
#include <stdint.h>
#include <string.h>

namespace android {

/**
 * Stores a sparsely populated array. Has a fixed size of 256
 * (number of entries that a byte can represent).
 */
template<typename T>
class ByteBucketArray {
public:
    ByteBucketArray() : mDefault() {
        memset(mBuckets, 0, sizeof(mBuckets));
    }

    ~ByteBucketArray() {
        for (size_t i = 0; i < NUM_BUCKETS; i++) {
            if (mBuckets[i] != NULL) {
                delete [] mBuckets[i];
            }
        }
        memset(mBuckets, 0, sizeof(mBuckets));
    }

    inline size_t size() const {
        return NUM_BUCKETS * BUCKET_SIZE;
    }

    inline const T& get(size_t index) const {
        return (*this)[index];
    }

    const T& operator[](size_t index) const {
        if (index >= size()) {
            return mDefault;
        }

        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
        T* bucket = mBuckets[bucketIndex];
        if (bucket == NULL) {
            return mDefault;
        }
        return bucket[0x0f & static_cast<uint8_t>(index)];
    }

    T& editItemAt(size_t index) {
        ALOG_ASSERT(index < size(), "ByteBucketArray.getOrCreate(index=%u) with size=%u",
                (uint32_t) index, (uint32_t) size());

        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
        T* bucket = mBuckets[bucketIndex];
        if (bucket == NULL) {
            bucket = mBuckets[bucketIndex] = new T[BUCKET_SIZE]();
        }
        return bucket[0x0f & static_cast<uint8_t>(index)];
    }

    bool set(size_t index, const T& value) {
        if (index >= size()) {
            return false;
        }

        editItemAt(index) = value;
        return true;
    }

private:
    enum { NUM_BUCKETS = 16, BUCKET_SIZE = 16 };

    T*  mBuckets[NUM_BUCKETS];
    T   mDefault;
};

} // namespace android

#endif // __BYTE_BUCKET_ARRAY_H
+44 −13
Original line number Diff line number Diff line
@@ -237,6 +237,7 @@ enum {
#define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF))

#define Res_MAXPACKAGE 255
#define Res_MAXTYPE 255

/**
 * Representation of a value in a resource, supplying type
@@ -510,6 +511,23 @@ private:
    uint32_t                    mStylePoolSize;    // number of uint32_t
};

/**
 * Wrapper class that allows the caller to retrieve a string from
 * a string pool without knowing which string pool to look.
 */
class StringPoolRef {
public:
    StringPoolRef();
    StringPoolRef(const ResStringPool* pool, uint32_t index);

    const char* string8(size_t* outLen) const;
    const char16_t* string16(size_t* outLen) const;

private:
    const ResStringPool*        mPool;
    uint32_t                    mIndex;
};

/** ********************************************************************
 *  XML Tree
 *
@@ -835,6 +853,8 @@ struct ResTable_package

    // Last index into keyStrings that is for public use by others.
    uint32_t lastPublicKey;

    uint32_t typeIdOffset;
};

// The most specific locale can consist of:
@@ -1469,9 +1489,13 @@ public:
             bool copyData=false);
    ~ResTable();

    status_t add(Asset* asset, const int32_t cookie, bool copyData,
                 const void* idmap = NULL);
    status_t add(const void *data, size_t size);
    status_t add(const void* data, size_t size, const int32_t cookie=-1, bool copyData=false);
    status_t add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
            const int32_t cookie=-1, bool copyData=false);

    status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false);
    status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false);

    status_t add(ResTable* src);
    status_t addEmpty(const int32_t cookie);

@@ -1610,13 +1634,14 @@ public:
            uint32_t typeSpecFlags;
            Res_value value;
        };

        struct type_info {
            size_t numEntries;
            theme_entry* entries;
        };

        struct package_info {
            size_t numTypes;
            type_info types[];
            type_info types[Res_MAXTYPE + 1];
        };

        void free_package(package_info* pi);
@@ -1711,6 +1736,7 @@ public:
    size_t getBasePackageCount() const;
    const String16 getBasePackageName(size_t idx) const;
    uint32_t getBasePackageId(size_t idx) const;
    uint32_t getLastTypeIdForPackage(size_t idx) const;

    // Return the number of resource tables that the object contains.
    size_t getTableCount() const;
@@ -1740,13 +1766,15 @@ public:
            void** outData, size_t* outSize) const;

    enum {
        IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t) + 2 * 256,
        IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256,
    };

    // Retrieve idmap meta-data.
    //
    // This function only requires the idmap header (the first
    // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file.
    static bool getIdmapInfo(const void* idmap, size_t size,
            uint32_t* pVersion,
            uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
            String8* pTargetPath, String8* pOverlayPath);

@@ -1756,21 +1784,24 @@ public:
private:
    struct Header;
    struct Type;
    struct Entry;
    struct Package;
    struct PackageGroup;
    struct bag_set;
    typedef Vector<Type*> TypeList;

    status_t addInternal(const void* data, size_t size, const int32_t cookie,
                 bool copyData, const Asset* idmap);
    status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
            const int32_t cookie, bool copyData);

    ssize_t getResourcePackageIndex(uint32_t resID) const;
    ssize_t getEntry(
        const Package* package, int typeIndex, int entryIndex,

    status_t getEntry(
        const PackageGroup* packageGroup, int typeIndex, int entryIndex,
        const ResTable_config* config,
        const ResTable_type** outType, const ResTable_entry** outEntry,
        const Type** outTypeClass) const;
        Entry* outEntry) const;

    status_t parsePackage(
        const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id);
        const ResTable_package* const pkg, const Header* const header);

    void print_value(const Package* pkg, const Res_value& value) const;
    
Loading