Just An Application

January 22, 2013

Android Internals: Resource Ids And Resource Lookup

1.0 Resource Id Structure

An Android Resource id is a 32-bit integer. It comprises

  • an 8-bit Package id [bits 24-31]
  • an 8-bit Type id [bits 16-23]
  • a 16-bit Entry index [bits 0-15]

The Package id identifies the Package chunk which contains the Resource.

The Type id identifies the type of the Resource and hence the corresponding Typespec chunk and
Type chunk or chunks which contain its value or value(s)

The Entry index identifies the individual Resource within the Typespec chunk and Type chunk(s).

1.1 Example

The four Resouce identifiers generated as part of the Resource Table example here break down as follows

Name Value Package id Type id Entry index
icon 0x7f020000 0x7f 2 0
main 0x7f030000 0x7f 3 0
hello 0x7f040000 0x7f 4 0
app_name 0x7f040001 0x7f 4 1

From this we can see that the icon resource is of type 2, the main resource is of type 3, and the hello and app_name resources are of type 4.

We know that the name of the Resource type with id i can be found at the index i - 1 in the typeStrings StringPool Chunk

Looking at the Package chunk of the Resource Table example here we can see that the names corresponding to the type ids

  • 2
  • 3
  • 4

are

  • drawable
  • layout
  • string

respectively as we would expect.

2.0 Resource Value Lookup

To find the value of a Resource in a Resource Table given its id.

  1. Extract the package id (p) the type id (t) and the entry index (e) from the given Resource id

  2. Find the Package chunk with id p. If there is no corresponding Package chunk then the Resource id is invalid.

  3. Find the Typespec chunk and associated Type chunk(s) with id t within the Package chunk. If there is no corresponding Typespec chunk then the Resource id is invalid.

  4. Check that the entry index e is a valid index into the entries in the Typespec chunk. If it is not then the Resource id is invalid.

At this point there are two possibilities, either there is a single Type chunk with the given id, or there are multiple Type chunks with the given id.

2.1 The Single Type Chunk Case

  1. Lookup the offset o of the entry for the Resource with the given id at index e within the offsets table of the Type chunk

  2. Go to the offset o past the start of the entries in the Type chunk as specified by the field entriesStart

  3. At this offset there will be either a simple or a complex resource entry which will specify the value of the Resource.

2.2 The Multiple Type Chunk Case

For each Type chunk

  1. Lookup the offset o of the entry for the Resource with the given id at index e within the offsets table of the Type chunk

  2. If the value of offset o is 0xFFFFFFFF then there is no value for the Resource in this Type chunk.

  3. Otherwise the value cane be found in the same way as for the single Type chunk case above.

If we are looking for a value for a specifiec configuration then for each Type chunk that contains a value for the Resource we need to record the configuration associated with the value as stored in the config field of the Type chunk.

Once we have collected all the available values for the Resource we then look for the closest match for the configuration we are interested in.

Exactly how you match configurations is left as an exercise for the reader.

2.3 Examples

2.3.1 Single Type Chunk Example

Given the id of the hello Resource, in the Resource table example here, which is 0x7f040000, then

  • p is 127
  • t is 4
  • e is 0

Using the algorithm above we end up with the Typespec chunk with id 4 and a single associated Type chunk which looks like this

    ...

    00000408 01 02       // type [TYPE]
    0000040a 34 00       // header size
    0000040c 5c 00 00 00 // chunk size
    --------------------

    00000410 04          // id
    00000411 00          // 0
    00000412 00 00       // 0
    00000414 02 00 00 00 // entryCount
    00000418 3c 00 00 00 // entriesStart (address 00000444)
    0000041c 20 00 00 00 // config: size
    00000420 00 00 00 00 // config: imsi
    00000424 00 00 00 00 // config: locale
    00000428 00 00 00 00 // config: screenType
    0000042c 00 00 00 00 // config: input
    00000430 00 00 00 00 // config: screenSize
    00000434 00 00 00 00 // config: version
    00000438 00 00 00 00 // config: screenConfig
    ++++++++++++++++++++

    0000043c 00 00 00 00 // entry_index[0]
    00000440 10 00 00 00 // entry_index[1]
    00000444 08 00       // entry[0] size
    00000446 00 00       // entry[0] flags
    00000448 02 00 00 00 // entry[0] key
    0000044c 08 00       // size
    0000044e 00          // 0
    0000044f 03          // dataType
    00000450 04 00 00 00 // data
    00000454 08 00       // entry[1] size
    00000456 00 00       // entry[1] flags
    00000458 03 00 00 00 // entry[1] key
    0000045c 08 00       // size
    0000045e 00          // 0
    0000045f 03          // dataType
    00000460 05 00 00 00 // data
    ==================== [End of TYPE]
    
    ...

The entry corresponding to the index 0 starts at 0x00000444.

It is a simple entry, the flag FLAG_COMPLEX is not set in the flags field, which means that it is immediately followed by an instance of struct Res_value. which specifies the value of the Resource.

