Monday, November 23, 2015

IOCTL Android

IOCTL is a very useful system call: it is simple and multiplexes the different commands to the appropriate kernel space function. In this post, I want to describe how you can implement a module with IOCTL support for Android. There are a lot of good articles about it (links below), and I just describe the differences regarding to the Android platform.

So, let's create a very simple misc device and let's play with it, doing some reads and writes. Initially, let's define a very simple kernel module (most of the code was taken from here).

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>


#define MY_MACIG 'G'
#define READ_IOCTL _IOR(MY_MACIG, 0, int)
#define WRITE_IOCTL _IOW(MY_MACIG, 1, int)
 
static int used; 
static char msg[200];

static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
{
  return simple_read_from_buffer(buffer, length, offset, msg, 200);
}

static ssize_t device_write(struct file *filp, const char __user *buff, size_t len, loff_t *off)
{
  if (len > 199)
    return -EINVAL;
  copy_from_user(msg, buff, len);
  msg[len] = '\0';
  return len;
}

static int device_open(struct inode *inode, struct file *file)
{
  used++;
  return 0;
}

static int device_release(struct inode *inode, struct file *file)
{
  used--;
  return 0;
}

char buf[200];
int device_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
  int len = 200;
  switch(cmd) {
  case READ_IOCTL: 
    copy_to_user((char *)arg, buf, 200);
    break;
 
  case WRITE_IOCTL:
    copy_from_user(buf, (char *)arg, len);
    break;

  default:
    return -ENOTTY;
  }
  return len;
}

static struct file_operations fops = {
        .owner = THIS_MODULE,
        .read = device_read,
        .write = device_write,
        .open = device_open,
        .release = device_release,
        .unlocked_ioctl = device_ioctl
};

static struct miscdevice my_miscdev = {
        .name         = "my_device",
        .mode         = S_IRWXUGO,
        .fops         = &fops,
};

static int __init cdevexample_module_init(void)
{
  int ret = misc_register(&my_miscdev);
  if (ret < 0) {
    printk ("Registering the character device failed\n");
    return ret;
  }
  printk("create node with mknod /dev/my_device\n");
  return 0;
}

static void __exit cdevexample_module_exit(void)
{
  misc_deregister(&my_miscdev);
}  

module_init(cdevexample_module_init);
module_exit(cdevexample_module_exit);
MODULE_LICENSE("GPL");

Compile it and insert into Android kernel.

shell@flounder:/ $ su
shell@flounder:/ $ insmod my_module.ko

Now, we can check the new device in /dev:

shell@flounder:/ $ su
shell@flounder:/ $ ls -l /dev
...
crw-rw---- root     mtp       10,  23 2015-11-23 18:04 mtp_usb
crw------- root     root      10,   0 2015-11-23 18:11 my_device
crw------- root     root      10,  36 2015-11-23 18:04 network_latency
crw------- root     root      10,  35 2015-11-23 18:04 network_throughput
crw-rw-rw- root     root       1,   3 2015-11-23 18:04 null

...

See that the permissions are limited. Don't forget to set it to:

shell@flounder:/ $ chmod 666 /dev/my_device
shell@flounder:/ $ ls -l /dev
...
crw-rw---- root     mtp       10,  23 2015-11-23 18:04 mtp_usb
crw-rw-rw- root     root      10,   0 2015-11-23 18:11 my_device
crw------- root     root      10,  36 2015-11-23 18:04 network_latency
crw------- root     root      10,  35 2015-11-23 18:04 network_throughput
crw-rw-rw- root     root       1,   3 2015-11-23 18:04 null

...

Now, let's try to do some operations with our device driver:

shell@flounder:/ $ echo "Hello world" > /dev/my_device
shell@flounder:/ $ cat /dev/my_device

You will see the following error on the logcat:

avc: denied { read write } for name="my_device" dev="tmpfs" scontext=u:r:system_app:s0 tcontext

This means that SELinux (yes, Android makes heavy usage of it) also controls the access to device drivers and you cannot read/write from/to your new drive. You have two options: i) disable SELinux in Android (you need to change some kernel options and rebuild it) or ii) add some new rules into SELinux. Let's do the last to learn a bit more :-)

So, we change the following files and give access (read, write, getattr, ioctl, open and create) to our new device /dev/my_device. If you need to restrict the access, you can adapt the policies according to your needs. For more information about SELinux and Android, take a look in this doc (specially the section "Implementation").

