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

Commit 81bc5677 authored by Kenny Root's avatar Kenny Root
Browse files

resolved conflicts for merge of 18490fb9 to master

Change-Id: I6c68e6fb0f9c1182a955689bbffc161a8f30bd37
parents d80cc888 f6bbefd5
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -222,6 +222,7 @@ private:
    {
    {
        String8 path;
        String8 path;
        FileType type;
        FileType type;
        String8 idmap;
    };
    };


    Asset* openInPathLocked(const char* fileName, AccessMode mode,
    Asset* openInPathLocked(const char* fileName, AccessMode mode,
@@ -262,6 +263,16 @@ private:
    void setLocaleLocked(const char* locale);
    void setLocaleLocked(const char* locale);
    void updateResourceParamsLocked() const;
    void updateResourceParamsLocked() const;


    bool createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
                               const String8& idmapPath);

    bool isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath,
                            const String8& idmapPath);

    Asset* openIdmapLocked(const struct asset_path& ap) const;

    bool getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename, uint32_t* pCrc);

    class SharedZip : public RefBase {
    class SharedZip : public RefBase {
    public:
    public:
        static sp<SharedZip> get(const String8& path);
        static sp<SharedZip> get(const String8& path);
+22 −4
Original line number Original line Diff line number Diff line
@@ -1798,9 +1798,9 @@ public:
    ~ResTable();
    ~ResTable();


    status_t add(const void* data, size_t size, void* cookie,
    status_t add(const void* data, size_t size, void* cookie,
                 bool copyData=false);
                 bool copyData=false, const void* idmap = NULL);
    status_t add(Asset* asset, void* cookie,
    status_t add(Asset* asset, void* cookie,
                 bool copyData=false);
                 bool copyData=false, const void* idmap = NULL);
    status_t add(ResTable* src);
    status_t add(ResTable* src);


    status_t getError() const;
    status_t getError() const;
@@ -2046,6 +2046,24 @@ public:


    void getLocales(Vector<String8>* locales) const;
    void getLocales(Vector<String8>* locales) const;


    // Generate an idmap.
    //
    // Return value: on success: NO_ERROR; caller is responsible for free-ing
    // outData (using free(3)). On failure, any status_t value other than
    // NO_ERROR; the caller should not free outData.
    status_t createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc,
                         void** outData, size_t* outSize) const;

    enum {
        IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t),
    };
    // 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* pOriginalCrc, uint32_t* pOverlayCrc);

#ifndef HAVE_ANDROID_OS
#ifndef HAVE_ANDROID_OS
    void print(bool inclValues) const;
    void print(bool inclValues) const;
    static String8 normalizeForOutput(const char* input);
    static String8 normalizeForOutput(const char* input);
@@ -2059,7 +2077,7 @@ private:
    struct bag_set;
    struct bag_set;


    status_t add(const void* data, size_t size, void* cookie,
    status_t add(const void* data, size_t size, void* cookie,
                 Asset* asset, bool copyData);
                 Asset* asset, bool copyData, const Asset* idmap);


    ssize_t getResourcePackageIndex(uint32_t resID) const;
    ssize_t getResourcePackageIndex(uint32_t resID) const;
    ssize_t getEntry(
    ssize_t getEntry(
@@ -2068,7 +2086,7 @@ private:
        const ResTable_type** outType, const ResTable_entry** outEntry,
        const ResTable_type** outType, const ResTable_entry** outEntry,
        const Type** outTypeClass) const;
        const Type** outTypeClass) const;
    status_t parsePackage(
    status_t parsePackage(
        const ResTable_package* const pkg, const Header* const header);
        const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id);


    void print_value(const Package* pkg, const Res_value& value) const;
    void print_value(const Package* pkg, const Res_value& value) const;
    
    
+234 −3
Original line number Original line Diff line number Diff line
@@ -37,6 +37,19 @@
#include <errno.h>
#include <errno.h>
#include <assert.h>
#include <assert.h>
#include <strings.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

