Android

From ArmadeusWiki
Revision as of 14:21, 24 September 2009 by NicolasD (Talk | contribs) (Android with touchscreen)

Jump to: navigation, search

Android-logo.png

This page will sumarize the efforts made to have Android running on the APF boards.

Note Note: Due to Android requirements (at least an ARM926 core), it is impossible to have it running on the APF9328


Some readings before starting

Prerequisites for Android installation

Install needed software packages

  • Install these package for build the kernel image and for format the mmc/µSD card
$ sudo apt-get install uboot-mkimage mtd-utils
  • The Armadeus Toolchain, only for Armadeus patches

Update the environment variables

Theses environment variables install the Android and Armadeus folder in our home directory, but of course, it can be placed anywhere!

export ANDROID_SOURCE=~/apf27droid
export ANDROID_SDK=~/android-sdk-linux_x86-1.5_r3
export ARMADEUS=~/armadeus-3.1
export PATH=${PATH}:${ANDROID_SDK}/tools:${ANDROID_SOURCE}/bin

Download Android source

The getting Android source document describes how to set up our local work environment. Follow theses instructions until Installing Repo chapter.

$ mkdir $ANDROID_SOURCE
$ cd $ANDROID_SOURCE
$ mkdir bin
$ curl http://android.git.kernel.org/repo >$ANDROID_SOURCE/bin/repo
$ chmod a+x $ANDROID_SOURCE/bin/repo
$ repo init -u git://android.git.kernel.org/platform/manifest.git -b android-sdk-1.5_r3
$ repo sync

Since android-sdk-1.5_r3 branch, the Linux kernel isn't with the Android source, We can download it in a compress archive (tar.gz) file with this android-kernel-2.6.29 (about (70Mib) or with git repository (more 300Mib)

$ mkdir $ANDROID_SOURCE/kernel
$ cd $ANDROID_SOURCE/kernel
$ git clone git://android.git.kernel.org/kernel/common.git android-2.6.29

Linux Kernel 2.6.29

Apply the Armadeus patchset

Before compiling the kernel, we patch the source with the Armadeus patches. In second time, I will give the URL to retrieve Linux 2.6.29.4 patch.

$ $ARMADEUS/buildroot/toolchain/patch-kernel.sh $ANDROID_SOURCE/kernel $ARMADEUS/downloads patch-2.6.29.4.bz2
$ $ARMADEUS/buildroot/toolchain/patch-kernel.sh $ANDROID_SOURCE/kernel $ARMADEUS/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29.4 \*.patch{,.gz,.bz2}
$ mkdir $ANDROID_SOURCE/kernel/drivers/armadeus
$ cp -r $ARMADEUS/target/linux/modules/* $ANDROID_SOURCE/kernel/drivers/armadeus

Android kernel configuration

$ cp $ARMADEUS/buildroot/target/device/armadeus/apf27/apf27-linux-2.6.29.config $ANDROID_SOURCE/kernel/arch/arm/configs/apf27_android_defconfig
$ cd $ANDROID_SOURCE/kernel
$ make ARCH=arm mrproper
$ make ARCH=arm apf27_android_defconfig
$ make ARCH=arm menuconfig

Make sure your kernel boots normally on your board. Then enable some Android specific configuration and make sure that your kernel still boots (with your standard file system).

  • Android pmem allocator
Device Drivers  --->
    [*] Misc devices  --->
        [*]   Android pmem allocator
  • Android drivers
Device Drivers  --->
    [*] Staging drivers  ---> 
        [ ] Exclude Staging drivers from being built (NEW)
        ...
        Android  --->
            [*] Android Drivers
            [*] Android Binder IPC Driver
            <*> Android log driver  
            [ ] Android RAM buffer console
            [*] Timed output class driver (NEW)
            < >   Android timed gpio driver (NEW)
            [*] Android Low Memory Killer 
  • Anonymous Shared Memory Subsystem
General setup  --->
    [*] Enable the Anonymous Shared Memory Subsystem

UBIFS kernel configuration

Device Drivers  --->
    <*> Memory Technology Device (MTD) support  --->
        UBI - Unsorted block images  ---> 
            <*> Enable UBI
            (4096) UBI wear-leveling threshold 
            (1)   Percentage of reserved eraseblocks for bad eraseblocks handling
            [ ]   Emulate MTD devices
                  *** UBI debugging options ***
            [ ]   UBI debugging    
...
File systems  --->
    [*] Miscellaneous filesystems  --->
        <*>   UBIFS file system support
        [ ]     Extended attributes support 
        [ ]     Advanced compression options
        [ ]     Enable debugging

Touchscreen kernel configuration

Power management options  --->
    [*] Wake lock
    [*]   Wake lock stats (NEW)
    [*]   Userspace wake locks (NEW)
    [*]   Early suspend (NEW) 
        User-space screen access (Console switch on early-suspend)  --->
            (X) Sysfs interface 
...
Device Drivers  --->
    Input device support  --->
        [*]   Touchscreens  --->
            <*>   TSC 2102 based touchscreens
    ...
    <*> Hardware Monitoring support  --->

Build Android kernel

$ make ARCH=arm CROSS_COMPILE=$ANDROID_SOURCE/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi- uImage
$ cp $ANDROID_SOURCE/kernel/arch/arm/boot/uImage $TFTPBOOT/apf27-linux.bin

Android

Battery patch

At the beginning, reboot happened over again even though Android logo appeared on board. Result of investigation, we found that battery power was returned with 0 when boot.. Then, we changed to notify full battery to Android by ignoring the information under /sys/class/power_supply so that to prevent the power down by low battery $ANDROID_SOURCE/frameworks/base/services/jni/com_android_server_BatteryService.cpp

  • Change the battery service status as TRUE
static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
   const int SIZE = 16;
   char buf[SIZE];

   jboolean value = true; /* change false -> true */
   /*!!!comment out!!!
   if (readFromFile(path, buf, SIZE) > 0) {
      if (buf[0] == '1') {
         value = true;
      }
   }
   */
   env->SetBooleanField(obj, fieldID, value);
}
  • Change the volume, voltage and temperature of battery. Return 100%.