external/sepolicy/device.te
type fscklogs, dev_type;
type full_device, dev_type;
type my_device, dev_type;


external/sepolicy/file_contexts
/dev/rproc_user        u:object_r:rpmsg_device:s0
/dev/my_device         u:object_r:my_device:s0
/dev/snd(/.*)?         u:object_r:audio_device:s0


external/sepolicy/app.te
allow appdomain usb_device:chr_file { read write getattr ioctl };
allow appdomain usbaccessory_device:chr_file { read write getattr };
allow appdomain my_device:chr_file { read write getattr ioctl open create };


Now, let's build the Android framework again and flash the device. Everything should work fine.

shell@flounder:/ $ echo "Hello world" > /dev/my_device
shell@flounder:/ $ cat /dev/my_device
Hello world

That's it!! You can also check the following links

  • http://www.all-things-android.com/content/understanding-se-android-policy-files
  • https://source.android.com/security/selinux/

Wednesday, November 11, 2015

Latency/Bandwidth and Performance

I've been always interested in scrapping a bit more my application... no matter its level: frontend, framework or native. That's why I guess I have decided to study a bit more Software Systems. Some weeks ago, I was implementing some tests to evaluate my current project in terms of "time spent for certain operations". In addition, I had to force some cache/memory fault, work on some *non* cache-friendly program and, at the end, I was thinking about other aspects of my system. Usually, for software systems, we pretty much focus on latency and bandwidth. For example, when you are developing an application to have an efficient access to memory (cache-friendly!), you are thinking about latency, as you want to avoid (expensive) access to a lower-level memory. Of course, depending on the operation (network, memory access, etc.), latency and bandwidth have a bit different meaning. For example, for network, latency is measured by sending a packet that is returned to the sender. For memory access, latency can be explained as "the delay time between the moment a memory controller tells the memory module to access a particular memory column on a RAM module, and the moment the data from the given array location is available on the module's output pins". [Wiki]

I took a look on some amazing links that gave me a very precise and detailed view about such discussion and here they are:



Monday, September 7, 2015

Asynchronous and synchronous I/O in Android NDK

This week, I had some issues on my tests in a Lollipop device with AOSP. I really wanted to have synchronous reads/writes so that I could evaluate my solution in a different way. I am also trying to minimize the effects of caching as much as possible. If you check for async and sync I/O operations for Linux, you'll a lot of references to the flags O_DIRECT, O_SYNC, O_DSYNC, ...

Actually, that's a good way to implement. However, with Android NDK apps, things are not so straightforward.

First, to have a good understanding about the O_*SYNC flags, check this link. Of course, the man page for the command open as well.

First, O_DIRECT does not work in Android since 4.4. See this link for more details about it.  That's sad. So, let's try to use the O_*SYNC data.

O_SYNC and O_DSYNC work fine in Android. But, as the description say, only for writes. Another detail: for Android, O_SYNC has the same semantics as O_DSYNC. That's good, but I still want something similar to reads as well.

Why don't we use O_RSYNC? Well, Android does not implement it :-( But it's not the only one... there are other Linux distributions that don't do it either.

What about dropping caches?? See this link for more details. Hum, that works, but after the first read, the data will be cached again :-(

So, I am still looking for a solution for Android Lollipop. Hope to post it soon!


Monday, August 17, 2015

Android - Using Crypto extensions

I had some problems compiling ASM code as part of an Android kernel module.

As part of my system, I need to encrypt/decrypt data inside the kernel and I decided to use AES CBC with 128 bits keys. I am also using a Nexus 9 (arm64 v8), which means that I have access to crypto extensions, including instructions for AES operations :-)

Considering this scenario, I implemented 3 options (of course, I picked the most efficient one at the end):
  1. A "standard" AES CBC implementation in C (the worst performance);
  2. The Linux kernel Crypto lib with crypto extensions set to ON (good performance and very easy to use);
  3. The AES CBC asm implementation from the OpenSSL lib (good performance and it took me extra time to integrate it into my code);
At the end, I took the option #03. Both #02 and #03 have good performance, but #03 is slightly better.

Let's discuss #02 and #03 in this post.

As the very first step, check if your kernel has the proper options enabled for the crypto extension usage. The following ones should be set to "y":

