2014年3月24日星期一

Android 入门 - 使用日志

日志是程序调试及查错的必备工具,由于Android不是运行在开发者的电脑上,而是运行在模拟器或手机上,需要借助一些工具才能查看到日志信息。

1、常用工具

1.1 Eclipse Logcat

在Eclipse下安装ADT工具后,就可以用Logcat插件来直观方便的查看Android系统的运行日志。其实,该工具只是抓取Android系统中的/dev/log/main文件的信息,并直观的显示到Eclipse界面中。只要模拟器一运行,在Eclipse中的Logcat插件就可以看到模拟器的运行日志。
默认情况下,Logcat只显示最近5000条日志,可以通过preferences -> Android -> Logcat来调整最多可以显示的日志条数。
eclipse Logcat工具还可以定制过滤器快速查看,或输入关键字查询日志,非常方便。

1.2 adb logcat

adb logcat是Android Sdk自带的日志查看工具,具体用法参看:Android官网的读写日志。命令行格式如下:
# adb logcat [<option>] ... [<filter-spec>] ...
下面例举几个常用命令:

a)带时间查看
# adb logcat -v time
b)查看所有的警告以上日志
# adb logcat -v time *:W
c)查看MyApp所有的警告以上的日志
# adb logcat -v time MyApp:W
d) MyApp所有的警告以上的日志输出到文件中
# adb logcat -v time -f /data/local/mylog.log MyApp:W
要注意:/data/local/mylog.log并非开发者电脑上的文件,而是模拟器(手机)上的文件。

1.3 Adb shell

adb 是Android SDK自带的工具,adb shell可以连接至模拟器(手机),直接操作模拟器(手机),相当于一个linux term,只有拥有权限,就可以运行全部的Linux命令。
# adb shell
root@generic:/ # 
a)查看Logcat日志
root@generic:/ # logcat
与前面描述的adb logcat操作相同。

b)查看内核日志
root@generic:/ # dmesg
dmesg是内核中的一个命令,可以查看内核日志,当然,你也可以用 cat /proc/kmsg。不同的是,dmesg只读取缓冲区中的内核日志,而cat /proc/kmsg则可以原始的、完整的日志文件。

2、日志实现

开发Andorid应用的日志最常用的是用户空间日志,开发Android驱动程序和内核的日志则为内核日志。这里作一简单介绍。

2.1 内核日志

内核日志保存在/proc/kmsg文件中,如何写内核日志呢?用printk函数,详情可参见:内核日志:API及实现。这里简单介绍如何使用及简单代码分析。
#include <linux/printk.h>

printk( KERN_INFO " soft_cursor ingored.\n" );
KERN_INFO是定义的宏,就是代表字符串:"<6>",中间没有逗号,C编译器会自动将两个串联在一起的字符串组合成一个字符串,所以,不要误认为KERN_INFO是一个参数。上面语句打印的实际信息为"<6> soft_cursor ingored.\n"。
printk函数只打印文本信息,没有时间等其他字段,查看一下printk.c源代码:
# cd /Volumes/androkd-kernel/source/goldfish
# vi kernel/printk.c
找到如下代码:
...
#if defined(CONFIG_PRINTK_TIME)
static bool printk_time = 1;
#else
static bool printk_time = 0;
#endif
module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR);

static bool always_kmsg_dump;
...
    
从上面的代码可以看出:只有定义了CONFIG_PRINTK_TIME的情况下,才会打印时间。我们可以直接修改printk.c,但既然有配置项,就将配置项加入再重新编译就OK了。我们采取Android 入门 - 定制开机画面一文中的方法,将CONFIG_PRINTK_TIME加入到配置并重新编译内核。
# vi arch/arm/configs/goldfish_armv7_defconfig
在文件末尾加入:
CONFIG_PRINTK_TIME=y

按 wq 保存退出。

# make goldfish_armv7_defconfig
# make -j16
编译过程很快完成。运行后用adb shell查看?时间信息已经加入:
# adb shell
# root@generic:/ # dmesg

...
<6>[    3.140000] binder: 48:48 transaction failed 29189, size 0-0
<3>[    3.210000] init: cannot find '/system/etc/install-recovery.sh', disabling 'flash_recovery'
<5>[    3.210000] type=1405 audit(1395383323.310:5): bool=in_qemu val=1 old_val=0 auid=4294967295 ses=4294967295
...
   
