Why we have to wait for Android on the Neo 1973

Wed, 21 Nov 2007 17:46:25 +0000
tech android article arm

update: It finally works!

So after a day of much fun and hacking, I sadly blog here in the face of defeat. Thwarted by binary only distribution and non-forwards compatible architectures. This tale of woe documents my attempt to get the Android stack running on the FIC Neo 1973 phone.

This post describes what I did to try and get this working, and ultimately why it isn't going to work until you get the source for the stack. And if you find the story excruciatingly just skip to the conclusion.

The day started out promisingly. I took the diff of the kernel I had earlier produced, and started to hack it down into something a little more manageable. First I got rid of the patches that enabled Qemu, since I only care about running this on the real hardware. Then I got rid of the patches that enabled the goldfish platform. The goldfish platform is the hardware platform that the Android SDK simulates. I don't need that for running on the Neo, so gone! Next there was a whole big patchset for enabling yaffs2. The Openmoko kernel already has yaffs2 patched in there, so this just causes confusion. Once all that is done, the final patch is much more manageable; 8000 lines rather than 30000 lines.

So with that in place, I pulled down the 2.6.22.5 kernel.org kernel, and then applied the Openmoko patchset with quilt. With that in place I applied my stripped down diff. Now this was against 2.6.23, rather than 2.6.22 so there was a bit of fuzz and a couple of failed hunks. (That sounds more, like gangster slang than hacking!). Anyway, after fixing up the patch, I now have patch that applies cleanly against the Openmoko kernel. (Not that it will do you much good as you are about to see.)

So I took the recommended default config for openmoko, and ran trusty make oldconfig. This prompted for a couple of new options:

PANIC_TIMEOUT=0
CONFIG_BINDER=y
CONFIG_LOW_MEMORY_KILLER=y

I ended up removing the low memory killer option because it had compile errors. I was going to go back and fix, but in the end didn't really matter.

This was a lot of progress in my first hour or two of hacking. I then proceeded to waste a whole bunch of time on stupid stuff. I'll save you all the gory details, but highlights follow.

Firstly just trying to get stuff running on Neo 1973 proved a bit of a challenge. I eventually found some known binaries, and workd ut how to get them onto the phone:

$ sudo dfu-util -a 5 -R -D om.rootfs.jffs2
$ sudo dfu-util -a 3 -R -D om.uImage.bin 

(Special thanks to David who pointed me at the right places.)

The next challenge was actually just getting the kernel I had built from to work with the rootfs rather than just the binary kernel. This was particularly difficult to actually debug because there were no error messages or panics, just the kernel sitting at Freeing init memory, and no more. As far as I could tell it was the same kernel source, and I had used the default config. So after lots of messing around (different compilers, with/without modules, etc), I stumbled upon the fact that the user mode binary applications on the rootfs image are all using the new EABI, as opposed to the old ABI. It turns out that special kernel support needs to be enabled for EABI, and this isn't in the default openmoko kernel config. (I don't have a good reference for EABI vs. OABI. Linux devices has a story, but the floating point stuff is really only one small part of the differences.). Anyway, after enabling the CONFIG_AEBI things started going a lot smoother. I really wish that more people enabled the /proc/config.gz option, it would have made life a lot easier.

So at this point I had a kernel with the Android patches loading and running the standard OpenMoko distribution. Next step was to run a different rootfs. Taking the filesytems I had extracted earlier as well as the rootfs I had extracted (see this post for details), I combined these and used mkfs.jffs2 to build an android jffs filesystem. (For reference the full command is: $ sudo mkfs.jffs2 -x lzo -r android-root-image -o android.jffs2 --eraseblock=0x4000 --pad -n -squash). At this point I thought I was home free. How very wrong I was.

On booting this I was back at the dreaded Freeing init memory, with no other output. Confused with this, I compiled a very simple hello world program to see if this would work as a replacement init (just for testing). This didn't work either.

With this failure I tried another tack. I would revert to my known good, of the working openmoko rootfs, and install my hello program on this rootfs just to test it. I didn't think I would have a problem here. It turns out it failed to run. Luckily the openmoko rootfs has gdb, which is great for fixing problems like this. Firing up gdb soon let me to the real problem.

ARMv4 vs. ARMv5

So, it turns out that my hello binary (and all the android binaries) are compiled for an ARM926Ej-S chip. This is a problem because the neo1973 has an ARM920T core. Now you would think that ARM926 and ARM920 would be pretty close. But if you thought that you would, unfortunately, be wrong, wrong, wrong! The ARM926EJ-S implement the ARMv5TEJ instruction set, but the ARM920T implements the ARMv4T instruction set. So what happens in my hello program is that we hit an ARMv5 instruction, which is undefined in the earlier ARMv5 ISA, which generates an undefined instruction trap to the kernel, and the kernel responds by sending SIGILL to the running process. Assuming that the program hasn't installed any special signal handlers this will kill the process. And this is what was happening to my hello program, and what I assumed was happening to init as well. (Of course, assumptions make an ass out of u and me, or in this case, mostly me.)

Now I really wasn't going to be daunted by a pesky little thing such as the CPU not implementing the instructions stand in my way! (Note: I could of course have compiled hello for ARMv4 architecture, but that isn't an option for the rest of the stack, and I was only interested in getting hello running so I could get the rest of the stack running). So, in an act of stupid defiance, I decided, if the CPU can't implement the instruction, I'll do it myself.

Luckily the kernel provides a neat infrastructure for managing undefined instructions, and even emulating them. So the first instruction to emulate was the ARM clz instruction. This is the instruction that counts the number of leading zero bits. The code below implements this. The only other thing to do is ensure that this hook is registered at startup using: register_undef_hook(&clz_hook);

static int clz_trap(struct pt_regs *regs, unsigned int instr)
{
  /* Extract the source register index */
  int src = instr & 0xf;
  /* Extract the destination (result) register index */
  int dst = (instr >> 12) & 0xf;
  /* Extract the conditional code */
  int cc = (instr >> 28) & 0xf;
  /* Test if the conditional code passes */
  if (handle_cc(regs, cc)) {
      /* Implement the instruction */
      regs->uregs[dst] = 32 - fls(regs->uregs[src]);
  }
  /* Print some stuff for debugging */
  printk("emulating clz: %x src=%d (%lx) dst=%d (%lx) @ %p\n", instr, 
	 src, regs->uregs[src], dst, regs->uregs[dst], (void*) regs->ARM_pc);
  /* Increment the PC register */
  regs->ARM_pc += 4;
  
  return 0;
}

static struct undef_hook clz_hook = {
	.instr_mask	= 0x0fff0ff0,
	.instr_val	= 0x016f0f10,
	.cpsr_mask	= PSR_T_BIT,
	.cpsr_val	= 0,
	.fn		= clz_trap,
};

One thing that may not be clear from the comments is that ARM supports conditionally executed instructions. The top 4 bits of the instruction are its condition field. Depending on the condition field, and the value of the N, Z, C and V flags (which are stored in the CPSR register), the instruction may or may not be executed. This is used to avoid having to branch for all if statements and the associated problems... but you didn't come here for an introduction to computer architecture. To correctly implement this, some code is needed, and I clag it here for posterity.

/* Return true if conditional code should be executed */
static int handle_cc(struct pt_regs *regs, int cc)
{
  int doit = 0;
  int cpsr = regs->ARM_cpsr;

  int n = (cpsr >> 31) & 1;
  int z = (cpsr >> 30) & 1;
  int c = (cpsr >> 29) & 1;
  int v = (cpsr >> 28) & 1;

  switch (cc) {
  case 0:
    doit = z;
    break;
  case 1:
    doit = !z;
    break;
  case 2:
    doit = c;
    break;
  case 3:
    doit = !c;
    break;
  case 4:
    doit = n;
    break;
  case 5:
    doit = !n;
    break;
  case 6:
    doit = v;
    break;
  case 7:
    doit = !v;
    break;
  case 8:
    doit = c && !z;
    break;
  case 9:
    doit = !c || z;
    break;
  case 10:
    doit = (n == v);
    break;
  case 11:
    doit = (n != v);
    break;
  case 12:
    doit = ((z == 0) && (n == v));
    break;
  case 13:
    doit = ((z == 1) || (n != v));
    break;
  case 14:
    doit = 1;
    break;
  case 15:
    doit = 0;
    break;
  default:
    printk("Error, should get here!\n");
  }
  return doit;
}

OK, one down. That wasn't so hard. The next one gets a little bit tricker. The compiler will use the BLX instruction if it is available. This is the Branch, Link and Exchange instruction. There are two versions of the instruction, and at this stage we only really care about version 2. In this version the address to branch to is stored in a register, and a flag indicates whether or not an exchange is required. (You can ignore exchange for now, more about that later.). This instruction is a little bit more effort to implement, but it is not too hard:

static int blxv2_trap(struct pt_regs *regs, unsigned int instr)
{
  int rm = instr & 0xf;
  int cc = (instr >> 28) & 0xf;
  
  printk("emulate blxv2: %x rm=%d (%lx) @ %p CC: %d cc(%lx) cpsr(%lx)\n", instr, rm,
	 regs->uregs[rm], (void*) regs->ARM_pc, handle_cc(regs, cc), cc, regs->ARM_cpsr);

  if (handle_cc(regs, cc)) {
    /* Update the link register with the return address 8/
    regs->ARM_lr = regs->ARM_pc + 4;
    /* Update the CPSR is this is an 'exchange' */
    regs->ARM_cpsr = (regs->ARM_cpsr & (~32))  | ((regs->uregs[rm] & 1) << 5);
    /* Jump to the register value */
    regs->ARM_pc = regs->uregs[rm] & 0xfffffffe;
  } else {
    /* If the condition code fail, just go to the next instruction. */
    regs->ARM_pc += 4;
  }
  return 0;
}

static struct undef_hook blxv2_hook = {
	.instr_mask	= 0x0ffffff0,
	.instr_val	= 0x012fff30,
	.cpsr_mask	= PSR_T_BIT,
	.cpsr_val	= 0,
	.fn		= blxv2_trap,
};