CONFIG_CRYPTO_HW
CONFIG_ARM64_CRYPTO
CONFIG_CRYPTO_AES_ARM64_CE
CONFIG_CRYPTO_AES_ARM64_CE_CCM
CONFIG_CRYPTO_AES_ARM64_CE_BLK

The Crypto lib

The Linux kernel provides the Crypto library, with several cryptography operations, such as AES, including different block/streams ciphers. The following code use AES 128-bits CBC, and a 0-ed IV (don't do that!)

static int pc_aes_encrypt_kernel(const void *key, int key_len,
                                        void *dst, size_t *dst_len,
                                        const void *src, size_t src_len)
{
        struct crypto_blkcipher *blkcipher = NULL;
        char *cipher = "__cbc-aes-ce";
        char iv[AES_BLOCK_SIZE];
        char *charkey = (unsigned char *) key;

        unsigned int ivsize = 0;
        char *scratchpad_in = NULL, *scratchpad_out = NULL; 
        struct scatterlist sg_in, sg_out;
        struct blkcipher_desc desc;
        int ret = -EFAULT;
        int len = ((int)(src_len/16) + 1) * 16;

        memset(iv, 0x00, AES_BLOCK_SIZE); // set the iv to a proper value!!

        blkcipher = crypto_alloc_blkcipher(cipher, 0, 0);
        if (IS_ERR(blkcipher)) {
                PC_LOGV("could not allocate blkcipher handle for %s\n", cipher);
                return -PTR_ERR(blkcipher);
        }

        if (crypto_blkcipher_setkey(blkcipher, charkey, key_len)) {
                PC_LOGV("key could not be set\n");
                ret = -EAGAIN;
                goto out;
        }

        ivsize = crypto_blkcipher_ivsize(blkcipher);
        if (ivsize) {
                if (ivsize != AES_BLOCK_SIZE)
                        PC_LOGV("IV length differs from expected length. It should be : %d\n",ivsize);
                crypto_blkcipher_set_iv(blkcipher, iv, ivsize);
        }

        scratchpad_in = kmalloc(src_len, GFP_KERNEL);
        if (!scratchpad_in) {
                PC_LOGV("could not allocate scratchpad_in for %s\n", cipher);
                goto out;
        }

        scratchpad_out = kmalloc(len, GFP_KERNEL);
        if (!scratchpad_out) {
                PC_LOGV("could not allocate scratchpad_in for %s\n", cipher);
                goto out;
        }

        memcpy(scratchpad_in,src,src_len);

        desc.flags = 0;
        desc.tfm = blkcipher;
        sg_init_one(&sg_in, scratchpad_in, src_len);
        sg_init_one(&sg_out, scratchpad_out, len);

        crypto_blkcipher_encrypt(&desc, &sg_out, &sg_in, src_len);

        // for decryption, use the following
        // crypto_blkcipher_decrypt(&desc, &sg_out, &sg_in, src_len);

        memcpy(dst,scratchpad_out,sg_out.length);

        *dst_len = sg_out.length;

        ret = 0;
        goto out;

out:
        if (blkcipher)
                crypto_free_blkcipher(blkcipher);
        if (scratchpad_out)
                kzfree(scratchpad_out);
        if (scratchpad_in)
                kzfree(scratchpad_in);

        return ret;

}

For decryption, you can use the same code, but use the function crypto_blkcipher_decrypt instead.

Integrating a *.S file as part if your module built process

As said, I used an existing implementation from OpenSSL, which uses the crypto extensions of arm64-v8. I only had to change the "includes" of the asm file. In addition, I included the object file into my Makefile, like this:

pc_module-objs := ...
                  src/pc_utility.o src/asm/aesv8-armx-64.o \
                  src/crypto/aes.o src/crypto/crypto.o


However, I had some problems with different asm files that I was testing. For example, for the OpenSSL library, some of them will not compile if you use GCC 4.8/4.9. The point is that they use a different architectural syntax (Apple) and you'll see several "Error: unknown mnemonic" error messages.

So, you can use LLVM to compile the asm files with the Apple architectural syntax. LLVM is available in the Android NDK. Then, you can copy the *.o files into your code and build your project. The symbols should match as a charm.

The following code shows the usage of the functions defined in the file aesv8-armx-64.S (available into the folder /external/openssl/)

static int pc_aes_encrypt_hw(const void *key, int key_len,
                                        void *dst, size_t *dst_len,
                                        const void *src, size_t src_len)
{
        AES_KEY enc_key;

        unsigned char enc_out[src_len];
        unsigned char iv[AES_BLOCK_SIZE];
        unsigned char *aes_input = (unsigned char *) src;
        unsigned char *aes_key = (unsigned char *) key;

        memset(iv, 0x00, AES_BLOCK_SIZE);

        HWAES_set_encrypt_key(aes_key, key_len*8, &enc_key);
        HWAES_cbc_encrypt(aes_input, enc_out, src_len, &enc_key, iv, AES_ENCRYPT);

        *dst_len = src_len;
        memcpy(dst,&enc_out[0],src_len);

        return 0;
}

static int pc_aes_decrypt_hw(const void *key, int key_len,
                                        void *dst, size_t *dst_len,
                                        const void *src, size_t src_len)
{
        AES_KEY dec_key;

        unsigned char iv[AES_BLOCK_SIZE];
        unsigned char dec_out[src_len];
        unsigned char *aes_input = (unsigned char *) src;
        unsigned char *aes_key = (unsigned char *) key;

        memset(iv, 0x00, AES_BLOCK_SIZE);

        HWAES_set_decrypt_key(aes_key, key_len*8, &dec_key);
        HWAES_cbc_encrypt(aes_input, dec_out, src_len, &dec_key, iv, AES_DECRYPT);

        *dst_len = src_len;
        memcpy(dst,&dec_out[0],src_len);

        return 0;
}


The functions HWAES_set_encrypt_key, HWAES_set_decrypt_key, HWAES_cbc_encrypt and HWAES_cbc_decrypt are implemented in a asm file (aesv8-armx-64.S) and defined in a header file (aes_cbc.h). Again, I used AES 128-bits CBC, with 0-ed IV (don't do that!).


Saturday, July 18, 2015

New syscalls for armv8-64 kernel

I decided to use a different target platform for my project. Initially, I was using Android Kitkat + Galaxy Nexus (ARMv7 + TI OMAP 4460). To obtain best performance with some crypto operations, I am now using Android Lollipop + Nexus 9 (ARMv8 64bits). I did some tests with basic OpenSSL examples (for example, AES-128 and SHA1) and the numbers are impressive.

The goal of this post is to document some problems that I had building the Android image and its respective kernel. You might think that I am being redundant, as you [might] have the same issues with a traditional Linux kernel. I've done this only for Android platform, so I don't know for other kernels. I'm also having some other problems, such as ARMv8-64 asm file compilation inside the kernel, but this will be described in another poster.

Basically, in my project, I have to create new syscalls and also change the sys_call_table to intercept existing ones, such as NR_write, NR_open, etc. You can check another post that describes how to create new syscalls and how to intercept existing syscalls. So, what is different?

1. Some syscalls have changed, some have been created and some others are not supported anymore. 


 __NR_open, __NR_link, __NR_chmod, __NR_rename, ... are not supported anymore. Instead, you should use __NR_openat, __NR_mkdirat, ...

For example, check the syscalls under the #def body __ARCH_WANT_SYSCALL_DEPRECATED and __ARCH_WANT_SYSCALL_NO_AT (file include/uapi/asm-generic/unistd.h)

In addition, the syscall number has also been changed. For example, NR_write changed from 3 to 63. Remember to change your code in userpsace that has any reference to one of these numbers.

2. Much easier than before: now, you only need to change less files. Add the definition of your syscalls in the following files:


include/linux/syscalls.h

 ...
 asmlinkage long sys_seccomp(unsigned int op, unsigned int flags,
                            const char __user *uargs);

 asmlinkage long sys_my_syscall(void __user *buf);
 ...

include/uapi/asm-generic/unistd.h

 ...
 #define __NR_seccomp 277
 __SYSCALL(__NR_seccomp, sys_seccomp)

 #define __NR_my_syscall 278
 __SYSCALL(__NR_my_syscall, sys_my_syscall)

 #undef __NR_syscalls
 #define __NR_syscalls 279
 ...

kernel/my_syscall_code.c (this file should contain the implementation of your syscall)

kernel/Makefile

 obj-y = ....
 ...
         async.o range.o groups.o lglock.o smpboot.o \
         my_syscall_code.o
 ...

3. Don't forget to add asm/unistd.h as part of your head files.


4. Kernel modules are implemented the same way as before. To build it, remember to set cflags properly


There's just a small change on building the module. Call make with CFLAGS_MODULE=-fno-pic

5. And of course, the syscall table address table has changed as well. 


Check the symbol sys_call_table in the file System.map.

As said, I am having other problems with it. In my case, I want to add some assembly files into my module and compile them all together. The point is that they are written in a specific format that GCC does not understand, but clang. So, things would be solved if I could use clang to build my module, but it doesn't work like that :-( Let's what comes next!

EDIT (31/07/2015) : this is a good guide for 32 bits -> 64 bits porting

Sunday, June 14, 2015

UEvents in Android - from kernel events to notifications

In my current project, the system has to notify the user about a certain action that actually takes place inside the kernel (in this case, transfer of security keys). I don't want to get into too much details about the tasks, but let's consider that the kernel and the Dalvik are the only trustworthy components and the security keys are stored inside the kernel. I've also seen some pieces of code that helps to implement this, but I could not find a end-to-end solution.

[This post is a bit long, as it contains a lot of code and I tried to explain some details of it.]

Ok, let's proceed. This is the flow:

1) The kernel is asked to perform a certain syscall (transfer securely stored keys inside the kernel);
2) The kernel has to notify the user that some application has asked to do something suspicious, for example, to have access to some security keys;
3) The event is sent all the way to the userspace and a notification appears to the user (UI).