上面蓝色的部分是表示时间距离CPU启动时间的间隔秒数,如何换算时间呢?在Android源代码中,未找到cpu_clock函数,此函数只在内核源码中有(未经权威确认),所以,无法知道当前的cpu_clock是多少,也就没法精确计算具体的时间,经过一翻折腾,还是认为直接修改源代码,把时间直接改成能够打印的时间。
# vi kernel/printk.c

....
#define CREATE_TRACE_POINTS
#include <trace/events/printk.h>
//一面一行为新增代码
#include <linux/time.h>
/*
 * Architectures can override it:
 */
void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...)
{
}
....
asmlinkage int vprintk(const char *fmt, va_list args)
{
 ...
    if (printk_time) {
            /* Add the current time stamp */
            char tbuf[50], *tp;
            unsigned tlen;
            struct timespec ts = current_kernel_time();
            // 下面的代码为注释代码
            /*unsigned long long t;
            unsigned long nanosec_rem;

            t = cpu_clock(printk_cpu);
            nanosec_rem = do_div(t, 1000000000);
            tlen = sprintf(tbuf, "[%5lu.%06lu] ",
                            (unsigned long) t,
                            nanosec_rem / 1000);*/
            //下面一行为新增代码
            tlen = sprintf(tbuf,"[%12lu.%09lu] ",ts.tv_sec,ts.tv_nsec); 
            for (tp = tbuf; tp < tbuf + tlen; tp++)
                    emit_log_char(*tp);
            printed_len += tlen;
    }
 ...
}
蓝色部分为新增代码,绿色部分为注释掉的代码。这样打印出来的时间,就可以格式成可以直观显示的时间了,时间是以秒为单位,精确到纳秒级。如下面所示:
...
<6>[           0.360000023] 8021q: 802.1Q VLAN Support v1.8
<6>[           0.360000023] VFP support v0.3: implementor 41 architecture 3 part 30 variant c rev 0
<6>[  1395585751.500000001] goldfish_rtc goldfish_rtc: setting system clock to 2014-03-23 14:42:31 UTC (1395585751)
<6>[  1395585751.500000001] Freeing init memory: 304K
...
  
你可能注意到了,前面会有一段时间为0,这是因为系统刚启动,尚未设置时间,在设置系统时间之后,时间就正确了。说明:曾尝试用do_gettimeofday来获取时间,但直接造成系统不开机了,何解?是不是未设置系统时间之前,不能调用do_gettimeofday,会造成死锁?欢迎专家指导
Linux的时间在内核空间和用户空间是不一样的。time,ctime等熟悉的函数只能在用户空间使用,这里不再纠结了,在显示时,我们可能通过一些手段来解决掉。
注意:dmesg.c是用klogctl函数取内核日志的,该函数是在读取缓冲区中的日志,这样效率很高,但存在的问题是,如果内核日志的大小超出的缓冲区的限制,则老日志会被挤出,只留下最近的日志。dmesg.c中的默认大小的:128KB。但这个值可以通过内核编译选项配置:CONFIG_LOG_BUF_SHIFT,值为12到21,表示内核缓冲区最小为2的12次方(4KB),最大为2的21次方(2048KB)。

为了更方便的查看内核日志,我们可以修改dmesg,该文件在Andorid源码(非内核源码)的system/core/toolbox/dmesg.c,这个文件非常简单,修改很容易,这里不再赘述。

2.2 用户空间日志

这个日志就是用Logcat能够查看的日志了,Android(非内核)提供两种日志接口:C接口和Java接口。
a)C/C++语言接口
头文件:system/core/include/android/log.h
# cd /Volumes/android/source
# vi system/core/include/android/log.h
代码如下:
...
/*
 * Android log priority values, in ascending priority order.
 */
typedef enum android_LogPriority {
    ANDROID_LOG_UNKNOWN = 0,
    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
    ANDROID_LOG_VERBOSE,
    ANDROID_LOG_DEBUG,
    ANDROID_LOG_INFO,
    ANDROID_LOG_WARN,
    ANDROID_LOG_ERROR,
    ANDROID_LOG_FATAL,
    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
} android_LogPriority;

/*
 * Send a simple string to the log.
 */
int __android_log_write(int prio, const char *tag, const char *text);

/*
 * Send a formatted string to the log, used like printf(fmt,...)
 */
int __android_log_print(int prio, const char *tag,  const char *fmt, ...)
#if defined(__GNUC__)
    __attribute__ ((format(printf, 3, 4)))
#endif
    ;

/*
 * A variant of __android_log_print() that takes a va_list to list
 * additional parameters.
 */
int __android_log_vprint(int prio, const char *tag,
                         const char *fmt, va_list ap);