After this, success! Hello world ran correctly. Of course this emulation isn't going to be particularly fast, but it is still infinitely faster than not running at all. (Well, OK, not really, divide by zero is undefined, not infinite.) At this point we were feeling pretty good with ourselves. At this point I must acknowledge Carl and Matt for there assistance with this.

Thumb interworking

So now I really thought I was home free, but wrong once again. (A pattern emerging maybe?) So first a bit of a primer on ARM's Thumb mode (so punny!). ARM has two different instruction sets, the ARM instruction set, and the Thumb instruction set. The Thumb instruction set is a 16-bit instruction set, which has a higher code density than the ARM instruction set. Now the neat thing about this is that you can actually combine both ARM and Thumb instruction in the same program. So if your compiler is smart, it should be able to use both instruction sets for optimisation. The CPU knows whether code is executing in ARM or Thumb mode by a bit in the CPSR register. When the bit is set the instruction stream is assumed to be 16-bit Thumb instruction. Now if you are running in ARM mode, and want to enter Thumb mode, you need to do an exchange operation, which is part of the bx and blx instructions. Now it turns out that Android is compiled with Thumb mode, so this means it uses blx to switch from ARM to Thumb mode. So at this stage I ended up needing to implement blx (version 1) function. This is shown below:

static int blxv1_trap(struct pt_regs *regs, unsigned int instr)
{
  int h = (instr >> 24) & 1;
  long imm = ((instr & 0xffffff) << 8);
  /* should be signed extended */
  imm = imm >> 8;
  imm = imm << 2;
  imm = imm | (h << 1);
  printk("emulate blxv1: %x imm=%lx @ %p\n", instr, imm, (void*) regs->ARM_pc);

  regs->ARM_lr = regs->ARM_pc + 4;
  regs->ARM_cpsr = regs->ARM_cpsr | (1 << 5);
  regs->ARM_pc += imm;
  return 0;
}

static struct undef_hook blxv1_hook = {
	.instr_mask	= 0xfe000000,
	.instr_val	= 0xfa000000,
	.cpsr_mask	= PSR_T_BIT,
	.cpsr_val	= 0,
	.fn		= blxv1_trap,
};

Now, we get a bit further. But still no go. It turns out that Thumb also has a new BLX instruction in V5. So, we have to go through and emulate this instruction for Thumb as well. Below is the code for that.

static int blxv1_t_trap(struct pt_regs *regs, unsigned int instr)
{
  u16 offset_11 = instr & 0x7ff;
  u16 h = (instr >> 11) & 3;
  printk("blx thumb %lx ofs: %lx h: %d @ %p\n", 
	 instr, offset_11, h, (void*) regs->ARM_pc);
  
  if (h == 2) {
    long imm = (offset_11 << 12) << 9;
    imm = imm >> 9;
    regs->ARM_lr = regs->ARM_pc + (imm << 12);
    regs->ARM_pc = regs->ARM_pc + 2;
  } else {
    long new_pc = regs->ARM_lr + (offset_11 << 1);
    /* We set the top bit for mega hack! */
    regs->ARM_lr = (regs->ARM_pc + 2) | 1;
    regs->ARM_pc = new_pc;
    if (h == 1) {
      regs->ARM_lr = (1 << 31) | (((regs->ARM_lr & 2) >> 1) << 30) | regs->ARM_lr;
      regs->ARM_pc = regs->ARM_pc & 0xfffffffc;
      regs->ARM_cpsr = regs->ARM_cpsr & (~32);
    }
  }

  printk(" blx thumb after: pc: %lx lr: %lx cpsr: %lx\n", regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr);
  return 0;
}

static struct undef_hook blxv1_t_hook = {
	.instr_mask	= 0xe000,
	.instr_val	= 0xe000,
	.cpsr_mask	= PSR_T_BIT,
	.cpsr_val	= PSR_T_BIT,
	.fn		= blxv1_t_trap,
};

Now if you are still with me, and actually read the code, you might recognise some pretty interesting code. Spot it? No? OK, so the problem is the way in which ARM code returns to Thumb mode. The blx instruction updates the link register with the return address. In Thumb mode it also sets the lowest bit. This ensures that when bx is called from ARM mode it will jump back into Thumb mode. It turns out that having to use bx to return from functions is a bit of a pain, so in ARMv5, the architecture was updated so that if you popped values from the stack into the pc register, the CPU would also check the low bit and switch to Thumb mode if required. Unfortunately ARMv4 doesn't do this. Rather than checking the lower bit, it simply ignores it and masks it off, which means you jump back to the return address but remain in ARM mode, so you end up executing 16-bit instructions as though they were 32-bit instructions. It may not surprise you to learn that this generally doesn't work so well.

Which gets us to the truly evil code found above. As well as setting the low bit, we also go and set the top bit of the LR. When the ARM code returns from the function, rather than going to the correct location, it ends up at an unmapped location, which causes a pre-fetch abort. The prefetch abort handler was then updated to handle this error case.

asmlinkage void __exception
do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
{
  if (addr >> 31) {
    printk("Magic prefetch abort happened on: %x\n", addr);
    regs->ARM_pc = addr & 0x3ffffffe | ((addr >> 29) & 2);
    printk("  jumping to : %x\n", regs->ARM_pc);
    /* Enable thumb mode */
    regs->ARM_cpsr = (regs->ARM_cpsr | 32);
    return;
  }
  do_translation_fault(addr, 0, regs);
}

Now, at this stage, we have something pretty hacked up, but all these hacks are pretty solid. Unfortunately it still doesn't work. We have successfully ensured that ARM code returns correctly when called from Thumb mode, what we have failed to do is ensure that Thumb code returns correct to ARM code. In ARMv4, this is only possible through the bx instruction, which correctly sets the Thumb bit, in the CPSR. Unfortunately on ARMv5, the pop instruction was extended to also correctly update the thumb bit. But we aren't on an ARMv5, so it is simply ignored. Which means we get stuck in Thumb mode and can't correctly return to ARM code.

The prefetch abort trick works to an extent the other way as well, e.g: for getting from Thumb, back into ARM, but it relies on the ARM code using the blx instruction. Unfortunately this isn't always the case, and it is perfectly reasonably for code to use a bl followed by a bx. As none of these trap it is not possible to put our magic fake value into the LR register.

The only other option left at this stage is some kind of code scanning technique. In this we scan the object code looking for the unsafe pop instructions, and replace them with undefined instruction so that we safely emulate them with the ARMv5 behaviour. Unfortunately ARM makes this approach basically impossible. It is not possible to tell if any block of code is Thumb or ARM instructions. More importantly, it is impossible to determine if a random word in the text segment is actually an instruction, or is in fact a literal value. Simply scanning for pop could actually modify some constants, which would lead to potentially subtle bugs. If ARM had separate execute and read permissions we could use the MMU to distinguish between code and data, but unfortunately the ARM MMU can't really do this. Which means that this approach is basically a no-go, at least not without some pretty nasty heuristics, or some really awesome static analysis. Of course we could just emulate every instruction, but this isn't exactly appealing to me. (And the performance would really suck!)

Conclusion

In summary, Android is compiled for ARMv5, Neo 1937 is ARMv4. These instruction sets are not compatible. Therefore Android will not run on the Neo 1937. Solutions to this problem would be either:

My guess is none of those three things is going to happen any time soon (although I'll be really happy to be disproved!), so it is better to focus on trying to get this running on an actual ARMv5 based chipset. (E.g: PXA270, i.MX21).

Finally, thanks to Jaq, Carl, David and Matt for providing inspiration and advice.

Update: Thanks to andrzej for spotting the bug in my clz() emulation. It should of course be 32 - fls(), not fls(). This is now updated.

Android - strace runtime

Sun, 18 Nov 2007 16:22:37 +0000
tech android article

Last time I looked at the processes involved with initialising the Android Java framework. This wasn't able to give us much information on what was happening. To get some more data we are going to be using the strace tool. I've compiled a version of strace for Android, which you can download and use.

This time we start runtime using strace: ./strace -ff -F -tt -s 200 -o /tmp/strace runtime. -ff makes strace follow fork() and output each forked files trace to a different file. -F means we try and follow any vfork()s. -tt prints out the time of system calls in microseconds. -s 200 so that we can see a bit more detail in any strings that are used. After doing this we also start up zygote.

After start-up we can get a little bit more information about the running process. When we dig into the /proc/ directory of our runtime task we find a bit more information. In particular we see that as well as the main process, there are two extra threads. Now we are in a good position to start examining the trace files. We copy these across to the host using adb pull to make analysis a little easier.

We start by looking at the main process. Below is the annotate trace of the main thread.

The first part is just standard process initialisation:

05:30:51.171267 execve("/system/bin/runtime", ["runtime"], [/* 8 vars */]) = 0
05:30:51.179335 getpid()                = 493
05:30:51.180403 syscall_983045(0xb0016b68, 0xb0013780, 0x3e4, 0, 0xbef86e58, 0x1, 0, 0xf0005, 0xb0013780, 0, 0, 0xbef86e54, 0, 0xbef86e08, 0xb0000d89, 0xb00016ec, 0x10, 0xb0016b68, 0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
05:30:51.181639 gettid()                = 493
05:30:51.182628 sigaction(SIGILL, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.184487 sigaction(SIGABRT, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.186137 sigaction(SIGBUS, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.187460 sigaction(SIGFPE, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.188726 sigaction(SIGSEGV, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.189968 sigaction(SIGSTKFLT, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0

Next up is the loading of shared libraries. I've stripped this down after the first one, as it is just the same blocks of code with different actual libraries. It doesn't quite load all the libraries on the system, but it goes pretty close:

05:30:51.191422 open("libandroid_runtime.so", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
05:30:51.193687 open("/system/lib/libandroid_runtime.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.195163 lseek(4, -8, SEEK_END)  = 313648
05:30:51.196653 read(4, "\0\0000\255PRE ", 8) = 8
05:30:51.198031 mmap2(0xad300000, 315392, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 4, 0) = 0xad300000
05:30:51.199144 close(4)                = 0
05:30:51.202026 mmap2(0xad34d000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) = 0xad34d000
...
...
05:30:51.205846 open("/system/lib/libexpat.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.212147 open("/system/lib/libc.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.222236 open("/system/lib/libstdc++.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.228944 open("/system/lib/libm.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.237093 open("/system/lib/libnativehelper.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.246900 open("/system/lib/libcutils.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.257238 open("/system/lib/libssl.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.266356 open("/system/lib/libcrypto.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.285991 open("/system/lib/libutils.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.292459 open("/system/lib/libz.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.314445 open("/system/lib/libicudata.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.323224 open("/system/lib/libicuuc.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.339731 open("/system/lib/libicui18n.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.366170 open("/system/lib/libsqlite.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.384451 open("/system/lib/libui.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.394162 open("/system/lib/libcorecg.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.403491 open("/system/lib/libpixelflinger.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.411948 open("/system/lib/libhardware.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.419561 open("/system/lib/libmedia.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.491956 open("/system/lib/libsgl.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.517146 open("/system/lib/libdvm.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.534603 open("/system/lib/libpim.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.542365 open("/system/lib/libopengles_cm.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.554764 open("/system/lib/libsonivox.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.566665 open("/system/lib/libdbus.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.602983 open("/system/lib/libsystem_server.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.610384 open("/system/lib/libsurfaceflinger.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.652278 open("/system/lib/libaudioflinger.so", O_RDONLY|O_LARGEFILE) = 4

Not quite sure what this stuff does. Nothing too important though.

05:30:51.698931 gettid()                = 493
05:30:51.699882 syscall_983045(0xbef86d5c, 0, 0x20, 0, 0xbef67000, 0xbef86e10, 0xbef86e50, 0xf0005, 0xbef86e50, 0, 0xd9e8, 0xd9e4, 0, 0xbef86d50, 0xafe20a4b, 0xafe0949c, 0x60000010, 0xbef86d5c, 0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0

This next bit is interesting. We open a socket, but the path is a little interesting. Definitely needs some more investigation.

05:30:51.701110 socket(PF_FILE, SOCK_STREAM, 0) = 4
05:30:51.702175 connect(4, {sa_family=AF_FILE, path=@property_service}, 19) = 0
05:30:51.703602 recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{"\0\200\0\0", 4}], msg_controllen=16, {cmsg_len=16, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, {5}}, msg_flags=0}, 0) = 4
05:30:51.704618 mmap2(NULL, 32768, PROT_READ, MAP_SHARED, 5, 0) = 0x40000000
05:30:51.705794 close(5)                = 0
05:30:51.706714 close(4)                = 0

Try to open a file for malloc debug. It might be interesting to actually create this file and seem what happens.

05:30:51.708084 open("/data/malloc_debug", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)

Something did a malloc presumably, causing the brk.

05:30:51.709572 brk(0x15000)            = 0x15000

Scan the /tmp directory for some reason.

05:30:51.710607 open("/tmp", O_RDONLY|O_LARGEFILE|O_DIRECTORY) = 4
05:30:51.711709 getdents64(4, /* 6 entries */, 4200) = 192
05:30:51.714895 getdents64(4, /* 0 entries */, 4200) = 0
05:30:51.716089 close(4)                = 0

It opens up a bunch of log files.

05:30:51.717808 open("/dev/log/main", O_WRONLY|O_LARGEFILE) = 4
05:30:51.719025 open("/dev/log/radio", O_WRONLY|O_LARGEFILE) = 5
05:30:51.720213 open("/dev/log/events", O_WRONLY|O_LARGEFILE) = 6

Write out a startup message

05:30:51.721375 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"*** Startup: root=\'/system\' asset=\'/system/app\' data=\'/data\' ***\n\0", 66}], 3) = 75

Check the input device is available, but don't open it.

05:30:51.723733 access("/dev/input/event0", R_OK) = 0
05:30:51.725407 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"Key input device: 1 1 \'/dev/input/event0\'\n\0", 43}], 3) = 52
05:30:51.727398 getpid()                = 493
05:30:51.728306 setpgid(0, 493)         = 0
05:30:51.729234 chdir("/system/app")    = 0

05:30:51.730480 open("/dev/alarm", O_RDWR|O_LARGEFILE) = 7
05:30:51.731637 ioctl(7, 0x40086104, 0xbef86ce0) = 0
05:30:51.732909 close(7)                = 0

This bit here is pretty important. /dev/binder is the character device for OpenBinder, which is something that you see in the kernel source. This appears to be an IPC sub-system for the Linux kernel. It was at some stage donated by Palm, and there was an OpenBinder.org website for sometime, although that site is now dead. Finding more information on this will be essential.

05:30:51.733954 open("/dev/binder", O_RDWR|O_LARGEFILE) = 7
05:30:51.735488 fcntl64(7, F_SETFD, FD_CLOEXEC) = 0
05:30:51.736483 ioctl(7, 0xc0046209, 0xbef86cbc) = 0
05:30:51.737443 ioctl(7, 0x40046205, 0xbef86cb8) = 0
05:30:51.738385 mmap2(NULL, 8388608, PROT_READ, MAP_PRIVATE|MAP_NORESERVE, 7, 0) = 0x40008000
05:30:51.739478 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"Entered boot_init()!\n\0", 22}], 3) = 31
05:30:51.741301 writev(4, [{"\3", 1}, {"runtime\0", 8}, {"ProcessState: 0x13420\n\0", 23}], 3) = 32


05:30:51.743109 ioctl(7, 0x40046207, 0xbef86c9c) = 0
05:30:51.744223 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"Binder driver opened.  Multiprocess enabled.\n\0", 46}], 3) = 55
05:30:51.746874 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(ui_hardware, 0x1357c)\n\0", 50}], 3) = 66

A loop trying to communicate with zygote.

05:30:51.749204 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:30:51.750298 socket(PF_FILE, SOCK_STREAM, 0) = 8
05:30:51.751305 connect(8, {sa_family=AF_FILE, path=@android.zygote}, 17) = -1 ECONNREFUSED (Connection refused)
05:30:51.752442 close(8)                = 0
...
05:30:51.753446 nanosleep({0, 500000000}, {0, 500000000}) = 0
05:30:52.266564 socket(PF_FILE, SOCK_STREAM, 0) = 8
05:30:52.268061 connect(8, {sa_family=AF_FILE, path=@android.zygote}, 17) = -1 ECONNREFUSED (Connection refused)
05:30:52.269456 close(8)                = 0
...
05:31:04.230401 nanosleep({0, 500000000}, {0, 500000000}) = 0
05:31:04.746608 socket(PF_FILE, SOCK_STREAM, 0) = 8
05:31:04.748192 connect(8, {sa_family=AF_FILE, path=@android.zygote}, 17) = 0

Once a connection with zygote is obtained it appears to send a message: 5
--allow-disconnect
--nice-name=system_server
--setuid=0
--setgid=0
android.server.SystemServer
. It then seems to receive back a 4 byte message, which seems to be 498, but it's not clear what that is. My guess would be a pid, but it doesn't seem to be true. It seems that zygote acts as a process manager, and runtime can communicate with it to start new processes, in particular it starts up the system_server process.

05:31:04.749657 fcntl64(8, F_GETFL)     = 0x2 (flags O_RDWR)
05:31:04.751247 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40808000
05:31:04.752834 mprotect(0x40808000, 4096, PROT_READ) = 0
05:31:04.754234 fstat64(8, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
05:31:04.757578 mprotect(0x40808000, 4096, PROT_READ|PROT_WRITE) = 0
05:31:04.758739 mprotect(0x40808000, 4096, PROT_READ) = 0
05:31:04.759853 write(8, "5\n--allow-disconnect\n--nice-name=system_server\n--setuid=0\n--setgid=0\nandroid.server.SystemServer\n", 97) = 97
05:31:04.762081 read(8, "\0", 1024)     = 1
05:31:05.818220 read(8, "\0", 1024)     = 1
05:31:05.820120 read(8, "\1", 1024)     = 1
05:31:05.821236 read(8, "\362", 1024)   = 1
05:31:05.822320 close(8)                = 0

Initialise and print out messages on the console.

05:31:05.823445 fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 0), ...}) = 0
05:31:05.825556 mprotect(0x40808000, 4096, PROT_READ|PROT_WRITE) = 0
05:31:05.826475 mprotect(0x40808000, 4096, PROT_READ) = 0
05:31:05.827278 ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
05:31:05.828818 write(1, "+++ post-zygote\n", 16) = 16

Create a new thread -- we find out what this does in a little bit.

05:31:05.832934 mmap2(0x10000000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x10000000
05:31:05.833889 mprotect(0x10000000, 4096, PROT_NONE) = 0
05:31:05.834890 clone(child_stack=0x100fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 484

Not quite sure what is going on here. There appears to be communication with another thread (the SurfaceFlinger) via the futex 0x134a8, it then prints a message once it has found the SurfaceFlinger.

05:31:05.837944 writev(4, [{"\3", 1}, {"runtime\0", 8}, {"DisplayDevice::detach\n\0", 23}], 3) = 32
05:31:05.839713 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: waiting for service SurfaceFlinger\n\0", 52}], 3) = 68
05:31:05.841621 clock_gettime(CLOCK_MONOTONIC, {127, 101400000}) = 0
05:31:05.842936 clock_gettime(CLOCK_MONOTONIC, {127, 103070000}) = 0
05:31:05.844238 clock_gettime(CLOCK_MONOTONIC, {127, 104026000}) = 0
05:31:05.845441 futex(0x134ac, FUTEX_WAIT, -1, {4, 997000000}) = 0
05:31:06.118721 futex(0x134a8, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
05:31:06.122382 futex(0x134a8, FUTEX_WAKE, 1) = 0
05:31:06.126330 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"Display startup: 1 devices\n\0", 28}], 3) = 37

No time like the present to get your PID and UID. No idea why this is required (twice!).

05:31:06.133210 getpid() = 493
05:31:06.154288 getuid32() = 0
05:31:06.160787 getpid() = 493
05:31:06.165845 getuid32() = 0

This block appears to be an IPC using the OpenBinder interface. This is a common block of code that we see throughout. Not quite sure why the getpriority is required every-time. It would be extremely useful to see the contents of the ioctl buffer, but this isn't really available easily in strace. It might mean either extensions to strace, or putting some hacks in the kernel.

05:31:06.168510 getpriority(PRIO_PROCESS, 0) = 20
05:31:06.171049 ioctl(7, 0xc0186201, 0xbef86b08) = 0
05:31:06.187776 ioctl(7, 0xc0186201, 0xbef86b08) = 0

More log file activity. Here we seem to have detached from the display device, presumably that was what the previous IPC was all about.

05:31:06.189902 writev(4, [{"\3", 1}, {"runtime\0", 8}, {"DisplayDevice::shutDown\n\0", 25}], 3) = 34
05:31:06.191663 writev(4, [{"\3", 1}, {"runtime\0", 8}, {"DisplayDevice::detach\n\0", 23}], 3) = 32

This next section seems to be waiting for service activity to occur.

05:31:06.193425 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: waiting for service activity\n\0", 46}], 3) = 62
05:31:06.271693 clock_gettime(CLOCK_MONOTONIC, {127, 531615000}) = 0
05:31:06.272800 clock_gettime(CLOCK_MONOTONIC, {127, 532606000}) = 0
05:31:06.273769 clock_gettime(CLOCK_MONOTONIC, {127, 533559000}) = 0
05:31:06.274735 futex(0x134ac, FUTEX_WAIT, -3, {4, 998000000}) = 0
05:31:06.800315 futex(0x134a8, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
05:31:06.802159 clock_gettime(CLOCK_MONOTONIC, {128, 61969000}) = 0
05:31:06.804065 clock_gettime(CLOCK_MONOTONIC, {128, 63928000}) = 0
05:31:06.879426 futex(0x134a8, FUTEX_WAKE, 1) = 0
05:31:06.882042 futex(0x134ac, FUTEX_WAIT, -4, {4, 467000000}) = 0
05:31:06.891935 futex(0x134a8, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
05:31:06.893777 futex(0x134a8, FUTEX_WAKE, 1) = 0
05:31:06.898754 getpid() = 493
05:31:06.904418 getuid32() = 0
05:31:06.907269 getpriority(PRIO_PROCESS, 0) = 20
05:31:06.909036 ioctl(7, 0xc0186201, 0xbef86b58) = 0
05:31:06.914561 ioctl(7, 0xc0186201, 0xbef86b58) = 0
05:31:06.920828 futex(0xafe40c00, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
05:31:06.923679 futex(0xafe40c00, FUTEX_WAKE, 1) = 0
05:31:06.928106 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Now that service activity is ready to happen, we go through a series of adding services.

05:31:06.933366 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(activity.services, 0x150d0)\n\0", 56}], 3)    = 72
05:31:06.939660 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:06.940605 getpriority(PRIO_PROCESS, 0) = 25
05:31:06.941485 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:06.946561 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Unknown IPC

05:31:08.201642 getpriority(PRIO_PROCESS, 0) = 35
05:31:08.202648 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.205743 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Add the window service

05:31:08.223890 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(window, 0x15418)\n\0", 45}], 3) = 61
05:31:08.226533 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:08.227542 getpriority(PRIO_PROCESS, 0) = 25
05:31:08.228462 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.231366 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Add the USB service

05:31:08.339061 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(USB, 0x151f8)\n\0", 42}], 3)    = 58
05:31:08.341890 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:08.342755 getpriority(PRIO_PROCESS, 0) = 25
05:31:08.343562 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.366177 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Unknown IPC

05:31:08.470072 getpriority(PRIO_PROCESS, 0) = 25
05:31:08.471010 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.471990 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Add the statusbar service.

05:31:08.481632 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(statusbar, 0x15660)\n\0", 48}], 3) = 64
05:31:08.483719 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:08.484601 getpriority(PRIO_PROCESS, 0) = 25
05:31:08.485720 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.491172 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Unknown IPCs

05:31:08.499888 getpriority(PRIO_PROCESS, 0)    = 25
05:31:08.501478 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.505934 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:08.514132 getpriority(PRIO_PROCESS, 0)    = 25
05:31:08.518759 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.523850 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Add the statusbar service.

05:31:08.530638 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(statistics, 0x15890)\n\0", 49}], 3)    = 65
05:31:08.533406 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:08.534278 getpriority(PRIO_PROCESS, 0) = 25
05:31:08.535099 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.626083 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Unknown IPCs

05:31:08.868326 getpriority(PRIO_PROCESS, 0) = 20
05:31:08.869383 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.870513 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:08.874043 getpriority(PRIO_PROCESS, 0) = 20
05:31:08.875021 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.877895 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:08.930930 getpriority(PRIO_PROCESS, 0) = 20
05:31:08.931941 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.932985 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:08.949753 getpriority(PRIO_PROCESS, 0) = 20
05:31:08.950735 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.951764 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:09.065015 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.066231 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.067293 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:09.122422 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.123511 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.124627 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:09.165747 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.166886 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.167966 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:09.206344 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.207418 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.208536 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Add the PhoneNotifier service

05:31:09.428385 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(android.telephony.PhoneNotifier, 0x15928)\n\0", 70}], 3) = 86
05:31:09.431266 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.432328 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.433284 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.434453 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Unknown IPC

05:31:09.449402 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.450514 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.451817 ioctl(7, 0xc0186201, 0xbef86c10) = 0
05:31:09.524328 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(SMS, 0x15988)\n\0", 42}], 3) = 58
05:31:09.526862 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.527847 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.528758 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.529847 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Add the SimPhoneBook service

05:31:09.549454 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(android.telephony.SimPhoneBook, 0x154c0)\n\0", 69}], 3) = 85
05:31:09.552176 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.553202 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.554154 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.556977 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Unknown IPC

05:31:09.704285 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.705523 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.706634 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:09.867287 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.868394 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.869523 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Add the volume service.

05:31:09.886578 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(volume, 0x15540)\n\0", 45}], 3) = 61
05:31:09.888998 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.890025 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.890977 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.892075 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Add the phone service.

05:31:09.910618 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(phone, 0x155b8)\n\0", 44}], 3) = 60
05:31:09.912975 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.913996 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.914986 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.917974 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Unknown IPCs

05:31:09.941316 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.942419 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.943557 ioctl(7, 0xc0186201, 0xbef86c10) = 0

05:31:09.948824 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.949879 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.950985 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Bluetooth service

05:31:10.141405 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth, 0x15ca8)\n\0", 58}], 3) = 74
05:31:10.144074 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:10.145156 getpriority(PRIO_PROCESS, 0) = 20
05:31:10.146278 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:10.147366 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Add bluetooth mailbox service

05:31:10.150666 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth_mailbox, 0x15d50)\n\0", 66}], 3) = 82
05:31:10.153207 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:10.154173 getpriority(PRIO_PROCESS, 0) = 20
05:31:10.155077 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:10.158028 ioctl(7, 0xc0186201, 0xbef86c10) = 0

Many more unknown IPCs

.....
05:31:12.209289 getpriority(PRIO_PROCESS, 0) = 20
05:31:12.210336 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:12.213804 ioctl(7, 0xc0186201

At the end of time, the runtime sits waiting for more IPCs to occur. That ends the analysis of the main runtime thread. In summary it seems to hang around as the ServiceManager, adding services. Presumably the other IPCs relate to other threads requesting services and the like. Before doing this it seems to help kick things off by spawning android.server.SystemServer.

Along the way you may remember that a new thread was spawned (thread id 484). We now take a look at what happens in that thread. It seems to do not very much. It has some interaction with the binder, and then seems to just spawn off a new thread of its own before exiting.

06:38:07.140035 syscall_983045(0x100fff80, 0, 0x20, 0, 0x100fff80, 0x139f0, 0xa9d25ef9, 0xf0005, 0xbeaacc38, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x100fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x100fff80, 0xc1000, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
06:38:07.141657 getpid()                = 479
06:38:07.142520 getuid32()              = 0
06:38:07.143438 ioctl(7, 0xc0186201, 0x100ffef8) = 0
06:38:07.305190 mmap2(0x10100000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x10100000
06:38:07.306160 mprotect(0x10100000, 4096, PROT_NONE) = 0
06:38:07.307099 clone(child_stack=0x101fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 488
06:38:07.308664 getpriority(PRIO_PROCESS, 0) = 20
06:38:07.309560 ioctl(7, 0xc0186201, 0x100ffda8) = 0
06:38:07.310866 ioctl(7, 0xc0186201, 0x100ffef8) = 0
06:38:15.193301 ioctl(7, 0xc0186201, 0x100ffef8) = 0
06:38:15.194829 futex(0x13a00, FUTEX_WAKE, 1) = 0
06:38:15.196296 munmap(0x10000000, 1048576) = 0
06:38:15.198228 exit(0)                 = ?

The next thread is 488. Like the last thread, it seems to just create a new thread (499), and then exit. (Not quite sure why we have these threads that start, do not much and then exit, maybe implementing some asynchronous style IPC(?).

06:38:07.311899 syscall_983045(0x101fff80, 0, 0x20, 0, 0x101fff80, 0x13d50, 0xa9d25ef9, 0xf0005, 0x100ffd90, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x101fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x101fff80, 0xc10f0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
06:38:07.313122 getpid()                = 479
06:38:07.313959 getuid32()              = 0
06:38:07.314808 ioctl(7, 0xc0186201, 0x101ffef8) = 0
06:38:07.370653 mmap2(0x10200000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x10200000
06:38:07.371719 mprotect(0x10200000, 4096, PROT_NONE) = 0
06:38:07.372785 clone(child_stack=0x102fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 499
06:38:07.374282 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: waiting for service SurfaceFlinger\n\0", 52}], 3) = 68
06:38:07.377988 clock_gettime(CLOCK_MONOTONIC, {41, 594807000}) = 0
06:38:07.379786 clock_gettime(CLOCK_MONOTONIC, {41, 596593000}) = 0
06:38:07.381511 clock_gettime(CLOCK_MONOTONIC, {41, 598312000}) = 0
06:38:07.383379 futex(0x134ac, FUTEX_WAIT, -1, {4, 996000000}) = 0
06:38:07.401561 futex(0x134a8, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
06:38:07.405316 futex(0x134a8, FUTEX_WAKE, 1) = 0
06:38:07.409904 getpriority(PRIO_PROCESS, 0) = 30
06:38:07.416223 ioctl(7, 0xc0186201, 0x101ffda8) = 0
06:38:07.440719 ioctl(7, 0xc0186201, 0x101ffef8) = 0
06:38:25.193171 ioctl(7, 0xc0186201, 0x101ffef8) = 0
06:38:25.194719 futex(0x13d60, FUTEX_WAKE, 1) = 0
06:38:25.196134 munmap(0x10100000, 1048576) = 0
06:38:25.198048 exit(0)                 = ?

And now thread 499. This seems to spawn another thread (TID 500), adds the SurfaceFlinger service, and then exit.

06:38:07.376978 syscall_983045(0x102fff80, 0, 0x20, 0, 0x102fff80, 0x14060, 0xa9d25ef9, 0xf0005, 0x101ffd90, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x102fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x102fff80, 0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
06:38:07.379192 getpid() = 479
06:38:07.380922 getuid32() = 0
06:38:07.382691 ioctl(7, 0xc0186201, 0x102ffef8) = 0
06:38:07.385026 mmap2(0x10300000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x10300000
06:38:07.392408 mprotect(0x10300000, 4096, PROT_NONE) = 0
06:38:07.393447 clone(child_stack=0x103fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 500
06:38:07.395459 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(SurfaceFlinger, 0x144a0)\n\0", 53}], 3) = 69
06:38:07.398890 futex(0x134ac, FUTEX_WAKE, 2147483647)                = 2
06:38:07.402396 futex(0x134a8, FUTEX_WAKE, 1)              = 0
06:38:07.406189 getpriority(PRIO_PROCESS, 0)    = 20
06:38:07.410508 ioctl(7, 0xc0186201, 0x102ffda8) = 0
06:38:07.433790 ioctl(7, 0xc0186201, 0x102ffef8) = 0
06:38:20.193223 ioctl(7, 0xc0186201, 0x102ffef8) = 0
06:38:20.194793 futex(0x14070, FUTEX_WAKE, 1) = 0
06:38:20.196186 munmap(0x10200000, 1048576) = 0
06:38:20.198087 exit(0)                 = ?

Thread 500 actually seems to do something useful. This seems to create yet another thread (502), and then seems to do something very similar to the main thread, sitting in a loop adding new services, and responding IPC messages. It is not clear why we need two different threads. Possibly there is a different thread for each possible client.

06:38:07.397343 syscall_983045(0x103fff80, 0, 0x20, 0, 0x103fff80, 0x143b8, 0xa9d25ef9, 0xf0005, 0x102ffd90, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x103fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x103fff80, 0xc12d0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
06:38:07.400080 getpid() = 479
06:38:07.403663 getuid32() = 0
06:38:07.407513 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:07.414539 mmap2(0x10400000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0)    = 0x10400000
06:38:07.439311 mprotect(0x10400000, 4096, PROT_NONE)    = 0
06:38:07.442042 clone(child_stack=0x104fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 502
06:38:07.444609 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(AudioFlinger, 0x14b40)\n\0", 51}], 3)              = 67
06:38:07.449432 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:07.452182 getpriority(PRIO_PROCESS, 0)    = 20
06:38:07.459755 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:07.473981 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.076657 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(power, 0x14830)\n\0", 44}], 3) = 60
06:38:08.078875 futex(0x134ac, FUTEX_WAKE, 2147483647) = 1
06:38:08.080710 futex(0x134a8, FUTEX_WAKE, 1) = 0
06:38:08.082566 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.084402 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.099967 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.165531 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.166646 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.167758 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.171184 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(activity, 0x14e88)\n\0", 47}], 3) = 63
06:38:08.173533 futex(0x134ac, FUTEX_WAKE, 2147483647) = 1
06:38:08.177313 futex(0x134a8, FUTEX_WAKE, 1) = 1
06:38:08.185288 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.186450 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.187727 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.206589 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(activity.receivers, 0x15028)\n\0", 57}], 3)    = 73
06:38:08.209646 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:08.210606 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.211485 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.219706 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.225865 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(activity.providers, 0x14e30)\n\0", 57}], 3)    = 73
06:38:08.228957 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:08.229950 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.230829 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.258759 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.049151 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(package, 0x15260)\n\0", 46}], 3) = 62
06:38:09.051523 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.052531 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.053477 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.054570 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.059698 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.060763 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.061861 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.083975 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.085589 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.086949 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.104476 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(content, 0x152a0)\n\0", 46}], 3) = 62
06:38:09.107125 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.108104 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.109013 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.118773 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.183888 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(battery, 0x15320)\n\0", 46}], 3) = 62
06:38:09.186582 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.187569 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.188464 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.225874 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.487155 getpriority(PRIO_PROCESS, 0) = 35
06:38:09.488238 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.489328 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.521681 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.522675 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.544021 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.601845 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth_service, 0x15150)\n\0", 66}], 3) = 82
06:38:09.604465 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.606401 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.607262 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.639998 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.803186 getpriority(PRIO_PROCESS, 0)    = 25
06:38:09.804803 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.810019 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.819942 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(statistics, 0x15888)\n\0", 49}], 3)    = 65
06:38:09.822767 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.823646 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.824478 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.865728 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.154474 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.157321 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.158537 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.163674 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.164666 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.165885 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.251413 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.252438 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.253512 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.274076 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.276875 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.278112 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.282103 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.283091 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.284126 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.318757 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.319829 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.320913 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.527902 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.529000 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.530108 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.553191 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.554366 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.557334 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.785261 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(android.telephony.PhoneNotifier, 0x15938)\n\0", 70}], 3) = 86
06:38:10.787969 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:10.788952 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.789867 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.790925 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.825487 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.826620 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.827751 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.864624 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(SMS, 0x15998)\n\0", 42}], 3) = 58
06:38:10.867192 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:10.868174 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.869088 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.870161 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.902914 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(android.telephony.SimPhoneBook, 0x154c0)\n\0", 69}], 3) = 85
06:38:10.905863 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:10.906910 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.908018 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.909213 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.964689 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.965983 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.967082 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.034374 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.038891 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.040226 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.067389 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.068514 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.078629 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.253134 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(volume, 0x15540)\n\0", 45}], 3) = 61
06:38:11.258928 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:11.260032 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.260964 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.270195 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.284493 getpriority(PRIO_PROCESS, 0)    = 20
06:38:11.288630 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.298964 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.432430 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth, 0x15cb8)\n\0", 58}], 3) = 74
06:38:11.434972 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:11.441544 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.445408 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.452511 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.460171 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.464099 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.478595 ioctl(7, 0xc0186201

This thread doesn't do much, it seems similar to the previous, where it is simply sitting waiting for IPC messages and adding services.

06:38:07.446832 syscall_983045(0x104fff80, 0, 0x20, 0, 0x104fff80, 0x140d8, 0xa9d25ef9, 0xf0005, 0x103ffd90, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x104fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x104fff80, 0xc13c0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
06:38:07.450234 getpid() = 479
06:38:07.452799 getuid32() = 0
06:38:07.471704 ioctl(7, 0xc0186201, 0x104ffef8) = 0
06:38:11.449551 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth_mailbox, 0x15db0)\n\0", 66}], 3)    = 82
06:38:11.453639 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:11.454617 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.460965 ioctl(7, 0xc0186201, 0x104ffda8) = 0
06:38:11.470727 ioctl(7, 0xc0186201

So there it is. A fairly detailed breakdown of what the runtime process does. Next up is to find out what zygote does. It looks like it is basically something like a CORBA ORB. It would definitely be nice to understand a lot more about the IPC.

Android Framework Startup

Sat, 17 Nov 2007 16:45:41 +0000
tech android article

In this post I want to try and find out more stuff about the Android software framework initialises. I previously looked a little bit at the boot process. Now I really want to get a better idea of what is actually happening with the system runtime.

The important processes in the system appear to be zygote (which is the app_process binary), and runtime. These are both started up by the init process. The following fragment of init.rc is relevant. It is important to note that these services have the autostart 1 attribute, indicating they are restarted if they quit.

    zygote {
        exec /system/bin/app_process
        args {
            0 -Xzygote
            1 /system/bin
            2 --zygote
        }
        autostart 1
    }
    runtime {
        exec /system/bin/runtime
        autostart 1
    }

If we go and kill either the zygote, or runtime process something interesting happens. Firstly the other process seems to kill itself, and then the whole system seems to restart.

Changing the ramdisk image

I want to get a bit more control of what things are starting up when. To do this I need to modify the init.rc file. To do this I first extracted the the ramdisk to the fileystem so that I can modify it (gnucpio -iz -F ramdisk.img).

After this I simply commented out the line from init.rc. Then we can recreate it: (gnucpio -i -t -F ../ramdisk.img | gnucpio -o -H newc -O ../rootfs.img).

Note if you are using Mac OS X, you will need to download and install gnucpio, becuase the built-in cpio doesn't support newc.

We can then use the emulator -ramdisk option to load our new ramdisk.

Manual startup

Now we can start things up manually. First we start up the zygote process. As # app_process -Xzygote /system/bin --zygote. When we do that there is no output either on the console, or on the LCD. Next we start the runtime process ( # runtime), and now things start to happen!

Once both processes start we get the cylon image, and we also end up with some console output:


 Prepping: /system/app/AlarmProvider.apk:/system/app/Browser.apk:/system/app/Ca
lendar.apk:/system/app/Camera.apk:/system/app/Contacts.apk:/system/app/Developm
ent.apk:/system/app/GDataFeedsProvider.apk:/system/app/Gmail.apk:/system/app/Gm
ailProvider.apk:/system/app/GoogleApps.apk:/system/app/GoogleAppsProvider.apk:/
system/app/Home.apk:/system/app/ImProvider.apk:/system/app/Maps.apk:/system/app
/MediaPickerActivity.apk:/system/app/MediaProvider.apk:/system/app/Phone.apk:/s
ystem/app/PimProvider.apk:/system/app/ApiDemos.apk:/system/app/SettingsProvider
.apk:/system/app/Sms.apk:/system/app/SyncProvider.apk:/system/app/TelephonyProv
ider.apk:/system/app/XmppService.apk:/system/app/YouTube.apk

File not found: /system/app/AlarmProvider.apk
File not found: /system/app/Calendar.apk
File not found: /system/app/Camera.apk
File not found: /system/app/GDataFeedsProvider.apk
File not found: /system/app/Gmail.apk
File not found: /system/app/GmailProvider.apk
File not found: /system/app/MediaPickerActivity.apk
File not found: /system/app/PimProvider.apk
File not found: /system/app/ApiDemos.apk
File not found: /system/app/Sms.apk
File not found: /system/app/SyncProvider.apk
File not found: /system/app/YouTube.apk
 Prep complete

It might give some clue of which Google applications will be available in the future. :)

The output from runtime looks like this:

+++ post-zygote

There is also some interesting output from the kernel. In particular the binder_open log. We can look at these in more detail later.

binder_open(c086f300 c0832160) (pid 462) got c5cea000
binder_open(c086f300 c095ec40) (pid 475) got c4e8b000
binder_open(c086f300 c095ef40) (pid 476) got c46f8000
android_power: auto off timeout set to 604800 seconds
Descriptor2Node failed secondary: desc=4, max=32, node=c4e8c350, weak=0
binder_open(c086f300 c4008d60) (pid 513) got c473a000
binder_open(c086f300 c4008760) (pid 512) got c39fc000
binder_open(c086f300 c4008260) (pid 531) got c35e3000

That is about enough detail for this entry, next time I'll be using strace to try and get a bit more information about these processes.

Android package installation

Sat, 17 Nov 2007 14:23:34 +0000
tech android article

So, I'm intrigued as to what exactly happens when you install a package. The way to work this out is get a look at the /data filesystem before package installation, then again afterwards, and do a big recursive diff

So lets get a relatively clean image:

  1. emulator -wipe-data
  2. adb push busybox ./
  3. adb shell ./busybox tar c -f /tmp/data.tar /data
  4. adb pull /tmp/data.tar .
  5. mkdir original
  6. cd original
  7. tar xf ../data.tar

Now that we have the clean image. Lets compile the simple Hello World sample.

  1. activityCreator.py --out HelloAndroid com.google.android.hello.HelloAndroid
  2. cd HelloAndroid
  3. ant

Now we have HelloAndroid.apk. So we should install this, and then we can find the diff of what happened.

  1. adb install HelloAndroid/bin/HelloAndroid.apk
  2. adb shell ./busybox tar c -f /tmp/data.tar /data
  3. adb pull /tmp/data.tar .
  4. mkdir after_install
  5. cd after_install
  6. tar xf ../data.tar

When we diff this we find not much as happened. There is the new HelloAndroid.apk file installed in the data/app directory. There is also a new com.google.android.hello in data/data, but it is empty, so not that interesting.

Only in after_install/data/app: HelloAndroid.apk
Only in after_install/data/data: com.google.android.hello
diff -ur original/data/system/packages.xml after_install/data/system/packages.xml
--- original/data/system/packages.xml   2007-11-17 14:27:17.000000000 +1100
+++ after_install/data/system/packages.xml      2007-11-17 14:34:20.000000000 +1100
@@ -5,6 +5,7 @@
   <package name="com.google.android.home" userId="10004" />
   <package name="com.google.android.fallback" userId="10003" />
   <package name="com.google.android.contacts" userId="10001" />
+  <package name="com.google.android.hello" userId="10006" />
   <shared-user name="android.uid.system" dummy-signature="android.signature.system" userId="1000" />
   <shared-user name="android.uid.phone" dummy-signature="android.signature.system" userId="1001" />
   <shared-user name="com.google.android.core" dummy-signature="com.google" userId="10002" />

This is pretty interesting to me. I'd really like to know how it finds the thing in the menu. As an experiment I'm going to edit the packages.xml file to see if this removes it from the menu. adb push original/data/system/packages.xml /data/system/packages.xml does this easily. Changing this doesn't update the menu immediately. On reboot it seems that the packages file is regenerated. Not much luck here, time to get a better idea of what is in the .apk file.

So, it turns out the .apk file isn't that difficult. file HelloAndroid.apk, tells us it is just a zip file. After extracting the zip file, we see just 4 files:

The XML files would presumably the most obvious, but they don't seem to be textual. file suggests it is a DBase 3 file, but that doesn't seem so likely. My guess is some kind of either unicode, or binary XML format, or actually both. I can't actually work it out to be perfectly honest! So, some reverse engineering required. The strings definitely look like UTF-16. hexdump helps to debug what is going on. The first part of the file looks like:

$ cat AndroidManifest.xml | hexdump -C
00000000  03 00 08 00 dc 05 00 00  01 00 1c 00 88 02 00 00  |................|
00000010  13 00 00 00 00 00 00 00  01 00 00 00 68 00 00 00  |............h...|
00000020  00 00 00 00 6a 00 00 00  c8 00 00 00 0a 01 00 00  |....j...........|
00000030  36 01 00 00 70 01 00 00  e8 00 00 00 00 00 00 00  |6...p...........|
00000040  8e 01 00 00 da 01 00 00  ce 00 00 00 c6 01 00 00  |................|
00000050  fc 00 00 00 94 00 00 00  12 00 00 00 52 01 00 00  |............R...|
00000060  28 01 00 00 6e 00 00 00  82 00 00 00 80 01 00 00  |(...n...........|
00000070  07 00 61 00 6e 00 64 00  72 00 6f 00 69 00 64 00  |..a.n.d.r.o.i.d.|
00000080  00 00 2a 00 68 00 74 00  74 00 70 00 3a 00 2f 00  |..*.h.t.t.p.:./.|
00000090  2f 00 73 00 63 00 68 00  65 00 6d 00 61 00 73 00  |/.s.c.h.e.m.a.s.|
000000a0  2e 00 61 00 6e 00 64 00  72 00 6f 00 69 00 64 00  |..a.n.d.r.o.i.d.|
000000b0  2e 00 63 00 6f 00 6d 00  2f 00 61 00 70 00 6b 00  |..c.o.m./.a.p.k.|
000000c0  2f 00 72 00 65 00 73 00  2f 00 61 00 6e 00 64 00  |/.r.e.s./.a.n.d.|
000000d0  72 00 6f 00 69 00 64 00  00 00 00 00 00 00 08 00  |r.o.i.d.........|
000000e0  6d 00 61 00 6e 00 69 00  66 00 65 00 73 00 74 00  |m.a.n.i.f.e.s.t.|
000000f0  00 00 07 00 70 00 61 00  63 00 6b 00 61 00 67 00  |....p.a.c.k.a.g.|
00000100  65 00 00 00 18 00 63 00  6f 00 6d 00 2e 00 67 00  |e.....c.o.m...g.|
00000110  6f 00 6f 00 67 00 6c 00  65 00 2e 00 61 00 6e 00  |o.o.g.l.e...a.n.|
00000120  64 00 72 00 6f 00 69 00  64 00 2e 00 68 00 65 00  |d.r.o.i.d...h.e.|
00000130  6c 00 6c 00 6f 00 00 00  01 00 20 00 00 00 0b 00  |l.l.o..... .....|
00000140  61 00 70 00 70 00 6c 00  69 00 63 00 61 00 74 00  |a.p.p.l.i.c.a.t.|
00000150  69 00 6f 00 6e 00 00 00  08 00 61 00 63 00 74 00  |i.o.n.....a.c.t.|
00000160  69 00 76 00 69 00 74 00  79 00 00 00 05 00 63 00  |i.v.i.t.y.....c.|
00000170  6c 00 61 00 73 00 73 00  00 00 0d 00 2e 00 48 00  |l.a.s.s.......H.|
00000180  65 00 6c 00 6c 00 6f 00  41 00 6e 00 64 00 72 00  |e.l.l.o.A.n.d.r.|
00000190  6f 00 69 00 64 00 00 00  05 00 6c 00 61 00 62 00  |o.i.d.....l.a.b.|
000001a0  65 00 6c 00 00 00 0c 00  48 00 65 00 6c 00 6c 00  |e.l.....H.e.l.l.|
000001b0  6f 00 41 00 6e 00 64 00  72 00 6f 00 69 00 64 00  |o.A.n.d.r.o.i.d.|
000001c0  00 00 0d 00 69 00 6e 00  74 00 65 00 6e 00 74 00  |....i.n.t.e.n.t.|
000001d0  2d 00 66 00 69 00 6c 00  74 00 65 00 72 00 00 00  |-.f.i.l.t.e.r...|
000001e0  06 00 61 00 63 00 74 00  69 00 6f 00 6e 00 00 00  |..a.c.t.i.o.n...|
000001f0  05 00 76 00 61 00 6c 00  75 00 65 00 00 00 1a 00  |..v.a.l.u.e.....|
00000200  61 00 6e 00 64 00 72 00  6f 00 69 00 64 00 2e 00  |a.n.d.r.o.i.d...|
00000210  69 00 6e 00 74 00 65 00  6e 00 74 00 2e 00 61 00  |i.n.t.e.n.t...a.|
00000220  63 00 74 00 69 00 6f 00  6e 00 2e 00 4d 00 41 00  |c.t.i.o.n...M.A.|
00000230  49 00 4e 00 00 00 08 00  63 00 61 00 74 00 65 00  |I.N.....c.a.t.e.|
00000240  67 00 6f 00 72 00 79 00  00 00 20 00 61 00 6e 00  |g.o.r.y... .a.n.|
00000250  64 00 72 00 6f 00 69 00  64 00 2e 00 69 00 6e 00  |d.r.o.i.d...i.n.|
00000260  74 00 65 00 6e 00 74 00  2e 00 63 00 61 00 74 00  |t.e.n.t...c.a.t.|
00000270  65 00 67 00 6f 00 72 00  79 00 2e 00 4c 00 41 00  |e.g.o.r.y...L.A.|
00000280  55 00 4e 00 43 00 48 00  45 00 52 00 00 00 00 00  |U.N.C.H.E.R.....|
00000290  80 01 08 00 54 00 00 00  00 00 00 00 00 00 00 00  |....T...........|

Well, this hasn't exactly been very informative. We've learned that basically all that happens on install is the .apk file is copied to /data/app. We also learned that this directory is scanned on startup to find packages to start. The strings in there are:

We can note that there aren't any duplicated strings, and updating the AndroidManifest.xml source file and regenerating it confirms this.

So looking at the WbXML looks likely. The first byte is '3', which indicates WbXML version 1.3. The next byte is '0', which indicates that public identifier is described by a string, which string index number '8'. The charset is indicated by '0', which means unknown. Next is the string table, and it looks bytes 'dc' and '05', which, if I've decode it right, indicates 11781 bytes. Which makes no sense at all. So we try and guess something else. As a 32-bit little-endian integer 0x5dc is exactly the length of the file. So that seems like a good guess for what that field is. In this case, I'm going to guess that '03 00 08 00', is a magic number to identify the file. The next 4 bytes are '01 00 1c 00', not sure hat this is it is followed by '88 02 00 00', which happen to be 0x288 little endian, which seem to represent the last character in what looks like a string table. Looking at other different binary XML files seems to confirm this hypothesis. The next field appears to be 0x13, which is pretty close to the number of strings we found earlier. And this is about as far as I can be bothered working out right now.

The use of the binary encoding seems a little strange. It certainly doesn't reduce the size, but maybe that isn't really the point. It is probably the case that this makes it a lot easier to parse, but you would expect there to be existing XML parsers in the libraries. So beats me what is going on!

The classes.dex file include in the .apk file is already documented by Retrodev.

The final file is the resources.arsc file. This doesn't seem to have too much information in it. There are a few strings that we would expect including, res/layout/main.xml, HelloAndroid and com.google.android.hello.

And that is about it for now. Not so useful at the end of the day, but it maybe give you some information as starting point for further reverse engineering.

Busybox for android

Wed, 14 Nov 2007 10:46:19 +0000
tech android

The toolbox that is provided on the Android environment is pretty limited. I wanted something more useful and familiar. Busybox to the rescue!

I have a busybox binary available for those who are interested.

To use it simply do # mkdir /data/busybox, on your emulated console. Then copy the busybox binary across: $ adb push busybox /data/busybox/busybox. Once you do this you can install the necessary hardlinks by doing: # cd /data/busybox; ./busybox --install. Once doing this you should have links to all the applets in the /data/busybox directory. To make this useful you probably want to put this into your PATH: # export PATH=/data/busybox:$PATH.

You should now be able to get access to all the useful busybox applets.

Update:busybox was a straight compile of the standard busybox 1.8.1 release. No changes were made to the source. Source code available here or from the Busybox website.

Android filesystem images

Wed, 14 Nov 2007 10:02:59 +0000
tech android article

For those of you hacking on Android, you might find it interesting to have the actual files extracted so that you can play with them. Here are the tarballs of the images I extracted from my running system. system.tar.gz data.tar.gz. Note these were extracted from a running system, they will have some cruft on them.

Native C applications for Android

Tue, 13 Nov 2007 14:32:35 +0000
tech android article

The Android Java SDK is nice and all, but what if you want to run some C service or code? Well, it turns out that this isn't exactly difficult. You can compile an application using a standard Linux cross-compiler, install it and run your programs from the shell.

You will need a cross-compiler (make sure you get the ARM GNU/Linux target). Then you can just create your program, compile, and upload to the device:

$ arm-none-linux-gnueabi-gcc -static hello.c -o hello
$ adb push hello/hello data/hello

You can then simply run you application:

$ adb shell data/hello           
Hello, Android!

Now of course, this doesn't at all explain how to tie into the graphics or the rest of the system, but that is left as an exercise for the reader ;)

Update: My instructions were missing the essential -static part!

Android - under the hood

Tue, 13 Nov 2007 14:20:09 +0000
tech android article

I'm really interested in finding out what is happening underneath the sheets on the Android platform. The first step to this is getting access to the Android shell. There are two options here, you can either start the emulator with a console option: $ emulator console, or connect to the console after book using: $ adb shell. A convention that I use through this document is that thing to type into you host shell use the '$' prompt, and things to type into the Android console use '#'.

Apology: This information isn't particularly well structured, and has mostly been written as a log of my initial explorations. At some time in the future I might actually order this in a sane way.

Executables

So our toolbox of useful POSIX commands is a little more limited than usual, but the basics of ls, ps, cat, echo are there. So we start by seeing what programs we can run. First we do an # echo $PATH, which shows we can expect useful programs in /sbin, /system/sbin and /system/bin/. So lets look a little closer. # ls /sbin, shows us:

-rwxr-xr-x root     root       226940 1970-01-01 00:00 recovery
-rwxr-xr-x root     root       102864 1970-01-01 00:00 adbd

recovery seems to be some kind of recovery tool for when you are screwed things up a bit too much. Running recovery from the build directory seems to actual output text on the LCD screen, specifically: W: Can't open version file "/etc/build.prop": No such file or directory. There isn't a build.prop file in /etc, but there is one in /system, it would be interesting to see what happens when recovery does find a file. Unfortunately, we don't actually have cp, but we can use adb, to do our dirty work: $ adb pull /system/build.prop .; adb push build.prop /etc. Running recovery now doesn't actually do anything very useful, so much for that. I presume that it is meant to be run by some process on the host, so we'll ignore this avenue of enquiry for now.

adbd, seems pretty obvious, it looks like the daemon that adb talks to. If we run # ps, we can see that the adbd process is running.

It turns out there is no /system/sbin directory, so we are left looking at what is in /system/bin, which seems to be where the bulk of the files are, around 100 executables.

-rwxr-xr-x root     root         4280 2007-11-11 20:57 AudioHardwareRecord
-rwxr-xr-x root     root         4152 2007-11-11 20:57 AudioInRecord
-rwxr-xr-x root     root         4672 2007-11-11 20:57 RecursiveMutexTest
-rwxr-xr-x root     root        20932 2007-11-11 20:57 SRecTest
-rwxr-xr-x root     root        20408 2007-11-11 20:57 SRecTestAudio
-rwxr-xr-x root     root         4860 2007-11-11 20:57 UAPI_PortabilityTest
-rwxr-xr-x root     root        12420 2007-11-11 20:57 UAPI_SrecTest
-rwxr-xr-x root     root        29560 2007-11-11 20:57 UAPI_test
-rwxr-xr-x root     root          196 2007-11-11 20:46 am
-rwxr-xr-x root     root         5124 2007-11-11 20:57 app_process
lrwxr-xr-x root     root              2007-11-11 20:57 cat -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 chmod -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 cmp -> toolbox
-rwxr-xr-x root     root         3912 2007-11-11 20:57 crasher
-rwxr-xr-x root     root         4892 2007-11-11 20:57 dalvikvm
lrwxr-xr-x root     root              2007-11-11 20:57 date -> toolbox
-rwxr-xr-x root     root       118096 2007-11-11 20:57 dbus-daemon
lrwxr-xr-x root     root              2007-11-11 20:57 dd -> toolbox
-rwxr-xr-x root     root         9264 2007-11-11 20:57 debuggerd
-rwxr-xr-x root     root        17416 2007-11-11 20:57 dexdump
-rwxr-xr-x root     root         3944 2007-11-11 20:57 dexopt
lrwxr-xr-x root     root              2007-11-11 20:57 df -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 dmesg -> toolbox
-rwxr-xr-x root     root        73748 2007-11-11 20:57 drm1_unit_test
-rwxr-xr-x root     root        85608 2007-11-11 20:57 drm2_unit_test
-rwxr-xr-x root     root         1240 2007-11-11 20:46 dumpstate
-rwxr-xr-x root     root         6808 2007-11-11 20:57 dumpsys
lrwxr-xr-x root     root              2007-11-11 20:57 exists -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 getevent -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 getprop -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 hd -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 ifconfig -> toolbox
-rwxr-xr-x root     root          208 2007-11-11 20:46 input
lrwxr-xr-x root     root              2007-11-11 20:57 insmod -> toolbox
-rwxr-xr-x root     root         6696 2007-11-11 20:57 install_boot_image
lrwxr-xr-x root     root              2007-11-11 20:57 ioctl -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 kill -> toolbox
-rwxr-xr-x root     root        81236 2007-11-11 20:57 linker
lrwxr-xr-x root     root              2007-11-11 20:57 ln -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 log -> toolbox
-rwxr-xr-x root     root         8772 2007-11-11 20:57 logcat
lrwxr-xr-x root     root              2007-11-11 20:57 ls -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 lsmod -> toolbox
-rwxr-xr-x root     root         7596 2007-11-11 20:57 mem_profiler
lrwxr-xr-x root     root              2007-11-11 20:57 mkdir -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 mkdosfs -> toolbox
-rwxr-xr-x root     root          212 2007-11-11 20:46 monkey
lrwxr-xr-x root     root              2007-11-11 20:57 mount -> toolbox
-rwxr-xr-x root     root         2888 2007-11-11 20:57 mtptest
-rwxr-xr-x root     root         8640 2007-11-11 20:57 netcfg
lrwxr-xr-x root     root              2007-11-11 20:57 netstat -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 notify -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 ping -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 powerd -> toolbox
-rwxr-xr-x root     root       144356 2007-11-11 20:57 pppd
lrwxr-xr-x root     root              2007-11-11 20:57 printenv -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 ps -> toolbox
-rwxr-xr-x root     root         8724 2007-11-11 20:58 pv
lrwxr-xr-x root     root              2007-11-11 20:57 r -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 readtty -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 reboot -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 renice -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 resetradio -> toolbox
-rwxr-xr-x root     root         4116 2007-11-11 20:57 rild
lrwxr-xr-x root     root              2007-11-11 20:57 rm -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 rmdir -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 rmmod -> toolbox
-rwxr-xr-x root     root         1027 2007-11-11 20:47 ro.xml
-rwxr-xr-x root     root         1782 2007-11-11 20:47 ro2.xml
-rwxr-xr-x root     root           98 2007-11-11 20:47 roerror.xml
lrwxr-xr-x root     root              2007-11-11 20:57 rotatefb -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 route -> toolbox
-rwxr-xr-x root     root        45484 2007-11-11 20:57 runtime
-rwxr-xr-x root     root         4572 2007-11-11 20:57 sdutil
lrwxr-xr-x root     root              2007-11-11 20:57 sendevent -> toolbox
-rwxr-xr-x root     root         6148 2007-11-11 20:57 service
lrwxr-xr-x root     root              2007-11-11 20:57 setconsole -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 setkey -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 setprop -> toolbox
-rwxr-xr-x root     root        90344 2007-11-11 20:57 sh
-rwxr-xr-x root     root         5116 2007-11-11 20:57 showmap
-rwxr-xr-x root     root         7524 2007-11-11 20:57 showslab
lrwxr-xr-x root     root              2007-11-11 20:57 sleep -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 smd -> toolbox
-rwxr-xr-x root     root        25944 2007-11-11 20:57 sqlite3
-rwxr-xr-x root     root          411 2007-11-11 20:47 ssltest
lrwxr-xr-x root     root              2007-11-11 20:57 start -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 stop -> toolbox
-rwsr-sr-x root     root        72436 2007-11-11 20:57 su
lrwxr-xr-x root     root              2007-11-11 20:57 sync -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 syren -> toolbox
-rwxr-xr-x root     root         2772 2007-11-11 20:57 system_server
-rwxr-xr-x root     root        88052 2007-11-11 20:57 toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 umount -> toolbox
-rwxr-xr-x root     root        16612 2007-11-11 20:57 usbd
lrwxr-xr-x root     root              2007-11-11 20:57 watchprops -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 webgrab -> toolbox
lrwxr-xr-x root     root              2007-11-11 20:57 wipe -> toolbox

Filesystem

We can use # mount to get an idea of what is happening.

rootfs / rootfs rw 0 0
/dev/pts /dev/pts devpts rw 0 0
/proc /proc proc rw 0 0
/sys /sys sysfs rw 0 0
/dev/block/mtdblock0 /system yaffs2 rw,nodev,noatime,nodiratime 0 0
/dev/block/mtdblock1 /data yaffs2 rw,nodev,noatime,nodiratime 0 0

Root file system

The root filesystem is using rootfs, which basically means it is whatever what in the initial-ramfs. It is interesting, because there is no tmpfs, which means an application writing to /tmp, could exhaust system memory. This doesn't seem like a good denial-of-service to leave open. Of course the root-filesystem is no good for storing files, since it has no backing store.

The initial-ramfs is loaded by the emulator from $SDKROOT/tools/lib/images/ramdisk.img. This is simply a gzipped, CPIO archive. So, if you wanted to modify it, for some reason, it wouldn't be too hard to do. Inspecting it there isn't too much interesting there though, most of the interesting stuff is on the /system and /data partitions.

System file system

/system is a yaffs2 filesystem. It is using the first nand chip as its backing store. # df shows us that there is a 64MB backing store, and it is about half used. This directory is where the core of the Android system is stored. It seems as though a normal user shouldn't ever need to update it, but I'm sure over-the-air updates would be used on this file system.

In the emulator the system image is located at $SDKROOT/tools/lib/image/system.img, although you should be able to change that by using the -image parameter when running the emulator. The emulator does not write-back changes to the system file-system, so it will be fresh, each time the emulator is loaded.

Data file system

The /data file system is very similar to /system; it is backed by 64MB nand, and is using the yaffs2. The is the filesystem on which new user applications are installed and run from. Changes made to the /data filesystem are honoured by the emulator, the updated nand image is stored in ~/.android/userdata.img.

Processes

To get an idea of what is actually executing on the phone we use trusty # ps. Which gives us:

USER     PID   PPID  VSIZE RSS   WCHAN    PC         NAME
root     1     0     252   164   c0082240 0000ab0c S /init
root     2     0     0     0     c0048eac 00000000 S kthreadd
root     3     2     0     0     c003acf0 00000000 S ksoftirqd/0
root     4     2     0     0     c0045e5c 00000000 S events/0
root     5     2     0     0     c0045e5c 00000000 S khelper
root     8     2     0     0     c0045e5c 00000000 S suspend/0
root     33    2     0     0     c0045e5c 00000000 S kblockd/0
root     36    2     0     0     c0045e5c 00000000 S cqueue/0
root     38    2     0     0     c0150c44 00000000 S kseriod
root     75    2     0     0     c005bed0 00000000 S pdflush
root     76    2     0     0     c005bed0 00000000 S pdflush
root     77    2     0     0     c005f880 00000000 S kswapd0
root     78    2     0     0     c0045e5c 00000000 S aio/0
root     201   2     0     0     c014e2f4 00000000 S mtdblockd
root     217   2     0     0     c0045e5c 00000000 S kmmcd
root     231   2     0     0     c0045e5c 00000000 S rpciod/0
root     450   1     720   288   c00386a4 afe092ac S /system/bin/sh
root     451   1     3316  128   ffffffff 0000ceb4 S /sbin/adbd
root     452   1     2816  284   ffffffff afe08b9c S /system/bin/usbd
root     453   1     636   216   c017c114 afe08e9c S /system/bin/debuggerd
root     454   1     12576 584   ffffffff afe08b9c S /system/bin/rild
root     455   1     56572 14608 c01dc388 afe083dc S zygote
root     456   1     20576 2076  ffffffff afe0861c S /system/bin/runtime
bluetooth 458   1     1200  760   c0082240 afe0947c S /system/bin/dbus-daemon
root     467   455   93632 17284 ffffffff afe0861c S system_server
app_4    505   455   76104 13632 ffffffff afe09604 S com.google.android.home
phone    509   455   75876 14952 ffffffff afe09604 S com.google.android.phone
app_2    523   455   74528 14332 ffffffff afe09604 S com.google.process.content
root     538   450   932   312   00000000 afe083dc R ps

Unfortunately we don't have pstree, so we don't get a nice hierarchal display, but things aren't very complicated so we can work it out fairly easily. First we can prune off the kernel threads, and we get a more manageable set of things left. If we then look at those processes directly parented by init, we are left with:

USER     PID   PPID  VSIZE RSS   WCHAN    PC         NAME
root     450   1     720   288   c00386a4 afe092ac S /system/bin/sh
root     451   1     3316  128   ffffffff 0000ceb4 S /sbin/adbd
root     452   1     2816  284   ffffffff afe08b9c S /system/bin/usbd
root     453   1     636   216   c017c114 afe08e9c S /system/bin/debuggerd
root     454   1     12576 584   ffffffff afe08b9c S /system/bin/rild
root     455   1     56572 14608 c01dc388 afe083dc S zygote
root     456   1     20576 2076  ffffffff afe0861c S /system/bin/runtime
bluetooth 458   1     1200  760   c0082240 afe0947c S /system/bin/dbus-daemon

/system/bin/sh, is clearly the shell we are using. We have already explored adbd earlier. usbd, is an interesting. It seems that most of the interaction between the client debugger happens over the USB port, and is handled by the USB daemon. There is a usbd.conf file in /etc. I'm not quite sure how this is working though, as there does not appear to be any USB devices in the system (# cat /proc/devices). debuggerd, is presumably the client side of the system debugger, although I can't find out more details than that. rild is the radio-interface-link daemon (I think! ;). The init files imply that you can connect a real-modem and get Android to us it, but by default it has the simulated Java radio-interface. zygote appears to get the launcher for the Java runtime, it isn't an actual program, it looks like app_process, masquerading as zygote.

Startup

User-land starts with the init program. This doesn't appear to be a standard init by any shot. At startup init parses /etc/init.rc, which has the overall format of:

## Global environment setup
## 
env {
    PATH /sbin:/system/sbin:/system/bin
    LD_LIBRARY_PATH /system/lib
    ANDROID_BOOTLOGO 1
    ANDROID_ROOT /system
    ANDROID_ASSETS /system/app
    ANDROID_DATA /EXTERNAL
    data_STORAGE /sdcard
    DRM_CONTENT /data/drm/content
}

## Setup and initialization we need to do on boot
##
onboot {
    setkey 0x0 0xe5 0x706
    ....

    # bring up loopback
    ifup lo
    ...
}

## Processes which are run once (and only once) at system boot.
## Init will wait until each of these completes before starting
## the next one.
##
startup {
    usbd-config {
        exec /system/bin/usbd
        args {
           0 -c
        }
    }
    qemu-init {
        exec /etc/qemu-init.sh
    }
}

## Processes which init runs and will re-run upon exit,
## until it is told to stop running them.  These will not be
## run until all the processes in startup have been run
##
service {
    console {
        exec /system/bin/sh
        console yes
    }
    adbd {
        exec /sbin/adbd
    }
    ......
}

I've no idea of a general purpose init that uses that file format, but really, I don't know that much. It looks custom, although seems to have familiar constructs to something like Apple's launchd. strings doesn't provide much more information, but it seems someone was frustrated enough during development to see the need for the string Stupid C library hack !!.

Conclusion

Well, that is a bit of an overview. I'm sure there will be more to come as I work out more stuff.

OKL4 wins iAward

Thu, 31 May 2007 17:16:20 +0000
tech okl4

Last night Open Kernel Labs won an iAward in the Application and Infrastructure Tools category.

It is quite cool that I can now say our microkernel is now award winning! I think a lot of this is really due the awesome engineers working here.

rsync backup

Wed, 30 May 2007 20:01:45 +0000
tech rsync article

Running with the explain things for Benno 5 months from now, here is how I'm currently making my backups work. The basic thing I want is incremental backups onto an external hard drive.

rsync makes this really easy to do. The latest version of rsync has --link-dest, which will hardlink to files in an existing directory tree, rather than creating a new file. This makes each incremental reasonably cheap, as most files are just hard links back to the previous backup.

So basically I do: sudo rsync -a --link-dest=../backups.0/ /Users/ backups.1/. Note that the trailing slash is actually important here.

A big gotcha is ensuring that the external disk is actually mounted with permissions enabled, or else the hard-links won't actual be made.

After this everything works nicely, when combined with a simple script:

#!/usr/bin/env python
"""
Use rsync to backup my /Users directory onto external hard drive
backup.
"""
import os

def get_latest():
    backups = [int(x.split(".")[-1]) for x in os.listdir(".") if x.startswith("backup")]
    backups.sort()
    return backups[-1]

os.chdir("/Volumes/backup2007")
latest = get_latest()
next = latest + 1
command = "sudo rsync -a --link-dest=../backups.%d/ /Users/ backups.%d/" % (latest, next)
os.system(command)

Currently I don't do any encryption, which might be useful in the future.