First, we have to generate one event in the kernel space (you can create a module and insert your code there). For this, we use an UEVENT. In addition, we can also add extra pieces of information into the event, for example strings, integers and so forth. The following code shows a simple UEVENT generated from the kernel space. It contains one integer field called "etype".

Some pieces of code were taken from the kernel itself (linux/samples/kobject/kset-example.c), and I made small changes to keep it even simpler. For more information, check the original one.

Initially, we need to create a kobject to embedded an attribute, for instance (https://www.kernel.org/doc/Documentation/kobject.txt). Once the kobject is registered, we broadcast it to other components of the system using kobject_uevent(). The files foo_kobject.c/h and foo_event.c/h contain code to broadcast an UEvent using a kobject.

foo_kobject.h (kernel code, for example, as part of your module)
#ifndef FOO_KOBJECT_
#define FOO_KOBJECT_

/* 
 * Partially copied from linux/samples/kobject/kset-example.c
 *
 * Released under the GPL version 2 only.
 */

/*
 * This is our "object" that we will create and register it with sysfs.
 */
struct foo_obj {
 struct kobject kobj;
 int foo;
};
#define to_foo_obj(x) container_of(x, struct foo_obj, kobj)

struct foo_obj *
create_foo_obj(const char *name);

int 
foo_kobj_init(void);

void
foo_kobj_exit(void);

#endif

foo_kobject.c (kernel code, for example, as part of your module)
/*
 * Partially copied from linux/samples/kobject/kset-example.c
 *
 * Released under the GPL version 2 only.
 *
 */

#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>

#include "foo_kobject.h"

/*
 * This module shows how to create a kset in sysfs called
 * /sys/kernel/foo
 * Then one kobject is created and assigned to this kset, "foo".  
 * In this kobject, one attribute is also
 * created and if an integer is written to these files, it can be later
 * read out of it.
 */

/* a custom attribute that works just for a struct foo_obj. */
struct foo_attribute {
 struct attribute attr;
 ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
 ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
};
#define to_foo_attr(x) container_of(x, struct foo_attribute, attr)

/*
 * The default show function that must be passed to sysfs.  This will be
 * called by sysfs for whenever a show function is called by the user on a
 * sysfs file associated with the kobjects we have registered.  We need to
 * transpose back from a "default" kobject to our custom struct foo_obj and
 * then call the show function for that specific object.
 */
static ssize_t foo_attr_show(struct kobject *kobj,
        struct attribute *attr,
        char *buf)
{
 struct foo_attribute *attribute;
 struct foo_obj *foo;

 attribute = to_foo_attr(attr);
 foo = to_foo_obj(kobj);

 if (!attribute->show)
  return -EIO;

 return attribute->show(foo, attribute, buf);
}

/*
 * Just like the default show function above, but this one is for when the
 * sysfs "store" is requested (when a value is written to a file.)
 */
static ssize_t foo_attr_store(struct kobject *kobj,
         struct attribute *attr,
         const char *buf, size_t len)
{
 struct foo_attribute *attribute;
 struct foo_obj *foo;

 attribute = to_foo_attr(attr);
 foo = to_foo_obj(kobj);

 if (!attribute->store)
  return -EIO;

 return attribute->store(foo, attribute, buf, len);
}

/* Our custom sysfs_ops that we will associate with our ktype later on */
static const struct sysfs_ops foo_sysfs_ops = {
 .show = foo_attr_show,
 .store = foo_attr_store,
};

/*
 * The release function for our object.  This is REQUIRED by the kernel to
 * have.  We free the memory held in our object here.
 *
 * NEVER try to get away with just a "blank" release function to try to be
 * smarter than the kernel.  Turns out, no one ever is...
 */
static void foo_release(struct kobject *kobj)
{
 struct foo_obj *foo;

 foo = to_foo_obj(kobj);
 kfree(foo);
}

/*
 * The "foo" file where the .foo variable is read from and written to.
 */
static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
   char *buf)
{
 return sprintf(buf, "%d\n", foo_obj->foo);
}