/*
 * Log an assertion failure and SIGTRAP the process to have a chance
 * to inspect it, if a debugger is attached. This uses the FATAL priority.
 */
void __android_log_assert(const char *cond, const char *tag,
                          const char *fmt, ...)
#if defined(__GNUC__)
    __attribute__ ((noreturn))
    __attribute__ ((format(printf, 3, 4)))
#endif
    ;
...
文件中定义了LOG级别android_LogPriority,并给出了几个函数来写日志:__android_log_write:写日志
__android_log_print:带格式化输出日志
__android_log_vprint:带格式化输出日志
网上有资料说,在system/core/include/cutils/log.h文件中做了相应的宏封装,但在我的源代码中,只有一句话:#include <log/log.h>,这什么情况?原来源码移至system/core/include/log/log.h了,这两个文件等价。这个头文件定义了几个常用的宏:
  • LOGV - 输出ANDROID_LOG_VERBOSE级别日志
  • LOGD - 输出ANDROID_LOG_DEBUG级别日志
  • LOGI - 输出ANDROID_LOG_INFO级别日志
  • LOGW - 输出ANDROID_LOG_WARN级别日志
  • LOGE - 输出ANDROID_LOG_ERROR级别日志
  • LOGF - 输出ANDROID_LOG_FATAL级别日志
  • LOGS - 输出ANDROID_LOG_SILENT级别日志

我们可以直接使用以上宏。用法举例:
#include <log/log.h>
...
LOGD("MyTag","My message!");//输出调试信息
b)Java接口
Java接口和C的宏定义类似,源文件:frameworks/base/core/java/android/util/Log.java中。这里不再具体分析,使用很简单。
import java.android.util.Log;
...
Log.d("MyTag","My message!");//输出调试信息
网上有些文章提到,修改android/system/core/logcat/logcat.cpp,使logcat工具能直接查看内核日志的信息,我尝试过,没有一个能用。其实,要做到也可以:
1. 不能使用klogctl,需要直接操作/proc/kmsg,原因上面已经有描述,因为klogctl只读取内核日志缓冲区中的日志。每次重新运行logcat命令,有可能得到的内核日志不一样,因为超过缓冲区后,超出部门的老日志会被挤出,也就无法显示了。
2. 需要处理两份日志(内核空间日志和用户空间日志)的时间排序,否则,显示的时间可能对不上,这样的话,即使能够同时显示两类日志也没有意义。要完成这个操作,基本完全改变了logcat的原有逻辑。
我自己也修改过代码,实现了相关同步显示的代码,最终,我放弃了这样的想法,愿意钻研的朋友可以自己尝试,但我建议你重新写一个logcat,这样也许还快些。

2014年3月21日星期五

CDT开发环境配置(MacOSX 10.9)

Mac提供的XCODE,可以调试C/C++程序,但XCODE对C/C++程序开发并不方便,重构等功能不能用,在编辑、重构方面还是Eclipse具备优势。所以,开发Android的C/C++程序,最好用Eclipse的CDT。
当然,XCODE5是需要安装的,但XCODE5用的调试工具不是gdb,而是lldb,而CDT用的是gdb,需要先安装gdb,在MacOSX用调试工具还需要代码签名,过程非常繁琐,这里将CDT的开发环境配置详细说明。

1. 安装GDB

用mac ports工具安装:
# sudo port install gdb
安装完成后,会提示你:
You will need to make sure
/System/Library/LaunchDaemons/com.apple.taskgated.plist has the '-p' option,
e.g.
            <key>ProgramArguments</key>
            <array>
                    <string>/usr/libexec/taskgated</string>
                    <string>-sp</string>
            </array>
修改这个文件,将红色部分替换为上面的文字,此文件需要管理员权限:
# sudo vi /System/Library/LaunchDaemons/com.apple.taskgated.plist
安装好的gdb的文件位置:/opt/local/bin/ggdb。注意,命令为ggdb。

2. 配置GDB

要gdb能够在Eclipse中正常调试,必须代码签名。

2.1 创建代码签名证书

运行 应用程序 -> 其他 -> 钥匙串访问。如下图:

取一个名称:gdbcert,证书类型选择:代码签名,勾选“让我覆盖这些默认值”,如下图:

一直按“继续”,到最后一屏,在钥匙串中选择“系统”,如下图:

点击创建,输入你的密码后,代码签名证书就创建了。如下图:

在“gdbcert”证书上按右键,选择“显示简介”,选择“总是信任”,如下图:

关闭时会要求输入密码。然后,退出“钥匙串访问”。

2.2 签名证书

先重启taskgated,在终端里,输入如下命令:
# killall taskgated
签名证书,需要启用root用户,进入 系统偏好设备 -> 用户与群组,如下图:


点击网络帐户服务器右边的“加入”按钮,出现下图:

点击“打开目录实用工具”,如下图:

 选择菜单项 编辑 -> 启用Root用户。再输入两次ROOT用户的密码就可以了。接下来就是签名了。在终端里:
# su root
# codesign -fs gdbc /opt/local/bin/ggdb
# sudo ln -s /opt/local/bin/ggdb /opt/local/bin/gdb
这样,ggdb调试程序的代码签名就完成了,可以在Eclipse里调试了。最后面是用了一个符号连接,方便直接使用gdb命令。

3. 调试C/C++程序

3.1 安装CDT

如果你没有安装CDT插件,请在Eclipse下面安装CDT,打开Eclipse,选择“Help”-> “Install New Software”,我的Eclipse版本是Kepler,如下图:
按照默认往下即可完成安装。

3.2 调试

用 New C++ Project 向导,新建一个工程。在工程项目上按右键,按 Debug As -> Debug Configurations,如下图:


配置好GDB的路径:/opt/local/bin/gdb。点击“Debug”即可调试程序。







2014年3月20日星期四

Android 入门 - 定制开机画面

本文并不准备介绍Android的开机画面的显示原理,只从实例的角度介绍如何快速定制Android开机画面。本文假定:
内核源代码目录:/Volumes/android-kernel/source/goldfish
Android源代码目录:/Volumes/android/source

一、开机画面的简单介绍

网上有许多资料都提到,Android设备的开机画面分为三个画面,即:Linux内核Logo,Android启动Logo,Android动态画面。经过研究分析,Android设备默认只显示Android动画,那么如何显示内核Logo和启动Logo呢?和两条编译配置有关:
  1. CONFIG_FRAMEBUFFER_CONSOLE:打开该配置选项,就可以显示Android启动Logo。但出现问题:默认情况下是Android文字和Android动画交替闪烁。
  2. CONFIG_LOGO:打开该配置选项,并且CONFIG_FRAMEBUFFER_CONSOLE也开启的情况下,可以显示内核Logo。此项无问题。
这样的情况下,制作出来的Android内核不只是在启动时出现闪烁,而且运行后也会么一直这么闪烁,本文后面将介绍如何解决这个问题。

二、定制内核Logo

内核Logo默认是一个Linux企鹅图标,该图标只支持224种颜色,图标格式为:ppm。在Android内核源代码中的路径为:drivers/video/logo/logo_linux_clut224.ppm。所以,我们只需要替换此图标,则重新编译即可。

2.1、安装netpbm工具

Mac OS X下没有直接生成PPM格式图片的工具,可用netpbm转换。要安装netpbm工具,请先安装MacPorts,本文不描述如何安装和使用MacPorts,可在网上搜索安装和使用教程。
# sudo port selfupdate
# sudo port install netpbm
sudo port selfupdtae是让MacPorts先自我更新,sudo port install netpbm安装netpbm。

2.2、PPM图片转换

将预先做好的图片(假定为:MyLinux.png),转换为logo_linux_clut224.ppm格式:
# pngtopnm MyLinux.png > logo_linux_clut.pnm
# pnmquant 224 logo_linux_clut.pnm > logo_linux_clut224.pnm
# pnmtoplainpnm logo_linux_clut224.pnm > logo_linux_clut224.ppm
# cp logo_linux_clut224.ppm /Volumes/android-kernel/source/goldfish/drivers/video/logo/

2.3、配置

许多教程中描述了使用make menuconfig来定制CONFIG_FRAMEBUFFER_CONSOLE和CONFIG_LOGO两个变量,这是一种方法,但在Mac环境中,make menuconfig不能正常运行,总是出现ncurses的相关库找不到,折腾了很久之后,放弃使用make menuconfig。
还有一种方法是使用make config,但这种方法要按N次回车,并仔细对比选项,太费事了。这里介绍一种快速的方法:
在编译内核的时候,我们使用了make goldfish_armv7_defconfig来配置的,只要将该默认配置修改一下,加入上面两个配置项至这个默认配置中,重新运行即可。命令如下:
# cd /Volumes/android-kernel/source/goldfish
# vi arch/arm/configs/goldfish_armv7_defconfig
在文件末尾加入:
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_LOGO=y

按 wq 保存退出。

2.4、去掉光标