static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
   const int SIZE = 128;
   char buf[SIZE];

   jint value = 100; /* change 0 -> 100 */
   /*!!!comment out!!!
   if (readFromFile(path, buf, SIZE) > 0) {
      value = atoi(buf);
   }
   */
   env->SetIntField(obj, fieldID, value);
}
  • Change the battery charged status as full and deterioration status as fair.
static void android_server_BatteryService_update(JNIEnv* env, jobject obj)
{
   setBooleanField(env, obj, AC_ONLINE_PATH, gFieldIds.mAcOnline);
   setBooleanField(env, obj, USB_ONLINE_PATH, gFieldIds.mUsbOnline);
   setBooleanField(env, obj, BATTERY_PRESENT_PATH, gFieldIds.mBatteryPresent);

   setIntField(env, obj, BATTERY_CAPACITY_PATH, gFieldIds.mBatteryLevel);
   setIntField(env, obj, BATTERY_VOLTAGE_PATH, gFieldIds.mBatteryVoltage);
   setIntField(env, obj, BATTERY_TEMPERATURE_PATH, gFieldIds.mBatteryTemperature);

   /* Change */
   env->SetIntField(obj, gFieldIds.mBatteryStatus, gConstants.statusFull);
   env->SetIntField(obj, gFieldIds.mBatteryHealth, gConstants.healthGood);
   env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF("1"));

   /*!!!comment out!!!
   const int SIZE = 128;
   char buf[SIZE];

   if (readFromFile(BATTERY_STATUS_PATH, buf, SIZE) > 0)
      env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));

   if (readFromFile(BATTERY_HEALTH_PATH, buf, SIZE) > 0)
      env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));

   if (readFromFile(BATTERY_TECHNOLOGY_PATH, buf, SIZE) > 0)
      env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf));
   */
}

Audio

Note Note: Still compilation error
cd $ANDROID_SOURCE/external 
git clone git://android.git.kernel.org/platform/external/alsa‐lib.git
git clone git://android.git.kernel.org/platform/external/alsa‐utils.git
cd $ANDROID_SOURCE/hardware 
git clone git://android.git.kernel.org/platform/hardware/alsa_sound.git  