static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
    const char *buf, size_t count)
{
 sscanf(buf, "%du", &foo_obj->foo);
 return count;
}

static struct foo_attribute foo_attribute =
 __ATTR(foo, 0666, foo_show, foo_store);

/*
 * Create a group of attributes so that we can create and destroy them all
 * at once.
 */
static struct attribute *foo_default_attrs[] = {
 &foo_attribute.attr,
 NULL, /* need to NULL terminate the list of attributes */
};

/*
 * Our own ktype for our kobjects.  Here we specify our sysfs ops, the
 * release function, and the set of default attributes we want created
 * whenever a kobject of this type is registered with the kernel.
 */
static struct kobj_type foo_ktype = {
 .sysfs_ops = &foo_sysfs_ops,
 .release = foo_release,
 .default_attrs = foo_default_attrs,
};

static struct kset *example_kset;

struct foo_obj *create_foo_obj(const char *name)
{
 struct foo_obj *foo;
 int retval;

 /* allocate the memory for the whole object */
 foo = kzalloc(sizeof(*foo), GFP_KERNEL);
 if (!foo)
  return NULL;

 /*
  * As we have a kset for this kobject, we need to set it before calling
  * the kobject core.
         */
 foo->kobj.kset = example_kset;

 /*
  * Initialize and add the kobject to the kernel.  All the default files
  * will be created here.  As we have already specified a kset for this
  * kobject, we don't have to set a parent for the kobject, the kobject
  * will be placed beneath that kset automatically.
  */
 retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
 if (retval) {
  kobject_put(&foo->kobj);
  return NULL;
 }