开启FRAME_BUFFER_CONSOLE后,会显示光标,需要修改源码解决,网上的方案比较复杂,经研究,这里介绍一个简单方法:
# vi drivers/video/console/softcursor.c
找到soft_cursor(...)函数,只留最后一句:return 0; 其他行注释掉:
int soft_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
/*      struct fbcon_ops *ops = info->fbcon_par;
        unsigned int scan_align = info->pixmap.scan_align - 1;
        unsigned int buf_align = info->pixmap.buf_align - 1;
        unsigned int i, size, dsize, s_pitch, d_pitch;
        struct fb_image *image;
        u8 *src, *dst;
...
        fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, image->height);
        image->data = dst;
        info->fbops->fb_imageblit(info, image);
*/      return 0;
}

2.5、编译

# export PATH=/Volumes/android/source/prebuilts/gcc/darwin-x86/arm/arm-eabi-4.7/bin/:$PATH
# make goldfish_armv7_defconfig
# make -j16
编译完成后,会在最后显示如下内容:
...
  OBJCOPY arch/arm/boot/zImage
  Kernel: arch/arm/boot/zImage is ready

2.6、运行

运行如下指令:
# emulator -kernel arch/arm/boot/zImage
运行可以成功显示开机Logo,但出现Android文字和Android动画交替闪烁的情况。

这个问题是由于启用CONFIG_FRAMEBUFFER_CONSOLE编译项后,init进程如果找不到“initlogo.rle”,就会进入text console模式,而后面的第三屏为动画的图形模式,从而造成两个界面交替闪烁的情况。分析过程如下:
先进入Android源代码根目录,注意不是内核目录。
# vi /Volumes/android/source/system/core/init/init.c
找到console_init_action函数,这个是console启动函数,代码如下:
...
static int console_init_action(int nargs, char **args)
{
    int fd;

    if (console[0]) {
        snprintf(console_name, sizeof(console_name), "/dev/%s", console);
    }

    fd = open(console_name, O_RDWR);
    if (fd >= 0)
        have_console = 1;
    close(fd);

    if( load_565rle_image(INIT_IMAGE_FILE) ) {
        fd = open("/dev/tty0", O_WRONLY);
        if (fd >= 0) {
            const char *msg;
                msg = "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"  // console is 40 cols x 30 lines
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "             A N D R O I D ";
            write(fd, msg, strlen(msg));
            close(fd);
        }
    }
    return 0;
}
可以看到console_init_action函数会调用load_565rle_image函数来装入initlogo.rle文件,load_565rle_image函数在system/core/init/logo.c中。打开后的代码如下:
...
/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */

int load_565rle_image(char *fn)
{
    struct FB fb;
    struct stat s;
    unsigned short *data, *bits, *ptr;
    unsigned count, max;
    int fd;

    if (vt_set_mode(1))
        return -1;

    fd = open(fn, O_RDONLY);
    if (fd < 0) {
        ERROR("cannot open '%s'\n", fn);
        goto fail_restore_text;
    }

...

    munmap(data, s.st_size);
    fb_update(&fb);
    fb_close(&fb);
    close(fd);
    unlink(fn);
    return 0;

fail_unmap_data:
    munmap(data, s.st_size);
fail_close_file:
    close(fd);
fail_restore_text:
    vt_set_mode(0);
    return -1;
}
由上面的代码可以看出,当装入initlogo.rle失败后,就走到fail_restore_text了,然后调用vt_set_mode(0),恢复成文本模式了。
现在问题找到了,是因为找不到initlogo.rle,系统回到text模式了。解决问题的方案有两个:
  1. 制作initlogo.rle,即Android启动静态画面,见后文。
  2. 如果我不想要initlogo.rle怎么办呢?很简单,修改源代码。注释掉上面代码中的fail_restore_text: 后面一句vt_set_mode(0);这样即使找不到initlogo.rle,也会进入图形模式,重新make,问题解决。
    # cd /Volumes/android/source
    # vi system/core/init/logo.c
    
    ...
    /* 565RLE image format: [count(2 bytes), rle(2 bytes)] */
    
    int load_565rle_image(char *fn)
    {
    ...
        munmap(data, s.st_size);
        fb_update(&fb);
        fb_close(&fb);
        close(fd);
        unlink(fn);
        return 0;
    
    fail_unmap_data:
        munmap(data, s.st_size);
    fail_close_file:
        close(fd);
    fail_restore_text:
        /*vt_set_mode(0)*/;
        return -1;
    }
    # make -j16

三、定制Android启动Logo