#ifndef TEMP_FAILURE_RETRY
/* Used to retry syscalls that can return EINTR. */
#define TEMP_FAILURE_RETRY(exp) ({         \
    typeof (exp) _rc;                      \
    do {                                   \
        _rc = (exp);                       \
    } while (_rc == -1 && errno == EINTR); \
    _rc; })
#endif


using namespace android;
using namespace android;


@@ -49,6 +62,7 @@ static const char* kDefaultVendor = "default";
static const char* kAssetsRoot = "assets";
static const char* kAssetsRoot = "assets";
static const char* kAppZipName = NULL; //"classes.jar";
static const char* kAppZipName = NULL; //"classes.jar";
static const char* kSystemAssets = "framework/framework-res.apk";
static const char* kSystemAssets = "framework/framework-res.apk";
static const char* kIdmapCacheDir = "resource-cache";


static const char* kExcludeExtension = ".EXCLUDE";
static const char* kExcludeExtension = ".EXCLUDE";


@@ -56,6 +70,35 @@ static Asset* const kExcludedAsset = (Asset*) 0xd000000d;


static volatile int32_t gCount = 0;
static volatile int32_t gCount = 0;


namespace {
    // Transform string /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
    String8 idmapPathForPackagePath(const String8& pkgPath)
    {
        const char* root = getenv("ANDROID_DATA");
        LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
        String8 path(root);
        path.appendPath(kIdmapCacheDir);

        char buf[256]; // 256 chars should be enough for anyone...
        strncpy(buf, pkgPath.string(), 255);
        buf[255] = '\0';
        char* filename = buf;
        while (*filename && *filename == '/') {
            ++filename;
        }
        char* p = filename;
        while (*p) {
            if (*p == '/') {
                *p = '@';
            }
            ++p;
        }
        path.appendPath(filename);
        path.append("@idmap");

        return path;
    }
}