 /*
  * We are always responsible for sending the uevent that the kobject
  * was added to the system.
  */
 kobject_uevent(&foo->kobj, KOBJ_ADD);

 return foo;
}

static void destroy_foo_obj(struct foo_obj *foo)
{
 kobject_put(&foo->kobj);
}

int
foo_kobject_init(void)
{
 /*
  * Create a kset with the name of "kset_foo",
  * located under /sys/kernel/
  */
 example_kset = kset_create_and_add("kset_foo", NULL, kernel_kobj);
 if (!example_kset)
  return -ENOMEM;

}

void 
foo_kobject_exit(void)
{
 destroy_foo_obj(foo_kobj);
 kset_unregister(example_kset);
}

foo_uevent.h (kernel code, for example, as part of your module)
#ifndef FOO_UEVENT_
#define FOO_UEVENT_

enum FOO_event_type {
  FOO_GET = 1,
  FOO_SET
};

struct foo_event {
  enum foo_event_type etype;
};

int foo_init_events(void);

int foo_send_uevent(struct foo_event *fooe);

#endif


In the following code, we send an event string as part of our new UEvent. In our case, just the type of the event (FOO_GET or FOO_SET). foo_uevent.c (kernel code, for example, as part of your module)
#include <linux/kobject.h>
#include "foo_kobject.h"
#include "foo_uevent.h"

static struct foo_obj *foo_kobj;