Android启动时的静态画面为initlogo.rle文件,具体制作过程如下:

3.1 安装ImageMagick工具

此工具用于将png图片转换成raw文件,安装过程如下:
# sudo port install ImageMagick
安装完成后,convert指令将可以使用。

3.2 编译to2565.c,生成rgb2565工具

to565.c是Android自带的工具,用于将raw文件转换为rle文件。编译过程如下:
# cd /Volumes/android/source/build/tools/rgb2565/
# gcc -O2 -Wall  -Wno-unused-parameter -o rgb2565 to565.c

3.3 制作initlogo.rle文件

先用Photoshop等软件制作一张与手机分辨率相同大小的图片(模拟器的分辨率为320*480),假定保存的文件名为:initlogo.png。为了看到运行变化,initlogo.png不要和内核Logo的图片一样。
# cd initlogo.png目录
# convert  -depth 8  initlogo.png  rgb:initlogo.raw
# /Volumes/android/source/build/tools/rgb2565/rgb2565 -rle  <initlogo.raw>  initlogo.rle
# cp initlogo.rle /Volumes/android/source/out/target/product/generic/root/

3.4 重新编译

为确保成功,先删除ramdisk.img。
# cd /Volumes/android/source
# rm out/target/product/generic/ramdisk.img
# make -j16
编译完成后,运行。
# cd /Volumes/android-kernel/source/golefish
# emulator -kernel arch/arm/boot/zImage
这时,你应该看到先启动内核Logo,再启动此静态画面,最后启动Android动画了。

四、定制Android开机动画

其实,默认情况下,前面的内核Logo和Andriod的启动Logo是看不见的,如果没有特别需要,是不需要定制的。但Android开机动画默认情况下是能看见的,因此,此动画定制的可能性要大的多。而且,定制开机动画也要容易,没有前面那么多复杂的步骤,这应该是谷歌考虑到此屏幕基本都要被定制才弄成这么简单吧。

4.1 仿Android自带的动画

即Android默认的动画,此动画由两张图片组成:android-logo-mask.png,android-logo-shine.png,mask是由空心的android文字组成,即一个黑色的块,中间用Android文字挖空,这张图片是作为遮罩放在前端。shine则是一个发光的图片,放在mask的后面,通过循环移动shine,就成为Android发光动画。
用Photoshop修改这两张图片,保存在frameworks/base/core/res/assets/images目录下,文件名不变,再make即可。这个涉及资源重新编译,编译过程比较长,耐心等待。

4.2 帧动画

帧动画是指由多帧图片组成的动画。这个设置是最简单的,以致于手机ROOT后,就可以在手机里直接下载一个bootanimation.zip,替换掉/system/media/bootanimation.zip,就能换掉这个开机动画。
bootanimation.zip可以放在/system/media目录或者放在/data/local目录下,/data/local目录下的优先使用。这两个目录在播放上也稍有区别,/system/media可以无限播放,/data/local只能播放10秒。

bootanimation.zip解压后的结构如下:
- desc.txt
- part0
  - 0000.png
  - 0001.png
  - ...
- part1
  - 0000.png
  - 0001.png
  - ...
part0,part1是两个存放动画帧系列图片的目录。desc.txt是一个文本文件,文件的内容如下:
320    480    30
p      1      0      part0
p      0      0      part1
第一行:320  480 30 表示播放分辨率为320*480,30表示每秒播放30帧图片。
第二行:p 1 0 part0  p为标志符,表示将part0目录里的图片,播放一次,间隔时间为0秒。
第三行:p 0 0 part1 p为标志符,表示将part1目录里的图片,重复播放,间隔时间为0秒。

上面的目录和文件准备好后,执行以下指令:
# cd bootanimation所在目录
# zip -0 -r ../bootanimation.zip ./*
# cp ../bootanimation.zip /Volumes/android/source/out/target/product/generic/system/media/
# make snod
先CD进入bootanimation目录后,再执行zip压缩指令,保证不会将bootanimation目录打进包内。-0 表示只存储,不压缩,-r 表示包括子目录
将打好的bootanimation.zip包,拷贝到/system/media目录,再用make snod重新生成。再运行查看效果。
# cd /Volumes/android-kernel/source/golefish
# emulator -kernel arch/arm/boot/zImage
下面是(自上而下依次为内核Logo,启动Logo,启动动画):






2014年3月18日星期二

Android 入门 - 编译内核