edit $ANDROID_SOURCE/build/target/board/generic/BoardConfig.mk

 
# HAVE_HTC_AUDIO_DRIVER := true
# BOARD_USES_GENERIC_AUDIO := true
BOARD_USES_ALSA_AUDIO := true
BUILD_WITH_ALSA_UTILS := true

edit $ANDROID_SOURCE/hardware/alsa_sound/AudioHardwareALSA.cpp and change bufferSize to 8192 in twice StreamDefaults structures
edit $ANDROID_SOURCE/system/core/init/device.c

static struct perms_ devperms[] = {
    { "/dev/null",          0666,   AID_ROOT,       AID_ROOT,       0 },
...
    { "/dev/qmi1",          0640,   AID_RADIO,      AID_RADIO,      0 },
    { "/dev/qmi2",          0640,   AID_RADIO,      AID_RADIO,      0 },
    // Add this line
    { "/dev/snd/",          0664,   AID_SYSTEM,     AID_AUDIO,      1 },
    { NULL, 0, 0, 0, 0 },
};
static void handle_device_event(struct uevent *uevent)
{
    char devpath[96];
    char *base, *name;
    int block;
...
     } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
            base = "/dev/mtd/";
            mkdir(base, 0755);
        // add this conditionnal block
        } else if(!strncmp(uevent->subsystem, "sound", 5)) {
	    base = "/dev/snd/";
            mkdir(base, 0755);
        } else if(!strncmp(uevent->subsystem, "misc", 4) &&
                    !strncmp(name, "log_", 4)) {
            base = "/dev/log/";
            mkdir(base, 0755);
            name += 4;
        } else
            base = "/dev/";
    }

Audio files