The dataType field is 3 which identifies it as as string. The data field is 4, which, in the case of a value of type string, is the index of the string within the String pool chunk

Looking at the example the string at index 4 is

    "Hello World, PendragonActivity!"

as we would expect

2.3.2 Muliple Type Chunk Example

Given the id of the icon Resource () in the Resource table example here, which is 0x7f020000, then

  • p is 127
  • t is 2
  • e is 0

Using the algorithm above we end up with the Typespec chunk with id 2 and three associated Type chunks which look like this

    ...

    000002bc 01 02       // type [TYPE]
    000002be 34 00       // header size
    000002c0 48 00 00 00 // chunk size
    --------------------

    000002c4 02          // id
    000002c5 00          // 0
    000002c6 00 00       // 0
    000002c8 01 00 00 00 // entryCount
    000002cc 38 00 00 00 // entriesStart (address 000002f4)
    000002d0 20 00 00 00 // config: size
    000002d4 00 00 00 00 // config: imsi
    000002d8 00 00 00 00 // config: locale
    000002dc 00 00 78 00 // config: screenType
    000002e0 00 00 00 00 // config: input
    000002e4 00 00 00 00 // config: screenSize
    000002e8 04 00 00 00 // config: version
    000002ec 00 00 00 00 // config: screenConfig
    ++++++++++++++++++++

    000002f0 00 00 00 00 // entry_index[0]
    000002f4 08 00       // entry[0] size
    000002f6 00 00       // entry[0] flags
    000002f8 00 00 00 00 // entry[0] key
    000002fc 08 00       // size
    000002fe 00          // 0
    000002ff 03          // dataType
    00000300 00 00 00 00 // data
    ==================== [End of TYPE]

    00000304 01 02       // type [TYPE]
    00000306 34 00       // header size
    00000308 48 00 00 00 // chunk size
    --------------------

    0000030c 02          // id
    0000030d 00          // 0
    0000030e 00 00       // 0
    00000310 01 00 00 00 // entryCount
    00000314 38 00 00 00 // entriesStart (address 0000033c)
    00000318 20 00 00 00 // config: size
    0000031c 00 00 00 00 // config: imsi
    00000320 00 00 00 00 // config: locale
    00000324 00 00 a0 00 // config: screenType
    00000328 00 00 00 00 // config: input
    0000032c 00 00 00 00 // config: screenSize
    00000330 04 00 00 00 // config: version
    00000334 00 00 00 00 // config: screenConfig
    ++++++++++++++++++++

    00000338 00 00 00 00 // entry_index[0]
    0000033c 08 00       // entry[0] size
    0000033e 00 00       // entry[0] flags
    00000340 00 00 00 00 // entry[0] key
    00000344 08 00       // size
    00000346 00          // 0
    00000347 03          // dataType
    00000348 01 00 00 00 // data
    ==================== [End of TYPE]

    0000034c 01 02       // type [TYPE]
    0000034e 34 00       // header size
    00000350 48 00 00 00 // chunk size
    --------------------

    00000354 02          // id
    00000355 00          // 0
    00000356 00 00       // 0
    00000358 01 00 00 00 // entryCount
    0000035c 38 00 00 00 // entriesStart (address 00000384)
    00000360 20 00 00 00 // config: size
    00000364 00 00 00 00 // config: imsi
    00000368 00 00 00 00 // config: locale
    0000036c 00 00 f0 00 // config: screenType
    00000370 00 00 00 00 // config: input
    00000374 00 00 00 00 // config: screenSize
    00000378 04 00 00 00 // config: version
    0000037c 00 00 00 00 // config: screenConfig
    ++++++++++++++++++++

    00000380 00 00 00 00 // entry_index[0]
    00000384 08 00       // entry[0] size
    00000386 00 00       // entry[0] flags
    00000388 00 00 00 00 // entry[0] key
    0000038c 08 00       // size
    0000038e 00          // 0
    0000038f 03          // dataType
    00000390 02 00 00 00 // data
    ==================== [End of TYPE]
	
    ...

The configurations stored in the Type chunks differ only in the screenType field, the values of which are

  • 0x78
  • 0xa0
  • 0xf0

respectively.

These values correspond to the anonymous enum values

  • DENSITY_LOW

  • DENSITY_MEDIUM

  • DENSITY_HIGH

In each Type chunk there is a single entry at index 0.

The entries start at

  • 0x000002f4

  • 0x0000033c

  • 0x00000384

Each entry is a simple entry.

In each case the dataType field of the value following the entry is 3 indicating that the data is a string.

The values of the data fields of the values are

  • 0
  • 1
  • 2

These are indexes into the String pool chunk.

Looking at the example we can see that the corresponding strings are

  • “res/drawable-ldpi/icon.png”

  • “res/drawable-mdpi/icon.png”

  • “res/drawable-hdpi/icon.png”

which is what we would expect given the configurations specified by the Type chunks.


Copyright (c) 2013 By Simon Lewis. All Rights Reserved.

Create a free website or blog at WordPress.com.