Android的源代码中并没有包含内核的源代码,本文介绍在MacOSX下如何下载编译Android的内核。在编译内核之前,请先了解如何编译Android源码,参看博文: Android 入门 - 编译源码(MacOSX)

一、准备工作

1.1、安装最新的XCODE

XCODE是MacOSX下的开发工具,安装之后,开发需要的各种环境基本就具备了,不需要再各自去下载。如:git,gcc等等。

1.2、安装GoAgent

googlesource站点在国内不能访问,需要翻墙才能访问。建议安装GoAgent来翻墙。
安装步骤:略,可从网上搜索解决。

1.3、设置代理

# export http_proxy=localhost:8087
# export https_proxy=localhost:8087
localhost:8087为你的GoAgent的代理地址及端口。

1.4、创建大小写敏感的磁盘映象

  1. 创建一个大小写敏感的磁盘映象,100G,映象根据实际大小占据磁盘空间,所以,大点没有关系,命令:
    # hdiutil create -type SPARSE -fs 'Case-sensitive Journaled HFS+' -size 100g ~/android-kernel.dmg
  2. 挂载磁盘,可在Finder里双击“个人”主目录的android-kernel.dmg.sparseimage,即可挂载一个UNNAME的磁盘,按“右键”更名为android。实际路径为:/Volumes/android-kernel。

二、下载内核源码

2.1、获取内核版本信息

有两种方法获取Android内核版本,都需要运行模拟器:
# emulator -cpu-delay 0
a). 在模拟器里查看内核版本
进入 应用 -> 设置  -> 关于手机  -> 内核版本,即可查看到此Android的内核版本。
b). 用adb shell 连接模拟器命令查看
# adb shell
# cd proc
# cat version
得到Android 4.4的内核版本为“Linux version 3.4.0-gd853d22”

2.2、下载源码

# cd /Volumes/android-kernel/
# mkdir source
# cd source
# git clone https://android.googlesource.com/kernel/goldfish.git

2.3、查看源码版本

# cd goldfish
# git branch -a
结果如下:
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/android-3.10
  remotes/origin/android-goldfish-2.6.29
  remotes/origin/android-goldfish-3.10
  remotes/origin/android-goldfish-3.4
  remotes/origin/linux-goldfish-3.0-wip
  remotes/origin/master

2.4、Check out 3.4版本

# git checkout remotes/origin/android-goldfish-3.4 

三、编译

3.1、初始化配置

# export PATH=/Volumes/android/source/prebuilts/gcc/darwin-x86/arm/arm-eabi-4.7/bin/:$PATH
# vi Makefile
修改如下内容:
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
改成:
ARCH ?=arm
CROSS_COMPILE ?=arm-eabi-

3.2、除错

Mac下编译会出现一些错误:
# cp /Volumes/android/source/external/elfutils/libelf/elf.h scripts/mod
# vi scripts/mod/mk_elfconfig.c
将 include <elf.h> 修改为 include "elf.h"。

# vi scripts/mod/modpost.h
将 include <elf.h> 修改为 include "elf.h"。

3.3、编译

# make goldfish_armv7_defconfig
# make -j16
编译完成后,会在最后显示如下内容:
...
  OBJCOPY arch/arm/boot/zImage
  Kernel: arch/arm/boot/zImage is ready

4、运行

运行如下指令:
# emulator -kernel arch/arm/boot/zImage
运行结果如下图:





2014年3月14日星期五

Android 入门 - 定制ROM(HelloAndroid)

上一篇笔记中已经说明如何在MacOSX环境下下载、编译、运行Android4.4,本篇将说明如何在Android系统中加入自己的应用,本文给出的范例:HelloAndroid。

Eclipse 版本为 Kepler。

一、新建HelloAndroid项目

下面的过程为开发一个普通的Android应用的基本过程,在此之前,你需要安装Eclipse和相应的ADT插件,本文不再描述。假定你的Android源代码在/Volumes/android/source目录。

1.1、新建项目

  1. 假如出现“Welcome”屏幕,关闭它
  2. File -> New -> Android Application Project
  3. 输入Application Name 为“HelloAndroid”,点击“Finish”。

1.2、运行此项目

在刚创建的工程“HelloAndroid”上,按右键,选择“Debug As”-> Android Project。启动模拟器运行,看看运行效果。

二、把你的应用放到Android源码

做好应用之后,我们可以把它放到Android源码中,这样就可以制作我们自己的Rom,包含自己的应用。

a). 在Android源码中,创建应用目录:
# cd /Volumes/android/source
# mkdir packages/apps/HelloAndroid