$ mkdir $ANDROID_SOURCE/system/core/rootdir/media 
$ mkdir $ANDROID_SOURCE/system/core/rootdir/media/audio 
$ mkdir $ANDROID_SOURCE/system/core/rootdir/media/audio/ui
$ mkdir $ANDROID_SOURCE/system/core/rootdir/media/audio/alarms 
$ mkdir $ANDROID_SOURCE/system/core/rootdir/media/audio/notifications 
$ mkdir $ANDROID_SOURCE/system/core/rootdir/media/audio/ringtones 
$ cp $ANDROID_SOURCE/frameworks/base/data/sounds/effects/* $ANDROID_SOURCE/system/core/rootdir/media/audio/ui/ 
$ cp $ANDROID_SOURCE/frameworks/base/data/sounds/Alarm_* $ANDROID_SOURCE/system/core/rootdir/media/audio/alarms/
$ cp $ANDROID_SOURCE/frameworks/base/data/sounds/notifications/* $ANDROID_SOURCE/system/core/rootdir/media/audio/notifications/
$ cp $ANDROID_SOURCE/frameworks/base/data/sounds/Ring_* $ANDROID_SOURCE/system/core/rootdir/media/audio/ringtones/

Touchscreen

New definition:

#define DRIVER_NAME "TSC210x Touchscreen"
#define X_AXIS_MAX    4000
#define X_AXIS_MIN    0
#define Y_AXIS_MAX    4200
#define Y_AXIS_MIN    100
#define PRESSURE_MIN  20
#define PRESSURE_MAX  40000
#define FACTOR        5000
static void tsc210x_touch(int touching)
{
    if (!touching) {
        //input_report_abs(dev, ABS_X, 0);
        //input_report_abs(dev, ABS_Y, 0);
        input_report_key(dev, BTN_TOUCH, 0);
        input_report_abs(dev, ABS_PRESSURE, 0);
        input_sync(dev);
    }

    //input_report_key(dev, BTN_TOUCH, touching);
    //do_poke_blanked_console = 1;
}
static void tsc210x_coords(int x, int y, int z1, int z2)
{
    int p;

    /* Calculate the touch resistance a la equation #1 */
    if (z1 != 0)
        p = x * (z2 - z1) / (z1 << 4) * FACTOR;
    else
        p = 1;
        
    y = Y_AXIS_MAX - y;
    input_report_key(dev, BTN_TOUCH, 1);
    input_report_abs(dev, ABS_X, x);
    input_report_abs(dev, ABS_Y, y);
    input_report_abs(dev, ABS_PRESSURE, p);
    input_sync(dev);
}
static int tsc210x_ts_probe(struct platform_device *pdev)
{
   int status;

   dev = input_allocate_device();
   if (!dev)
      return -ENOMEM;

   status = tsc210x_touch_cb(tsc210x_touch);
   if (status)
   {
      goto error;
   }

   status = tsc210x_coords_cb(tsc210x_coords);
   if (status)
   {
      goto error;
   }

   dev->name = DRIVER_NAME;
   dev->dev.parent = &pdev->dev;
   dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
   dev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
   dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE);
   input_set_abs_params(dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
   input_set_abs_params(dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
   input_set_abs_params(dev, ABS_PRESSURE, PRESSURE_MIN, PRESSURE_MAX, 0, 0);
   status = input_register_device(dev);
   if (status)
   {
      printk(KERN_INFO "Unable to register TSC210x as input device !\n");
      goto error;
   }

   printk(DRIVER_NAME " driver initialized\n");
   return 0;

error:
   input_free_device(dev);
   return status;
}

Android start up

init.rc

Open $ANDROID_SOURCE/system/core/rootdir, comment the 'mount roofs' in read only mode and 'mount yaffs2' lines like this:

 # setup the global environment
    export PATH /sbin:/system/sbin:/system/bin:/system/xbin
    export LD_LIBRARY_PATH /system/lib
    export ANDROID_BOOTLOGO 1
    export ANDROID_ROOT /system
    export ANDROID_ASSETS /system/app
    export ANDROID_DATA /data
    export EXTERNAL_STORAGE /sdcard
    export BOOTCLASSPATH /system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar

    mount rootfs rootfs / rw remount

# Backward compatibility
    symlink /system/etc /etc

# create mountpoints and mount tmpfs on sqlite_stmt_journals
    mkdir /sdcard 0000 system system
    mkdir /system
    mkdir /data 0771 system system
    mkdir /cache 0770 system cache
    mkdir /sqlite_stmt_journals 01777 root root
    mount tmpfs tmpfs /sqlite_stmt_journals size=4m

    mount rootfs rootfs / ro remount

    write /proc/sys/kernel/panic_on_oops 1
    write /proc/sys/kernel/hung_task_timeout_secs 0
    write /proc/cpu/alignment 4
    write /proc/sys/kernel/sched_latency_ns 10000000
    write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000

# mount mtd partitions
    # Mount /system rw first to give the filesystem a chance to save a checkpoint
    # mount yaffs2 mtd@system /system 
    # mount yaffs2 mtd@system /system ro remount

    setprop EXTERNAL_STORAGE_STATE mounted
    mount vfat /dev/block/mmcblk0p1 /sdcard nosuid nodev

    # We chown/chmod /data again so because mount is run as root + defaults
    mount ext2 /dev/block/mmcblk0p2 /data nosuid nodev
    chown system system /data
    chmod 0771 /data

    # Same reason as /data above
    # mount yaffs2 mtd@cache /cache nosuid nodev
    chown system cache /cache
    chmod 0770 /cache

    # This may have been created by the recovery system with odd permissions
    chown system system /cache/recovery
    chmod 0770 /cache/recovery

Compile

$ cd $ANDROID_SOURCE
$ make

Making RootFS

Android Root File system

Android emulator has 3 basic images on tools/lib/images directory.

  • ramdisk.img is gziped cpio archive. ramdisk.img is a small partition image that is mounted read-only by the kernel at boot time. It only contains /init and a few config files. It is used to start init which will mount the rest of the system images properly and run the init procedure. A Ramdisk is a standard Linux feature. It is made just for the Android and do special things to start up the Android system.
  • system.img is a partition image that will be mounted as / and thus contains all system binaries.
  • userdata.img is a partition image that can be mounted as /data and thus contains all application-specific and user-specific data.

Create the Android root filesystem for apf27

Android’s root file system is generated in $ANDROID_SOURCE/out/target/product/generic. We will create a folder containing all Android files images.

$ sudo rm -rf $ANDROID_SOURCE/rootfs/
$ cd $ANDROID_SOURCE/out/target/product/generic
$ mkdir $ANDROID_SOURCE/rootfs
$ cp -a root/* $ANDROID_SOURCE/rootfs/
$ cp -a system/* $ANDROID_SOURCE/rootfs/system/
$ cd $ANDROID_SOURCE/rootfs
$ sudo chown -R root.root .
$ sudo chmod -R a+rwX data system

JFFS2 Root file system

Actually, Android should use a file system how support mmap function like yaffs2, ext2 or ubifs. Unfortunaly, U-Boot support only jffs2 or ext2. So we will use ext2 on a µSD for data.

Prepare the MMC/µSD card

We will create two partitions on our mmc/µSD card, The first one will use for Android memory card, the second one will use for Android file system. First connect your card reader to your workstation, with the mmc/µSD card inside. Type the dmesg command to see which device is used by your workstation. Let’s assume that this device is /dev/sdb

$ dmesg
...
[ 9145.613954]  sdb: sdb1 sdb2
[ 9145.615125] sd 10:0:0:0: [sdc] Attached SCSI removable disk
[ 9145.615258] sd 10:0:0:0: Attached scsi generic sg3 type 0

Type the mount command to check your currently mounted partitions. If MMC/SD partitions are mounted, unmount them.
In a terminal edit partitions with fdisk:

sudo fdisk /dev/sdb

Delete any existing partition with the d command.
Now, create the boot partition:

Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-495, default 1): 1
Last cylinder, +cylinders or +size{K,M,G} (1-239, default 239): +1G

Change its type to FAT32:

Command (m for help): t
Selected partition 1
Hex code (type L to list codes): c
Changed system type of partition 1 to c (W95 FAT32 (LBA))

Using the n command again, create a second partition filling up the rest of your card (just accept default values).
Now, format the partitions in your card:

sudo mkfs.vfat -n MemoryCard -F 32 /dev/sdb1
sudo mkfs.ext2 -L data /dev/sdb2

Boot setup

$ sudo mkfs.jffs2 -n -e 0x20000 --pad=0x700000 -r $ANDROID_SOURCE/rootfs -o $TFTPBOOT/apf27-rootfs.arm.jffs2

The last thing left to do is to specify how the board boots Linux.
In the U-boot prompt, make the mmc boot is on second partition of the mmc/µSD card

 
# setenv addjffsargs 'setenv bootargs ${bootargs} root=/dev/mtdblock4 rootfstype=jffs2 init=/init'
# setenv bootcmd run mmcboot
# saveenv
# run update_kernel
# run update_rootfs

Root file system on APF27 NAND memory

Note Note: wait the UBIFS integration in the u-boot loader

Boot setup

The last thing left to do is to specify how the board boots Linux. In the U-boot prompt, make the UBIFS boot.

 
# setenv addubifsargs 'setenv bootargs ${bootargs} root=/dev/mtdblock4 rootfstype=ubifs init=\init'
# setenv ubifsboot 'setenv bootargs ${console} ${mtdparts};run addubifsargs addipargs; setenv autostart yes;nboot.ubifs A0000000 0 ${kernel_offset}'
# setenv bootcmd run ubifsboot
# saveenv

Apf27 android.JPG

Debug

Prerequisite

Installing the Android Development Tools (ADT) Trace with logcat with Eclipse-ADT

 
$ adb kill-server
$ adb start-server

Test with Android emulator

Documentation on Android emulator

$ cd $ANDROID_SOURCE/kernel
$ make ARCH=arm goldfish_defconfig
$ make ARCH=arm CROSS_COMPILE=$ANDROID_SOURCE/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi
# Create AVD (Android Virtual Device)
$ $ANDROID_SDK/tools/android create avd -n APF27-H -t 2 -s 272x480
$ $ANDROID_SDK/tools/android create avd -n APF27-L -t 2 -s 480x272
$ $ANDROID_SOURCE/out/host/linux-x86/bin/emulator -avd APF27-H -sysdir $ANDROID_SOURCE/out/target/product/generic/ -kernel $ANDROID_SOURCE/kernel/arch/arm/boot/zImage -data $ANDROID_SOURCE/out/target/product/generic/userdata.img -ramdisk $ANDROID_SOURCE/out/target/product/generic/ramdisk.img -system $ANDROID_SOURCE/out/target/product/generic/system.img

Links

Thanks

Thanks to Xavier Romanens and Fabrice Carrel form Ecole d'ingénieurs et d'architectes de Fribourg for their contributions.