/*
/*
 * ===========================================================================
 * ===========================================================================
@@ -134,9 +177,181 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie)
        *cookie = (void*)mAssetPaths.size();
        *cookie = (void*)mAssetPaths.size();
    }
    }


    // add overlay packages for /system/framework; apps are handled by the
    // (Java) package manager
    if (strncmp(path.string(), "/system/framework/", 18) == 0) {
        // When there is an environment variable for /vendor, this
        // should be changed to something similar to how ANDROID_ROOT
        // and ANDROID_DATA are used in this file.
        String8 overlayPath("/vendor/overlay/framework/");
        overlayPath.append(path.getPathLeaf());
        if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK)) == 0) {
            asset_path oap;
            oap.path = overlayPath;
            oap.type = ::getFileType(overlayPath.string());
            bool addOverlay = (oap.type == kFileTypeRegular); // only .apks supported as overlay
            if (addOverlay) {
                oap.idmap = idmapPathForPackagePath(overlayPath);

                if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) {
                    addOverlay = createIdmapFileLocked(ap.path, oap.path, oap.idmap);
                }
            }
            if (addOverlay) {
                mAssetPaths.add(oap);
            } else {
                LOGW("failed to add overlay package %s\n", overlayPath.string());
            }
        }
    }

    return true;
}

bool AssetManager::isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath,
                                      const String8& idmapPath)
{
    struct stat st;
    if (TEMP_FAILURE_RETRY(stat(idmapPath.string(), &st)) == -1) {
        if (errno == ENOENT) {
            return true; // non-existing idmap is always stale
        } else {
            LOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno));
            return false;
        }
    }
    if (st.st_size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
        LOGW("file %s has unexpectedly small size=%zd\n", idmapPath.string(), (size_t)st.st_size);
        return false;
    }
    int fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_RDONLY));
    if (fd == -1) {
        LOGW("failed to open file %s: %s\n", idmapPath.string(), strerror(errno));
        return false;
    }
    char buf[ResTable::IDMAP_HEADER_SIZE_BYTES];
    ssize_t bytesLeft = ResTable::IDMAP_HEADER_SIZE_BYTES;
    for (;;) {
        ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf + ResTable::IDMAP_HEADER_SIZE_BYTES - bytesLeft,
                                            bytesLeft));
        if (r < 0) {
            TEMP_FAILURE_RETRY(close(fd));
            return false;
        }
        bytesLeft -= r;
        if (bytesLeft == 0) {
            break;
        }
    }
    TEMP_FAILURE_RETRY(close(fd));

    uint32_t cachedOriginalCrc, cachedOverlayCrc;
    if (!ResTable::getIdmapInfo(buf, ResTable::IDMAP_HEADER_SIZE_BYTES,
                                &cachedOriginalCrc, &cachedOverlayCrc)) {
        return false;
    }

    uint32_t actualOriginalCrc, actualOverlayCrc;
    if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &actualOriginalCrc)) {
        return false;
    }
    if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &actualOverlayCrc)) {
        return false;
    }
    return cachedOriginalCrc != actualOriginalCrc || cachedOverlayCrc != actualOverlayCrc;
}

bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename,
                                        uint32_t* pCrc)
{
    asset_path ap;
    ap.path = zipPath;
    const ZipFileRO* zip = getZipFileLocked(ap);
    if (zip == NULL) {
        return false;
    }
    const ZipEntryRO entry = zip->findEntryByName(entryFilename);
    if (entry == NULL) {
        return false;
    }
    if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc)) {
        return false;
    }
    return true;
    return true;
}
}


bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
                                         const String8& idmapPath)
{
    LOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n",
         __FUNCTION__, originalPath.string(), overlayPath.string(), idmapPath.string());
    ResTable tables[2];
    const String8* paths[2] = { &originalPath, &overlayPath };
    uint32_t originalCrc, overlayCrc;
    bool retval = false;
    ssize_t offset = 0;
    int fd = 0;
    uint32_t* data = NULL;
    size_t size;

    for (int i = 0; i < 2; ++i) {
        asset_path ap;
        ap.type = kFileTypeRegular;
        ap.path = *paths[i];
        Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap);
        if (ass == NULL) {
            LOGW("failed to find resources.arsc in %s\n", ap.path.string());
            goto error;
        }
        tables[i].add(ass, (void*)1, false);
    }

    if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &originalCrc)) {
        LOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string());
        goto error;
    }
    if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &overlayCrc)) {
        LOGW("failed to retrieve crc for resources.arsc in %s\n", overlayPath.string());
        goto error;
    }

    if (tables[0].createIdmap(tables[1], originalCrc, overlayCrc,
                              (void**)&data, &size) != NO_ERROR) {
        LOGW("failed to generate idmap data for file %s\n", idmapPath.string());
        goto error;
    }

    // This should be abstracted (eg replaced by a stand-alone
    // application like dexopt, triggered by something equivalent to
    // installd).
    fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644));
    if (fd == -1) {
        LOGW("failed to write idmap file %s (open: %s)\n", idmapPath.string(), strerror(errno));
        goto error_free;
    }
    for (;;) {
        ssize_t written = TEMP_FAILURE_RETRY(write(fd, data + offset, size));
        if (written < 0) {
            LOGW("failed to write idmap file %s (write: %s)\n", idmapPath.string(),
                 strerror(errno));
            goto error_close;
        }
        size -= (size_t)written;
        offset += written;
        if (size == 0) {
            break;
        }
    }

    retval = true;
error_close:
    TEMP_FAILURE_RETRY(close(fd));
error_free:
    free(data);
error:
    return retval;
}

bool AssetManager::addDefaultAssets()
bool AssetManager::addDefaultAssets()
{
{
    const char* root = getenv("ANDROID_ROOT");
    const char* root = getenv("ANDROID_ROOT");
@@ -405,6 +620,7 @@ const ResTable* AssetManager::getResTable(bool required) const
        ResTable* sharedRes = NULL;
        ResTable* sharedRes = NULL;
        bool shared = true;
        bool shared = true;
        const asset_path& ap = mAssetPaths.itemAt(i);
        const asset_path& ap = mAssetPaths.itemAt(i);
        Asset* idmap = openIdmapLocked(ap);
        LOGV("Looking for resource asset in '%s'\n", ap.path.string());
        LOGV("Looking for resource asset in '%s'\n", ap.path.string());
        if (ap.type != kFileTypeDirectory) {
        if (ap.type != kFileTypeDirectory) {
            if (i == 0) {
            if (i == 0) {
@@ -434,7 +650,7 @@ const ResTable* AssetManager::getResTable(bool required) const
                    // can quickly copy it out for others.
                    // can quickly copy it out for others.
                    LOGV("Creating shared resources for %s", ap.path.string());
                    LOGV("Creating shared resources for %s", ap.path.string());
                    sharedRes = new ResTable();
                    sharedRes = new ResTable();
                    sharedRes->add(ass, (void*)(i+1), false);
                    sharedRes->add(ass, (void*)(i+1), false, idmap);
                    sharedRes = const_cast<AssetManager*>(this)->
                    sharedRes = const_cast<AssetManager*>(this)->
                        mZipSet.setZipResourceTable(ap.path, sharedRes);
                        mZipSet.setZipResourceTable(ap.path, sharedRes);
                }
                }
@@ -458,7 +674,7 @@ const ResTable* AssetManager::getResTable(bool required) const
                rt->add(sharedRes);
                rt->add(sharedRes);
            } else {
            } else {
                LOGV("Parsing resources for %s", ap.path.string());
                LOGV("Parsing resources for %s", ap.path.string());
                rt->add(ass, (void*)(i+1), !shared);
                rt->add(ass, (void*)(i+1), !shared, idmap);
            }
            }


            if (!shared) {
            if (!shared) {
@@ -499,6 +715,21 @@ void AssetManager::updateResourceParamsLocked() const
    res->setParameters(mConfig);
    res->setParameters(mConfig);
}
}


Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const
{
    Asset* ass = NULL;
    if (ap.idmap.size() != 0) {
        ass = const_cast<AssetManager*>(this)->
            openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER);
        if (ass) {
            LOGV("loading idmap %s\n", ap.idmap.string());
        } else {
            LOGW("failed to load idmap %s\n", ap.idmap.string());
        }
    }
    return ass;
}

const ResTable& AssetManager::getResources(bool required) const
const ResTable& AssetManager::getResources(bool required) const
{
{
    const ResTable* rt = getResTable(required);
    const ResTable* rt = getResTable(required);
+275 −0
Original line number Original line Diff line number Diff line
Android Utility Function Library
Android Utility Function Library
================================



If you need a feature that is native to Linux but not present on other
If you need a feature that is native to Linux but not present on other
platforms, construct a platform-dependent implementation that shares
platforms, construct a platform-dependent implementation that shares
@@ -12,3 +14,276 @@ The ultimate goal is *not* to create a super-duper platform abstraction
layer.  The goal is to provide an optimized solution for Linux with
layer.  The goal is to provide an optimized solution for Linux with
reasonable implementations for other platforms.
reasonable implementations for other platforms.




Resource overlay
================


Introduction
------------

Overlay packages are special .apk files which provide no code but
additional resource values (and possibly new configurations) for
resources in other packages. When an application requests resources,
the system will return values from either the application's original
package or any associated overlay package. Any redirection is completely
transparent to the calling application.

Resource values have the following precedence table, listed in
descending precedence.

 * overlay package, matching config (eg res/values-en-land)

 * original package, matching config

 * overlay package, no config (eg res/values)

 * original package, no config

During compilation, overlay packages are differentiated from regular
packages by passing the -o flag to aapt.


Background
----------

This section provides generic background material on resources in
Android.


How resources are bundled in .apk files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Android .apk files are .zip files, usually housing .dex code,
certificates and resources, though packages containing resources but
no code are possible. Resources can be divided into the following
categories; a `configuration' indicates a set of phone language, display
density, network operator, etc.

 * assets: uncompressed, raw files packaged as part of an .apk and
           explicitly referenced by filename. These files are
           independent of configuration.

 * res/drawable: bitmap or xml graphics. Each file may have different
                 values depending on configuration.

 * res/values: integers, strings, etc. Each resource may have different
               values depending on configuration.

Resource meta information and information proper is stored in a binary
format in a named file resources.arsc, bundled as part of the .apk.

Resource IDs and lookup
~~~~~~~~~~~~~~~~~~~~~~~
During compilation, the aapt tool gathers application resources and
generates a resources.arsc file. Each resource name is assigned an
integer ID 0xppttiii (translated to a symbolic name via R.java), where

 * pp: corresponds to the package namespace (details below).

 * tt: corresponds to the resource type (string, int, etc). Every
       resource of the same type within the same package has the same
       tt value, but depending on available types, the actual numerical
       value may be different between packages.

 * iiii: sequential number, assigned in the order resources are found.

Resource values are specified paired with a set of configuration
constraints (the default being the empty set), eg res/values-sv-port
which imposes restrictions on language (Swedish) and display orientation
(portrait). During lookup, every constraint set is matched against the
current configuration, and the value corresponding to the best matching
constraint set is returned (ResourceTypes.{h,cpp}).

Parsing of resources.arsc is handled by ResourceTypes.cpp; this utility
is governed by AssetManager.cpp, which tracks loaded resources per
process.

Assets are looked up by path and filename in AssetManager.cpp. The path
to resources in res/drawable are located by ResourceTypes.cpp and then
handled like assets by AssetManager.cpp. Other resources are handled
solely by ResourceTypes.cpp.

Package ID as namespace
~~~~~~~~~~~~~~~~~~~~~~~
The pp part of a resource ID defines a namespace. Android currently
defines two namespaces:

 * 0x01: system resources (pre-installed in framework-res.apk)

 * 0x7f: application resources (bundled in the application .apk)

ResourceTypes.cpp supports package IDs between 0x01 and 0x7f
(inclusive); values outside this range are invalid.

Each running (Dalvik) process is assigned a unique instance of
AssetManager, which in turn keeps a forest structure of loaded
resource.arsc files. Normally, this forest is structured as follows,
where mPackageMap is the internal vector employed in ResourceTypes.cpp.

mPackageMap[0x00] -> system package
mPackageMap[0x01] -> NULL
mPackageMap[0x02] -> NULL
...
mPackageMap[0x7f - 2] -> NULL
mPackageMap[0x7f - 1] -> application package



The resource overlay extension
------------------------------

The resource overlay mechanism aims to (partly) shadow and extend
existing resources with new values for defined and new configurations.
Technically, this is achieved by adding resource-only packages (called
overlay packages) to existing resource namespaces, like so:

mPackageMap[0x00] -> system package -> system overlay package
mPackageMap[0x01] -> NULL
mPackageMap[0x02] -> NULL
...
mPackageMap[0x7f - 2] -> NULL
mPackageMap[0x7f - 1] -> application package -> overlay 1 -> overlay 2

The use of overlay resources is completely transparent to
applications; no additional resource identifiers are introduced, only
configuration/value pairs. Any number of overlay packages may be loaded
at a time; overlay packages are agnostic to what they target -- both
system and application resources are fair game.

The package targeted by an overlay package is called the target or
original package.

Resource overlay operates on symbolic resources names. Hence, to
override the string/str1 resources in a package, the overlay package
would include a resource also named string/str1. The end user does not
have to worry about the numeric resources IDs assigned by aapt, as this
is resolved automatically by the system.

As of this writing, the use of resource overlay has not been fully
explored. Until it has, only OEMs are trusted to use resource overlay.
For this reason, overlay packages must reside in /system/overlay.


Resource ID mapping
~~~~~~~~~~~~~~~~~~~
Resource identifiers must be coherent within the same namespace (ie
PackageGroup in ResourceTypes.cpp). Calling applications will refer to
resources using the IDs defined in the original package, but there is no
guarantee aapt has assigned the same ID to the corresponding resource in
an overlay package. To translate between the two, a resource ID mapping
{original ID -> overlay ID} is created during package installation
(PackageManagerService.java) and used during resource lookup. The
mapping is stored in /data/resource-cache, with a @idmap file name
suffix.

The idmap file format is documented in a separate section, below.


Package management
~~~~~~~~~~~~~~~~~~
Packages are managed by the PackageManagerService. Addition and removal
of packages are monitored via the inotify framework, exposed via
android.os.FileObserver.

During initialization of a Dalvik process, ActivityThread.java requests
the process' AssetManager (by proxy, via AssetManager.java and JNI)
to load a list of packages. This list includes overlay packages, if
present.

When a target package or a corresponding overlay package is installed,
the target package's process is stopped and a new idmap is generated.
This is similar to how applications are stopped when their packages are
upgraded.


Creating overlay packages
-------------------------

Overlay packages should contain no code, define (some) resources with
the same type and name as in the original package, and be compiled with
the -o flag passed to aapt.

The aapt -o flag instructs aapt to create an overlay package.
Technically, this means the package will be assigned package id 0x00.

There are no restrictions on overlay packages names, though the naming
convention <original.package.name>.overlay.<name> is recommended.


Example overlay package
~~~~~~~~~~~~~~~~~~~~~~~

To overlay the resource bool/b in package com.foo.bar, to be applied
when the display is in landscape mode, create a new package with
no source code and a single .xml file under res/values-land, with
an entry for bool/b. Compile with aapt -o and place the results in
/system/overlay by adding the following to Android.mk:

LOCAL_AAPT_FLAGS := -o com.foo.bar
LOCAL_MODULE_PATH := $(TARGET_OUT)/overlay


The ID map (idmap) file format
------------------------------

The idmap format is designed for lookup performance. However, leading
and trailing undefined overlay values are discarded to reduce the memory
footprint.


idmap grammar
~~~~~~~~~~~~~
All atoms (names in square brackets) are uint32_t integers. The
idmap-magic constant spells "idmp" in ASCII. Offsets are given relative
to the data_header, not to the beginning of the file.

map          := header data
header       := idmap-magic <crc32-original-pkg> <crc32-overlay-pkg>
idmap-magic  := <0x706d6469>
data         := data_header type_block+
data_header  := <m> header_block{m}
header_block := <0> | <type_block_offset>
type_block   := <n> <id_offset> entry{n}
entry        := <resource_id_in_target_package>


idmap example
~~~~~~~~~~~~~
Given a pair of target and overlay packages with CRC sums 0x216a8fe2
and 0x6b9beaec, each defining the following resources

Name          Target package  Overlay package
string/str0   0x7f010000      -
string/str1   0x7f010001      0x7f010000
string/str2   0x7f010002      -
string/str3   0x7f010003      0x7f010001
string/str4   0x7f010004      -
bool/bool0    0x7f020000      -
integer/int0  0x7f030000      0x7f020000
integer/int1  0x7f030001      -

the corresponding resource map is

0x706d6469 0x216a8fe2 0x6b9beaec 0x00000003 \
0x00000004 0x00000000 0x00000009 0x00000003 \
0x00000001 0x7f010000 0x00000000 0x7f010001 \
0x00000001 0x00000000 0x7f020000

or, formatted differently

0x706d6469  # magic: all idmap files begin with this constant
0x216a8fe2  # CRC32 of the resources.arsc file in the original package
0x6b9beaec  # CRC32 of the resources.arsc file in the overlay package
0x00000003  # header; three types (string, bool, integer) in the target package
0x00000004  #   header_block for type 0 (string) is located at offset 4
0x00000000  #   no bool type exists in overlay package -> no header_block
0x00000009  #   header_block for type 2 (integer) is located at offset 9
0x00000003  # header_block for string; overlay IDs span 3 elements
0x00000001  #   the first string in target package is entry 1 == offset
0x7f010000  #   target 0x7f01001 -> overlay 0x7f010000
0x00000000  #   str2 not defined in overlay package
0x7f010001  #   target 0x7f010003 -> overlay 0x7f010001
0x00000001  # header_block for integer; overlay IDs span 1 element
0x00000000  #   offset == 0
0x7f020000  #   target 0x7f030000 -> overlay 0x7f020000
+351 −53

File changed.

Preview size limit exceeded, changes collapsed.