b). 创建Android.mk文件,可以pacages/apps/的一些源码中找到样本编辑:
# vi packages/apps/HelloAndroid/Android.mk
贴入以下内容:
# Copyright 2014 The Android Open Source Project

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_JAVA_LIBRARIES :=
LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := HelloAndroid
LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE)

保存退出!
要注意LOCAL_PACKAGE_NAME,应该为你的应用名称,不能乱写。

c). 拷贝应用源代码

HelloAndroid项目的目录结构应该如下图:


把项目下的res、src和AndroidManifest.xml三个文件拷贝到 packages/apps/HelloAndroid目录下。

d). 编译应用
# source build/envsetup.sh
# lunch 1
# mmm packages/apps/HelloAndroid
会出现一些警告,但没有关系。

e). 重新生成ROM
# make snod
这样会重新生成ROM,out/target/product/generic目录下的system.img, userdata.img, ramdisk.img等文件都会重新生成。

f). 运行模拟器
# emulator
这样可以在模拟器里看到HelloAndroid已经在应用目录里了。

这样,我们就知道如何新加自己的应用,当然,要改Android原生的应用,也很容易,但需要先详细了解Android的原生应用结构与原理,修改后,直接执行上面的d,e,f几步就OK了。如何开发定制ROM的入门就算完成了。


2014年3月12日星期三

Android 入门 - 编译源码(MacOSX)

此笔记根据安卓源码官方教程(http://source.android.com/source/initializing.html)记录。我准备编译的Android版本为4.4。
硬件环境为:Macbook pro 2013最新版本,操作系统为OSX10.9,内存:8GB,硬盘500GB。

一、准备工作

1.1、安装最新的XCODE

XCODE是MacOSX下的开发工具,安装之后,开发需要的各种环境基本就具备了,不需要再各自去下载。如:git,gcc等等。

1.2、安装GoAgent

googlesource站点在国内不能访问,需要翻墙才能访问。建议安装GoAgent来翻墙。
安装步骤:略,可从网上搜索解决。

1.3、设置代理

# export http_proxy=localhost:8087
# export https_proxy=localhost:8087
在每次运行repo sync同步之前,运行上面两句指令,不建议设置用户的环境变量,可能影响其他程序的运行。

1.4、创建大小写敏感的磁盘映象

  1. 创建一个大小写敏感的磁盘映象,100G,映象根据实际大小占据磁盘空间,所以,大点没有关系,命令:
    # hdiutil create -type SPARSE -fs 'Case-sensitive Journaled HFS+' -size 100g ~/android.dmg
  2. 挂载磁盘,可在Finder里双击“个人”主目录的android.dmg.sparseimage,即可挂载一个UNNAME的磁盘,按“右键”更名为android。实际路径为:/Volumes/android。

1.5、下载Repo

# mkdir ~/bin
# curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
# chmod a+x ~/bin/repo
# PATH=$PATH:~/bin

二、下载源代码

2.1、创建工作目录

# cd /Volumes/android
# mkdir source
# cd source

2.2、初始化代码库

# repo init -u https://android.googlesource.com/platform/manifest

指令某个版本库(如:android-4.2.2_r1),则用:
# repo init -u https://android.googlesource.com/platform/manifest -b android-4.2.2_r1

2.3、下载源码树

# repo sync -j16
repo支持断点续点,如果失败了,重新运行上述指令,直至成功即可。

三、编译

3.1、初始化

# cd /Volumns/android/source
# source build/envsetup.sh

3.2、选择一个目标

# lunch aosp_arm-eng

3.3、编译

# make -j16
编译完成后,ROM目标代码保存在/Volumes/android/source/out/target/product/generic/目录下面,主要有三个img文件:System.img,userdata.img,ramdisk.img。

4、运行

编译完后,一些指令将被自动加入PATH路径中。如果你新开启了终端,可以在out/host/darwin-x86/bin下面找到这些命令,你可以把这个目录加到PATH中。如果新建终端,你需要配置以下环境变量才能运行:
# export PATH=$PATH:/Volumes/android/source/out/host/darwin-x86/bin
# export ANDROID_PRODUCT_OUT=/Volumes/android/source/out/target/product/generic
# export ANDROID_BUILD_TOP=/Volumes/android/source 
你可以刷机或运行在模拟器中。

4.1 刷机

连接好手机后,运行如下指令:


# adb reboot bootloader
# fastboot flashall -w

4.2 运行模拟器

# emulator
到此,Android的源码下载、编译、运行就完成了,可以看出,在MacOSX下编译Android要比Ubuntu下简单方便。