int foo_init_events(void)
{
  int ret;

  ret = example_init();
  if (ret) 
  { 
    printk("error - could not create ksets\n");  
    goto foo_error;
  } 
 
  foo_kobj = create_foo_obj("foo");
  if (!foo_kobj)
  {
    printk("error - could not create kobj\n"); 
    goto foo_error;
  }
 
  return 0;

foo_error:
  return -EINVAL;
}

int foo_send_uevent(struct foo_event *pce)
{
  char event_string[20];
  char *envp[] = { event_string, NULL };

  if (!foo_kobj)
    return -EINVAL;

  snprintf(event_string, 20, "FOO_EVENT=%d", pce->etype);

  return kobject_uevent_env(&foo_kobj->kobj, KOBJ_CHANGE, envp);
}

So far, we've only made changes in the kernel level. But also, we need to listen to UEVENTs in the userspace. For that, I changed a little bit the Dalvik and added the following binder to listen to UEvents and send notifications, so that the user can see the event.

For more details on how to add a system service in Android, check this link. In this case, I added a Binder, as we are not providing any service (setting an integer, for instance). In our example, we simply broadcast events to the user interface.

To add the following Binder, create a file that contains the code into framework/base/services/java/com/android/server/. You also have to change the file Android.mk at framework/base/services/jni/Android.mk

...
LOCAL_SRC_FILES:= \
     com_android_server_VibratorService.cpp \
     com_android_server_location_GpsLocationProvider.cpp \
     com_android_server_connectivity_Vpn.cpp \
+    com_android_server_pm_PackageManagerService.cpp \
     onload.cpp
...

Also, don't forget to register the service (check the link that I mentioned before!), by changing the file framework/base/services/java/com/android/server/SystemServer.java

class ServerThread extends Thread {
...
         WifiP2pService wifiP2p = null;
         WifiService wifi = null;
         NsdService serviceDiscovery= null;
+        MyServiceBinder myService = null;
         IPackageManager pm = null;
         Context context = null;
         WindowManagerService wm = null;
...
         class ServerThread extends Thread {
...
             }

             try {
+                Slog.i(TAG, "My Service - Example");
+                myService = MyServiceBinder.create(context);
+                ServiceManager.addService(
+                        Context.MY_SERVICE, myService);
+            } catch (Throwable e) {
+                reportWtf("starting Privacy Capsules Service", e);
+            }
+
...

Finally, here is the Java code. This Binder simply listens to UEvents that contains the string "FOO_EVENT", which also embeds the integer as part of the UEvent. Then, we create a UI notification and we attach an Intent to the notification, so that when the user clicks on it, a Activity or a Dialog is launched.
...
public class MyServiceBinder extends Binder {

    private final int FOO_GET = 1;
    private final int FOO_SET = 2;

    private MyServiceWorkerThread mWorker;
    private MyServiceWorkerHandler mHandler;
    private Context mContext;
    private NotificationManager mNotificationManager;

    /*
     * We create an observer to listen to the UEvent we are interested in. 
     * See UEventObserver#startObserving(String). From the UEvent, we extract 
     * an existing integer field and we propagate create a notification to 
     * be seen by the user.
     */
    private final UEventObserver mUEventObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            final int eventType = Integer.parseInt(event.get("FOO_EVENT"));
            sendMyServiceNotification(eventType);
        }
    };

    public MyServiceBinder(Context context) {
        super();
        mContext = context;
        mNotificationManager = (NotificationManager)mContext.getSystemService(
                               Context.NOTIFICATION_SERVICE);

         /* We have to specify a filter to receive only certain UEvents from the 
          * kernel. In this case, the UEVENT that we want to listen to contains 
          * the string "FOO_EVENT".
          */
        mUEventObserver.startObserving("FOO_EVENT=");

        mWorker = new MyServiceWorkerThread("MyServiceWorker");
        mWorker.start();
    }

    /*
     * Add potential initialization steps here
     */
    public static MyServiceBinder create(Context context) {
        MyServiceBinder service = new MyServiceBinder(context);
        return service;
    }
    
    /*
     * Sends notification to the user. In this case, we are create notifications. 
     * If the user clicks on the notification, we send an Intent so that an system 
     * application is launched. For example, if we are listening to notifications 
     * for USB UEvents, the USB Settings application can be launched when the 
     * notification is clicked.
     */
    private final void sendNotificationToUser(int type) {
        String notificationMessage = "";
        Resources r = mContext.getResources();
        switch(type) {
                case FOO_GET:
                // do something if the event type is A
                notificationMessage = "This is event type FOO_GET";
                break;
                case FOO_SET:
                // do something if the event type is B
                notificationMessage = "This is event type FOO_SET";      
                break;
                default:
                break;
        }  

        String notificationTitle = r.getString(R.string.my_service_notification_title);
        long notificationWhen = System.currentTimeMillis();
        int requestID = (int) notificationWhen;
 
        Intent intent = new Intent(Intent.ACTION_FOO_EVENT);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP
                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);

        intent.putExtra(FooManager.EXTRA_ETYPE, type);
        intent.putExtra(FooManager.EXTRA_MESSAGE, notificationMessage);

        PendingIntent pi = PendingIntent.getActivityAsUser(mContext, requestID,
                                intent, 0, null, UserHandle.CURRENT);

        Notification notification = new Notification.Builder(mContext)
                        .setSmallIcon(R.drawable.indicator_input_error)
                        .setContentTitle(notificationTitle)
                        .setContentText(notificationMessage)
                        .setPriority(Notification.PRIORITY_HIGH)
                        .setDefaults(0)
                        .setWhen(notificationWhen)
                        .setOngoing(true)
                        .setContentIntent(pi)
                        .setAutoCancel(true)
                        .build();

        mNotificationManager.notifyAsUser(null,
                        R.string.my_service_notification_title,
                        notification, UserHandle.ALL);
    }


    /*
     * Runner for the 
     *
     */
    private void sendMyServiceNotification(final int type) {
        
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                sendNotificationToUser(type);
            }
        });

    }

    private class MyServiceWorkerThread extends Thread {

        public MyServiceWorkerThread(String name) {
            super(name);
        }

        public void run() {
            Looper.prepare();
            mHandler = new MyServiceWorkerHandler();
            Looper.loop();
        }

    }
 
    private class MyServiceWorkerHandler extends Handler {

        private static final int MESSAGE_SET = 0;

        @Override
        public void handleMessage(Message msg) {
            try {
                if (msg.what == MESSAGE_SET) {
                    Log.i(TAG, "set message received: " + msg.arg1);
                }
            } catch (Exception e) {
                // Log, don't crash!
                Log.e(TAG, "Exception in MyServiceWorkerHandler.handleMessage:", e);
            }
        }

    }
}

Sunday, May 10, 2015

Android Linux Kernel module building/installation

Let's now show how to install a module into the just compiled Android kernel (see this post for more information)

For compiling the module, it's important that you use the same kernel source that is installed in your device. Otherwise, you cannot install the module.

a. Go to the code that contains an example of kernel module for Android (for instance, [your_code]/module/intercept);
b. Update the makefile to point to your kernel source code;
c. You need to set some env variables, including the cross-compiler. In this case, you can use the ones provided by the Android source, in the folder prebuilts:

   @desktop:$ export CROSS_COMPILE=[android_sdk]/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin/arm-eabi-
   @desktop:$ export ARCH=arm
   @desktop:$ export PATH=$PATH:/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin/
   @desktop:$ make
   (a kernel module file will be generated ([your_module].ko) and this is the one that we need to install in our rooted device)

d. Copy the .ko file to the device using the following command:

   @desktop:$ adb push [your_module].ko /data/local/tmp

e. Install the kernel module using the following commands:

   @desktop:$ adb shell
   @android:$ su
   @android:$ cd /data/local/tmp
   @android:$ insmod [your_module].ko

f. You might get some errors, such as "function not implemented". To check more details about what's wrong, you can check the log file by typing the following command.

   @android:$ dmesg

Friday, May 8, 2015

Some other tips on building your AOSP for development

As you need to implement your solution into Android system, you end up learning a lot about the different Android layers (kernel, OS and applications) and how to integrate them. I decided to add the following list with some tips, as these small things took me some precious time to get it solved. The list will be often edited:

#01 - Make sure that you're flashing the device with the proper kernel image

This is what happened to me: I had previously built the kernel (something like two months before). Then, I had to build the OS image from scratch, that is, cleaning up the previous build (with make clobber). When I used the command make bootimage, including setting the variables properly, the output always had the wrong kernel image (not the one that I had previously built, but the existing one in the directory prebuilts). The build process won't take too old kernel images. Therefore, make sure that the compressed kernel image is always new. Even if you don't make any change on the kernel source, do make again to generate a new file.