工作,学习,生活,这里将会有一些记录. 备用域名:http://meisw.51099.com 注册 | 登陆
浏览模式: 标准 | 列表2011年08月的文章

反编译与回编译android的framework.jar(其他jar文件也可参考)

最近对android 2.3.3的framework.jar进行了一些反编译和回编译的操作,写下来备忘。

一、framework.jar反编译为smali文件

1、下载smali-1.2.6.jarbaksmali-1.2.6.jar这两个工具(下载地址:http://code.google.com/p/smali/downloads/list

2、将framework.jar中的classes.dex解压出来(好像不解压,直接用framework.jar也行)

3、使用baksmali.jar对classes.dex进行反编译(前提是安装了jdk,并且设置好了环境变量),执行命令:

java -jar baksmali-1.2.6.jar classes.dex -o out/

其中classes.dex是要反编译的文件,out/是要把反编译后的文件存放到的文件夹,如果不是在当前目录下,那么baksmali-1.2.6.jar还要加上路径

这样就OK了,在out文件夹中可以看到一堆扩展名为.smali的文件,用记事本就可以打开它们,从中可以窥到一些信息。但是与.java文件还是有一些不同,我也不太清楚这是什么结构。

二、smali文件回编译为classes.dex

1、一条命令就OK了:java -jar smali-1.2.6.jar out/ -o classes.dex

2、再把编译好的classes.dex放回到framework.jar中就行了(可以使用winrar、winzip之类的工具作为辅助)。

三、framework.jar反编译为.class和.java文件

1、下载以下工具:

(1)dex2jar(http://code.google.com/p/dex2jar/

(2)xjad(http://www.skycn.com/soft/41898.html)或jd-gui(http://java.decompiler.free.fr/?q=jdgui

2、使用dex2jar对framework.jar进行转换,执行命令:dex2jar.bat framework.jar   将会生成一个framework.jar.dex2jar.jar

3、直接对该jar文件解压,可以看到里面都是.class文件了

4、如果还需要转换成.java文件,可以使用xjad或jd-gui,均可将class文件变为java文件。

注:dex2jar工具也可以处理.dex文件,因此也可以不直接处理framework.jar。而是先将framework.jar解压,生成classes.dex后再处理也行。

linux 操作系统下目录创建和文件创建的限制

我们经常程序在生产环境上上线好几天了,突然不工作了,反复查找原因,发现在Linux操作系统下无法创建目录或文件了,那一般有哪些原因会导致出现这种情况呢?

1.磁盘满
最常出现的问题就是磁盘满了,第一时间查看这个原因,在linux系统下执行df命令,Use%这一列是否有100%的磁盘。

2.文件名称长度限制
linux系统下ext3文件系统内给文件/目录命名,最长只能支持127个中文字符,英文则可以支持255个字符,包含完整路径名称及目录(/)的完整文件名为4096个字符。
测试代码:

Java代码  
  1. LENTH=`for i in {1..255};do for x in a;do echo -n $x;done;done`   
  2. touch $LENTH  


当增加到256时,touch报错,File name too long

3.目录数量的限制
ext3文件系统下一级子目录是有数量限制的,一般是31998(个),为什么是这个数字,我也不清楚,网上有说是Linux为了cpu的搜索效率而规定的,要想改变数目大概要重新编译内核.
测试代码:

 
  1. for i in {1..32000};do mkdir $i;done    
  2. mkdir: cannot create directory `31999': Too many links   
  3. mkdir: cannot create directory `32000': Too many links  



4.文件个数限制
目录下对于文件个数没有限制,文件个数的限制主要取决于磁盘文件系统的inode数。
df -i或者使用tune2fs -l /dev/sdaX或者dumpe2fs -h /dev/sdaX查看可用inode数,后两个命令输出结果是一样的,但是跟df所得出的可用inode数会有些误差,至今不明白什么原因。

5.打开文件数限制(文件句柄、文件描述符)
ulimit -n 65535设置,或者/etc/security/limit.conf里设置用户打开文件数、进程数、CPU等

----------------------------------------

for i in `ls`;do echo $i;cd $i;ls -l | wc -l;cd ..;done

find ./ -type d -empty -exec rm -fr {} \;

android触摸屏坐标手工校准/调整成功

1.简述
android 的坐标转换处理:
This implementation is a linear transformation using 7 parameters
(a, b, c, d, e, f and s) to transform the device coordinates (Xd, Yd)
into screen coordinates (Xs, Ys) using the following equations:

s*Xs = a*Xd + b*Yd + c
s*Ys = d*Xd + e*Yd + f

Xs,Ys:LCD坐标
Xd,Yd:触摸屏坐标

在编译好了的ANDROID根文件系统的system/etc/pointercal这个文件内,存放着7个数,
这7个数就是对应上面公式的a,b,c,d,e,f,s
比如我原来的:(如果表格没对齐,请拷到记事本里面看)
+----+-----+--------+------+--+--------+----+
| a | b | c     | d    |e |   f    | s |
+----+-----+--------+------+--+--------+----+
|-411|37818|-3636780|-51325|39|47065584|6553|
+----+-----+--------+------+--+--------+----+

2.处理说明:
“system/etc/pointercal”这个文件是被java程序读取使用的,文件目录:
f rameworks/base/services/java/com/android/server/InputDevice.java
---注:我用的是koolu的源码(http://git.koolu.org/),官方的源码请自行搜索。
该文件的第32行定义了:static final String CALIBRATION_FILE = "/system/etc/pointercal";
这个CALIBRATION_FILE变量在第237行被使用于打开该文件:
FileInputStream is = new FileInputStream(CALIBRATION_FILE);
后面的代码就是从这个文件里读取那7个数据,用于上层函数的坐标转换。
所以只要根据该公式通过手工计算出那7个值,就可以准确的进行触摸操作了。

3.计算
计算前需要取得4个触摸屏的坐标,我们取LCD 4个对角的坐标,因为只有这4个坐标知道确切的LCD坐标。
要取坐标必须打开内核中触摸屏的调试代码,启动后在console上使用dmesg命令来跟踪取得。
我是在一个角上用笔点一下,再用dmesg调出记录,然后记录下来。如下是我的320x240屏记录的结果:
坐标轴里小括号()里面的是调试信息给的触摸屏坐标,中括号[]里的是对应的LCD坐标。

x坐标
/|\
|(X:870, Y:140)   (X:870, Y:890)
| [320,0]          [320,240]
|
|
|
+-------------------------------> y坐标
(X:120, Y:140)    (X:120, Y:890)
     [0,0]             [0,240]

*LCD 分辨率:320 x 240 ,也是LCD坐标的最大值
             x     y

设定s=65536
将那4个坐标代入那个公式,可以得出8个方程组

        0 = a*120 + b*140 + c
        0 = d*120 + e*140 + f
        0 = a*120 + b*890 + c
65536*240 = d*120 + e*890 + f
65536*320 = a*870 + b*140 + c
        0 = d*870 + e*140 + f
65536*320 = a*870 + b*890 + c
65536*240 = d*870 + e*890 + f
解方程组后就可得:
a = 0
b = 20971
c = -2935940
d = 27962
e = 0
f = -3355440
s = 65535
然后用工具打开andriod的“system/etc/pointercal”文件,把这几个数输进去,用空格分开,
注意不要改文件结尾的两个字节0x00 0x0A,我是用Ghex工具打开的,在右边窗口中进行输入,在编辑里可以切换插入和覆盖模式。
此时启动试试。

////////////////////////////
我的屏到这个步骤后触摸操作的左右变成了上下,上下变成了左右操作,
处理方法:把a,b,c值和d,e,f值分别对调。
再启动试试。。
还是不行,左右是正确的,上下是反的。
处理方法:
把Y坐标再反向的公式:
Ys‘ = 320 - Ys = 320 - (a*Xd + b*Yd + c)/s
重新计算a,b,c的值,就OK了。

现在我点击计算器的按钮等操作都很准了,指哪打哪。

前面发的那个视频里的坐标是在内核里调的坐标,不是很准。

这是手工调的方法,如果要用程序校准的话可以用tslib,有时间再玩下。

svn强制要求提交注释--pre-commit钩子

不少开发员提交修改的时候都不写注释,导致查看历史时很费劲,也不太符合规范。有的公司要求每次提交修改时都写上bug号或者任务描述,那么如何在工具上防止开发员们不写注释呢?   利用svn的pre-commit钩子可简单实现此要求。 进入仓库project1/hooks目录
-
不少开发员提交修改的时候都不写注释,导致查看历史时很费劲,也不太符合规范。有的公司要求每次提交修改时都写上bug号或者任务描述,那么如何在工具上防止开发员们不写注释呢?

  利用svn的pre-commit钩子可简单实现此要求。

进入仓库project1/hooks目录,找到pre-commit.tmpl文件,重命名,去掉后缀.tmpl。
编辑pre-commit文件:

将:
$SVNLOOK log -t "$TXN" "$REPOS" |
   grep "[a-zA-Z0-9]" > /dev/null || exit 1
commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1
这三行注释掉(前面加#符号),
在此位置添加如下几行:

LOGMSG=`$SVNLOOK log -t "$TXN" "$REPOS" | grep "[a-zA-Z0-9]" | wc -c`
if [ "$LOGMSG" -lt 5 ];#要求注释不能少于5个字符,您可自定义
then
  echo -e "nLog message cann't be empty! you must input more than 5 chars as comment!." 1>&2
  exit 1
fi

保存,退出。
给pre-commit添加可执行权限:
chmod +x pre-commit

配置结束,可以使用了。

Android 开机画面

Android 开机会出现3个画面:
a.  Bootloader启动,出现Android机器人图像;
b.  Android平台启动初始化,出现"A N D R I O D"文字字样画面;
c.  Android平台图形系统启动,出现含闪动的ANDROID字样的动画图片(start)。

 

a. Bootloader显示的Android机器人图像

 

b. 开机文字("A N D R I O D")

 

Android 系统启动后,读取 /initlogo.rle(一张565 rle压缩的位图),如果读取成功,测在/dev/graphics/fb0显示Logo图片;如果读取失败,则将/dev/tty0设为TEXT模式,并打开/dev/tty0,输出文本“A N D R I O D”字样。

相关代码:

/system/core/init/init.c
/system/core/init/init.h
/system/core/init/init.rc
/system/core/init/logo.c

*.rle文件的制作步骤:
1. 使用GIMP或者Advanced Batch Converter软件,将图象转换为RAW格式;
2. 使用android自带的rgb2565工具,将RAW格式文件转换为RLE格式(如:rgb2565 -rle < initlogo.raw > initlogo.rle)。

 

c. Android平台图形系统启动

 

Android的系统登录动画类似于Windows系统的滚动条,是由前景和背景两张PNG图片组成。前景图片(android-logo-mask.png)上的Android文字部分镂空,背景图片(android-logo-shine.png)则是简单的纹理。系统登录时,前景图片在最上层显示,程序代码控制背景图片连续滚动,透过前景图片文字镂空部分滚动显示背景纹理,从而实现动画效果。

相关代码:
frameworks/base/cmds/bootanimation/BootAnimation.h
frameworks/base/cmds/bootanimation/BootAnimation.cpp

frameworks/base/core/res/assets/images/android-logo-mask.png
Android默认的前景图片,文字部分镂空,大小256×64
frameworks/base/core/res/assets/images/ android-logo-shine.png
Android默认的背景图片,有动感效果,大小512×64

修改Android开机画面

Android系统开机动画包括两部分:
开机显示的 ANDROID 文字;
ANDROID发光动画。
这篇文章说的开机动画是第一种,下面开始正文!

1. 制作当前屏幕像素的图片(模拟器默认为320*480)
使用PS制作一张320*480的图片,保存时选“保存为 Web 所用格式”,然后在弹开的窗口上,“预设”项选择“PNG-24”,保存为android_logo.png
注:好像只支持png-24,其他格式生成的rle文件显示不正常,有兴趣大家可以再验证一下。

2. 将图片转换为raw格式
使用linux下的ImageMagick自带的convert命令,进行raw格式转换,命令为:
  convert -depth 8 android_logo.png rgb:android_logo.raw
注:ubuntu 10.04 默认已经安装ImgageMagick工具,如果当前系统没有安装,可以执行下面的命令安装:
  sudo apt-get install imagemagick

3. 将raw格式转化为rle文件
需要用到android编译后的rgb2565工具,在android/out/host/linux-x86/bin目录下(android为当前源码所在目录),转换命令如下:
rgb2565 -rle < android_logo.raw > initlogo.rle
到目前为止,启动需要显示的图像已经做好了,就是initlogo.rle,注意文件名必须是这个,如果想改文件名,需要修改android/system/core/init/init.h中的宏:
#define INIT_IMAGE_FILE "/initlogo.rle"
============================================================================================

下面需要将initlogo.rle加入的android文件系统中
4. 找到ramdisk.img文件(android/out/target/product/generic/ramdisk.img),将文件名改为ramdisk.img.gz,然后使用下面的命令解压:
gunzip ramdisk.img.gz
解压后得到ramdisk.img,可能有人要问,怎么文件名又改回去了?其实不然,使用file ramdisk.img查看一下就知道了:
解压前:ramdisk.img: gzip compressed data, from Unix
解压后:ramdisk.img: ASCII cpio archive (SVR4 with no CRC)
跑题了,还是说正事儿。

5. 使用cpio提取文件:
新建一个temp目录:
mkdir temp
cd temp
cpio -i -F ../ramdisk.img

6. 导出文件列表:
cpio -i -t -F ../ramdisk.img > list      
注:list是一个文本文件,里面存储了ramdisk.img的文件结构,我们需要在这个文件中加入initlogo.rle这一行,修改后的文件如下:
data
default.prop
dev
init
init.goldfish.rc
init.rc
initlogo.rle
proc
sbin
sbin/adbd
sys
system

7. 生成ramdisk.img
cpio -o -H newc -O ramdisk.img < list
注:根据list文件的描述,生成ramdisk.img文件

8. 用ramdisk.img覆盖sdk目录下的ramdisk.img(android-sdk-windows/platforms/android-2.1/images/ramdisk.img),最好先备份一下。

9. 启动模拟器,就可以看到我们自己做的的开机界面了。
/////////////////////////
开机图片设置 的下载地址为:http://docs.google.com/leaf?id=0 ... TIzOGQ5OWQ3&amp;hl=en

init 文件下载地址 http://docs.google.com/leaf?id=0 ... 2RiYjZjNjM2&amp;hl=en

split_bootimg.pl 下载地址:http://android-dls.com/files/linux/split_bootimg.zip

T卡文件下载地址:http://docs.google.com/leaf?id=0 ... GVjYzVhMjg4&amp;hl=en


开机图片设置 软件原理:
     根据android 系统 开机LOGO和开机动画的存放路径:/initlogo.rle 和 /data/local/bootanimation.zip 在init.rc 中 建立两个链接:
/initlogo.rle ->/data/data/com.android.e139.gallery/initlogo.rle 和 /data/local/bootanimation.zip ->/data/data/com.android.e139.gallery/bootanimation.zip 
来完成开机LOGO和开机动画的动态设定

安装要求:

1.本apk文件只能安装在android 2.1 或 2.0的操作系统 中
2.必须修改android 根目录下面的两个文件:init.rc 和 init 功能才能实现设置开机LOGO和开机动画的功能


修改init.rc 和 init 的方法:

1.修改 init.rc 和init文件需要修改手机中的 boot.img
导出方法:
cat /dev/mtd/mtd2 > /sdcard/root.img

然后

adb pull /sdcard/root.img ./

这样就把root.img拷贝到本地目录下了。

boot.img的组成结构是

+—————–+
| boot header | 1 page
+—————–+
| kernel | n pages
+—————–+
| ramdisk | m pages
+—————–+
| second stage | o pages
+—————–+


那我们要做的就是把这几个部分分别分离出来

我们现在先进行镜像分离。用命令

./split_bootimg.pl boot.img

 

成功执行后,可以得到两个文件,一个是boot.img-kernel,另一个是boot.img-ramdisk.gz。我们这里以修改ramdisk为例,所以将其解包

mkdir ramdisk

cd ramdisk

gzip -dc ../boot.img-ramdisk.gz | cpio -i

cd ..

2.进入ramdisk 修改init.rc 在init.rc 中增加:

on logo-init

    mkdir /data 0775 system system

# We chown/chmod /data again so because mount is run as root + defaults
    mount yaffs2 mtd@userdata /data nosuid nodev
    chown system system /data
    #for other user can read this dir
    chmod 0775 /data

    symlink /data/data/com.android.e139.gallery/initlogo.rle /initlogo.rle

on early-boot
# copy file   
    symlink /data/data/com.android.e139.gallery/bootanimation.zip /data/local/bootanimation.zip
    chmod 0666 /data/local/bootanimation.zip

再将已经下载的init文件覆盖到目录下面的init文件
3.生成新img 回到ramdisk 的上一级目录后执行:
mkbootfs ./ramdisk | gzip > ramdisk-new.gz mkbootimg --cmdline 'no_console_suspend=1 console=null' --kernel boot.img-kernel --ramdisk ramdisk-new.gz -o boot-new.img
就会重新生成 boot-new.img
3.刷入新img

生成了新的img后,我们如何在系统上应用我们的新boot.img呢?首先,把img文件放到sdcard上去

adb push ./boot_new.img /sdcard

然后执行这两个操作

cat /dev/zero > /dev/mtd/mtd2
flash_image boot /sdcard/mynewimage.img

执行第一个操作时,可能会提示你

write: No space left on device

这个信息没关系,不用管它。两个命令都执行后,最后adb shell reboot即可。如果重启顺利,那么恭喜你,你刷成功了.


安装步骤:
1.按上面的步骤修改手机中的init.rc 和init文件
2.把下载的T卡文件放到T卡中
3.安装 开机图片设置.apk文件

修改boot.img的方法参考:http://www.kunli.info/2009/09/14/how-to-modify-ramdisk-android/ 和
http://android-dls.com/wiki/index.php?title=HOWTO:_Unpack,_Edit,_and_Re-Pack_Boot_Images

关于boot.img和recovery.img的编辑和修改( 转)

以下是偶翻译的关于boot.img和recovery.img的编辑和修改方面的文章,希望能够为感兴趣的朋友节约一些看资料的时间。感谢本文的作者:Alansj, DarkriftX, RyeBrye, Will, Try OP9, Tonyb486, Timmmm, Lxrose还有好多不知名的作者们在wiki上的不懈努力。

如何解包/编辑/大包boot.img文件

很多人用自己的方式解决了boot.img的解包/编辑/打包的问题,有人要求我来写一篇关于boot和recovery映像的文件结构和如何对其编辑的文章,于是就有了下面这篇文章。

目录
1、背景知识
2、boot和recovery映像的文件结构
3、对映像文件进行解包、编辑、打包的常规方法
3.1、另一种解包、编辑、打包的方法
4、将新的映像刷回到手机
5、解包、编辑、打包为我们带来了什么
6、本文讲的内容与使用update.zip刷机包不是一码事

正文

1、背景知识

Android手机的文件系统有许多存储器组成,以下是在adb shell下面的输出:
#cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00040000 00020000 "misc"
mtd1: 00500000 00020000 "recovery"
mtd2: 00280000 00020000 "boot"
mtd3: 04380000 00020000 "system"
mtd4: 04380000 00020000 "cache"
mtd5: 04ac0000 00020000 "userdata"

注意,不同的手机在上述存储设备的顺序可能会各不相同!一定要检查您的手机,确定在以下的操作中选择正确的设备号(mtdX,这个X的序号一定要检查清楚)。
在本向导中,我们主要描述对"recovery"和"boot"的存储设备进行操作;"system"存储设备保存了android系统目录的所有数据(在系统启动后会挂载到“system/”目录);“userdata”存储设备将保存了android数据目录中的所有数据(在系统启动后会挂载到“data/”目录,里面是会有很多应用数据以及用户的preference之类的配置数据)。

从上面的输出可以看出来,recovery和boot分区对应着/dev/mtd/mtd1和/dev/mtd/mtd2,在你您开始做任何修改之前一定要做两件事情,第一件事情,一定要先对这两个分区进行备份。

可以使用如下命令进行备份:
# cat /dev/mtd/mtd1 > /sdcard/recovery.img
# cat /dev/mtd/mtd2 > /sdcard/boot.img
(注意added by lxros,只有手机获取了ROOT权限以后才能够执行上述的备份命令)

第二件事情,你您应该把你您最喜欢的update.zip刷机包放置到你您的sd卡的根目录上面。如此一来,即使你您在后续的操作中出了问题,也可以启动到recovery模式进行恢复。

另外一个你您需要知道的重要文件是在android系统目录下的/system/recovery.img,此文件是mtd1存储设备的完全拷贝。这个文件在每次关机的时候,会自动地被写回到mtd1存储设备里面。

这会意味着两个事情:
(1)任何对/dev/mtd/mtd1中数据的直接修改都会在下一次重启手机以后消失。

(2)如果希望对/dev/mtd/mtd1进行修改,最简单的做法是用你您自己的recovery.img替换掉/system/recovery.img。当你您创建自己的update.zip刷机包的时候(特别是在做刷机包的适配的时候),如果你您忘记替换这个/system/recovery.img,这个recovery.img就会在关机的时候被烧写到mtd1里面去或许会变砖。一定要注意这一点!
(译者的话,关于这个/system/recovery.img文件,在2.1的android的平台里面并没有找到,或许这个机制已经out了?!或者偶本人对这段话的理解不够深入?!希望明白的朋友不吝斧正)

2、boot和recovery映像的文件结构

boot和recovery映像并不是一个完整的文件系统,它们是一种android自定义的文件格式,该格式包括了2K的文件头,后面紧跟着是用gzip压缩过的内核,再后面是一个ramdisk内存盘,然后紧跟着第二阶段的载入器程序(这个载入器程序是可选的,在某些映像中或许没有这部分)。此类文件的定义可以从源代码android-src/system/core/mkbootimg找到一个叫做bootimg.h的文件。

(译者的话,原文是一个叫做mkbootimg.h的文件,但从Android 2.1的代码来看,该文件名应该是改为bootimg.h了)。
/*
** +-----------------+
** | boot header     | 1 page
** +-----------------+
** | kernel          | n pages  
** +-----------------+
** | ramdisk         | m pages  
** +-----------------+
** | second stage    | o pages
** +-----------------+
**
** n = (kernel_size + page_size - 1) / page_size
** m = (ramdisk_size + page_size - 1) / page_size
** o = (second_size + page_size - 1) / page_size
**
** 0. all entities are page_size aligned in flash
** 1. kernel and ramdisk are required (size != 0)
** 2. second is optional (second_size == 0 -> no second)
** 3. load each element (kernel, ramdisk, second) at
**    the specified physical address (kernel_addr, etc)
** 4. prepare tags at tag_addr.  kernel_args[] is
**    appended to the kernel commandline in the tags.
** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
** 6. if second_size != 0: jump to second_addr
**    else: jump to kernel_addr
*/
ramdisk映像是一个最基础的小型文件系统,它包括了初始化系统所需要的全部核心文件,例如:初始化init进程以及init.rc(可以用于设置很多系统的参数)等文件。如果你您希望了解更多关于此文件的信息可以参考以下网址:
http://git.source.android.com/?p=kernel/common.git;a=blob;f=Documentation/filesystems/ramfs-rootfs-initramfs.txt
以下是一个典型的ramdisk中包含的文件列表:
./init.trout.rc
./default.prop
./proc
./dev
./init.rc
./init
./sys
./init.goldfish.rc
./sbin
./sbin/adbd
./system
./data
recovery映像包含了一些额外的文件,例如一个叫做recovery的二进制程序,以及一些对该程序支持性的资源图片文件(当你您按下home+power组合键的时候就会运行这个recovery程序)。
典型的文件列表如下:
./res
./res/images
./res/images/progress_bar_empty_left_round.bmp
./res/images/icon_firmware_install.bmp
./res/images/indeterminate3.bmp
./res/images/progress_bar_fill.bmp
./res/images/progress_bar_left_round.bmp
./res/images/icon_error.bmp
./res/images/indeterminate1.bmp
./res/images/progress_bar_empty_right_round.bmp
./res/images/icon_firmware_error.bmp
./res/images/progress_bar_right_round.bmp
./res/images/indeterminate4.bmp
./res/images/indeterminate5.bmp
./res/images/indeterminate6.bmp
./res/images/progress_bar_empty.bmp
./res/images/indeterminate2.bmp
./res/images/icon_unpacking.bmp
./res/images/icon_installing.bmp
./sbin/recovery

3、对映像文件进行解包、编辑、打包的常规方法

(注意,下面我给你您介绍的是手工命令行方式进行解包以及重新打包的方法,但是我仍然创建了两个perl脚本,这两个脚本可以让你您的解包和打包工作变得轻松许多。可以参考本文的附件unpack-bootimg.zip和repack-bootimg.zip)

如果你您很擅长使用16进制编辑器的话,你您可以打开boot.img或者recovery.img,然后跳过开始的2K的头数据,然后寻找一大堆0的数据,在这一堆0的数据后面,紧跟着1F 8B这两个数字(1F 8B是gzip格式的文件的结束标记)。从此文件开始的地方(跳过2K的头),一大堆0后面紧跟着到1F 8B这两个数字为止的全部数据,就是gzip压缩过的linux内核。从1F 8B后面紧跟着的数据一直到文件的结尾包含的全部数据,就是ramdisk内存盘的数据。你您可以把把内核和ramdisk两个文件分别保存下来,在进行分别的修改和处理。我们可以通过un-cpio和un-gzip操作来读取ramdisk文件中的数据,可以使用如下的命令来实现这个目的,以下操作会生成一个目录,直接cd进去就可以看到ramdisk中的数据了:
gunzip -c ../your-ramdisk-file | cpio -i
此命令可以将ramdisk中的所有的文件解包到当前的工作目录下面,然后就可以对它进行编辑了。

当需要重新打包ramdisk的时候,就需要re-cpio然后re-gzip这些数据和目录,可以通过如下命令来实现:(cpio会把所有当前目录下面的文件都打包进去,因此,在进行此步骤之前,请把不需要的文件都清除掉。)
find . | cpio -o -H newc | gzip > ../newramdisk.cpio.gz
最后一步就是通过mkbootimg这个工具,把kernel和ramdisk打包在一起,生成一个boot.img:
mkbootimg --cmdline 'no_console_suspend=1 console=null' --kernel your-kernel-file --ramdisk newramdisk.cpio.gz -o mynewimage.img
这里的mkbootimg工具会在编译android的源代码的时候会在~/android-src/out/host/linux-x86/bin目录下面自动生成。
下载地址:
http://git.source.android.com/?p=platform/system/core.git;a=tree;f=mkbootimg

现在,如果不想背这些复杂的命令或者摆弄那个让人眩晕的16进制编辑器的话,可以尝试使用我编写的用于解包和打包的perl脚本了。希望这些脚本能够节约各位的键盘。

3.1、另一种解包、编辑、打包的方法

下载split_bootimg.zip文件(译者注,会在本文的附件中提供),在此zip文件中包含一个perl文件,split_bootimg.pl脚本,该脚本可以读取boot.img头(根据Android源码中的bootimg.h读取)将kernel和ramdisk读取出来,此脚本也会输出内核命令行和板子名字。
(注意,不要使用从/dev/mtd/mtd2直接拷贝出来的boot.img,此映像可能在读取过程遭到损坏。)
下面是一个从TC4-RC28更新中提取出来的boot.img进行解包操作:
% ./split_bootimg.pl boot.img
Page size: 2048 (0x00000800)
Kernel size: 1388548 (0x00153004)
Ramdisk size: 141518 (0x000228ce)
Second size: 0 (0x00000000)
Board name:
Command line: no_console_suspend=1
Writing boot.img-kernel ... complete.
Writing boot.img-ramdisk.gz ... complete.
解包ramdisk的命令如下:
% mkdir ramdisk
% cd ramdisk
% gzip -dc ../boot.img-ramdisk.gz | cpio -i
% cd ..
解码完毕后,就可以修改了(例如,在default.prop设置ro.secure=0等等)

使用mkbootfs工具(mkbootfs工具是编译完毕Android源代码以后,就会在~/android-src/out/host/linux-x86/bin自动生成)来重新创建ramdisk,可以使用如下命令来操作:
% mkbootfs ./ramdisk | gzip > ramdisk-new.gz
使用mkbootimg来重新创建boot.img,mkbootimg也可以在~/android-src/out/host/linux-x86/bin目录中可以找到:
% mkbootimg --cmdline 'no_console_suspend=1 console=null' --kernel boot.img-kernel --ramdisk ramdisk-new.gz -o boot-new.img
(注意:console=null的命令行选现是从TC4-RC30的boot.img引入的,用以去掉root shell)

4、将新的映像刷回到手机

可以将recovery.img拷贝到/system目录下面,然后重新启动手机,让手机自动为你您刷写到mtd里面(工作原理在上面已经提过了)。对于boot.img可以通过将其拷贝到sd卡的根目录,然后通过手机内的刷写工具将此映像写入到手机中。

例如,使用adb工具将boot.img拷贝到手机的sd卡的根目录:
adb push ./mynewimage.img /sdcard
然后通过adb shell登录手机的shell交互模式,利用命令行进行交互:
# cat /dev/zero > /dev/mtd/mtd2
   write: No space left on device [this is ok, you can ignore]
# flash_image boot /sdcard/mynewimage.img
然后重启。
如果能够正常启动,那么祝贺你您,你您的修改和替换已经成功了;如果不能够顺利启动,则需要重新启动进入recovery模式,并且使用update.zip来恢复。

5、解包、编辑、打包为我们带来了什么

可以修改开机启动时候的画面,具体的操作的地址为:
http://forum.xda-developers.com/showthread.php?t=443431

6、本文讲的内容与使用update.zip刷机包不是一码事

您可以很容易地在其他论坛上看到关于如何自制update.zip刷机包的方法,也可以下载到很多在网络上共享的自制刷机包。

Android System Services 环境浅析

原创文章欢迎转载,转载请注明住处rickleaf

1.System Services

首先我要声明一下,我讲的System Services并非Android 开发应用程序时,所涉及的Service(后台应用服务程序)的概念。

我要讲的System Services是Android操作系统Java应用程序下层的,伴随操作系统启动而运行的系统后台服务程序。

它是Android系统运行的基石,它配合binder(Android多进程通讯方法)、dalvik虚拟机和Android应用程序构成了一个多进程

交互通讯,交互服务的Android系统。

2.浏览一下Android系统的service

启动shell

adb shell

执行下面指令

#service list

Found 47 services:
0    phone: [com.android.internal.telephony.ITelephony]
1    iphonesubinfo: [com.android.internal.telephony.IPhoneSubInfo]
2    simphonebook: [com.android.internal.telephony.IIccPhoneBook]
3    isms: [com.android.internal.telephony.ISms]
4    appwidget: [com.android.internal.appwidget.IAppWidgetService]
42    SurfaceFlinger: [android.ui.ISurfaceComposer]
43    media.audio_policy: [android.media.IAudioPolicyService]
46    media.audio_flinger: [android.media.IAudioFlinger]
#


从结果看来Android后台有很多的service,他们是分散在不同进程中的线程实体(有点绕嘴,但是我认为这样说比较确切)。

 

3.什么是Service Manager

里面理解一下就可以了,Service manager是管理以上services的一个进程,他是实际存在的。

您可以在adb shell中运行ps看看进程列表就知道了。

 

源代码位于:

frameworks/base/cmds/servicemanager

执行方式:

他是用c和c++语言编写的natvie可以执行文件。在Android中称之为EXECUTABLE,这个名称很重要因为Android.mk文件中

用这个名字来确定他是可以执行的二进制文件。

 

4.探究一下Service Manager的启动过程和方法

开始有点复杂了,也该开始进入真正的Linux阶段了。

众所周知Linux的启动和文件系统的加载需要一个ramdisk,ramdisk负责让Linux kernel加载第一个进程init进程

 

在Android的ramdisk中就有这样一个可执行文件init,

在深入一下,我们可以去看一下

 

system/core/init/init.c

 

int main(int argc, char **argv)
{
    。。。。。。
    parse_config_file("/init.rc");

    。。。。。。

 

对的,没看错。这个文件会编译出一个init的二进制可执行文件,并且去读init.rc文件。

 

至此,我们称init.rc文件为Android启动配置脚本。

 

现在我们打开init.rc文件,(如果您不知道init.rc,请参考google吧)

 

## Daemon processes to be run by init.
##
service servicemanager /system/bin/servicemanager
    user system
    critical
    onrestart restart zygote
    onrestart restart media

 

看到吗,servicemanager 是init通过init.rc加载的第一个进程

接下来启动了zygote和media

4.system server进程

继续阅读init.rc

servicemanager进程运行起来以后,我们就可以应用binder来应用servicemanager提供的服务函数去创建

system-server和mediaserver了,下面是init.rc中的代码

 

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

#system-server的创建是通过app_process这个二进制程序去加载的
    socket zygote stream 666
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media

service media /system/bin/mediaserver   #mediaserver的启动代码比较简单,看看就知道了不用参数就创建了
    user media
    group system audio camera graphics inet net_bt net_bt_admin

 

5.回过头再看系统的进程列表

# ps
USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME
root      1     0     296    204   c009a694 0000c93c S /init
root      2     0     0      0     c004dea0 00000000 S kthreadd
root      25    1     728    316   c003d444 afe0d6ac S /system/bin/sh
system    26    1     796    256   c019a810 afe0ca7c S /system/bin/servicemanager
root      30    1     82860  26580 c009a694 afe0cba4 S zygote
media     31    1     20944  3184  ffffffff afe0ca7c S /system/bin/mediaserver
root      32    1     784    280   c0209468 afe0c7dc S /system/bin/installd
keystore  33    1     1616   396   c01a65a4 afe0d40c S /system/bin/keystore
root      34    1     728    272   c003d444 afe0d6ac S /system/bin/sh
root      35    1     824    332   c00b7dd0 afe0d7fc S /system/bin/qemud
root      37    1     1308   152   ffffffff 0000eca4 S /sbin/adbd
root      44    34    780    304   c0209468 afe0c7dc S /system/bin/qemu-props
system    52    30    158356 37804 ffffffff afe0ca7c S system_server
app_1     92    30    108640 20580 ffffffff afe0da04 S com.android.inputmethod.pinyin
radio     93    30    122852 23340 ffffffff afe0da04 S com.android.phone
app_1     98    30    143244 34888 ffffffff afe0da04 S android.process.acore

 

有点复杂了,请大家跟上思路。我们注意观察进程列表的PID和PPID,我们要通过实际的列表去理清他们的亲缘关系。

 

servicemanager是init的子进程

mediaserver是init的子进程

zygote是init的子进程

system_server和所有的java应用程序是zygote的子进程

 

休息一下我们看看他们的应用程序代码方式

Java script caller (executable)
frameworks/base/cmds/app_process/
app_main.cpp

app_process是android系统下面基于命令行的java的应用程序的调用工具

 

system_server executable(c/c++写的程序)


frameworks/base/cmds/system_server/

system_main.cpp
library/system_init.cpp

 

SystemServer (java程序)
frameworks/base/services/java/com/android/server/
SystemServer.java


Zygote (java程序)
frameworks/base/core/java/com/android/internal/os/
ZygoteInit.java

 

6.分析具体的调用过程(很痛苦)

app_main 调用 zygoteInit

p { margin-bottom: 0.21cm; }

/ Next arg is startup classname or "--zygote"

if ( i < argc ) {

arg = argv [ i ++];

if ( 0 == strcmp ( "--zygote" , arg )) {

bool startSystemServer = ( i < argc ) ?

strcmp ( argv [ i ], "--start-system-server" ) == 0 : false ;

setArgv0 ( argv0 , "zygote" );

set_process_name ( "zygote" );

runtime. start ( "com.android.internal.os.ZygoteInit" ,

startSystemServer );

} else {

set_process_name ( argv0 );


runtime. mClassName = arg ;


// Remainder of args get passed to startup class main ()

runtime. mArgC = argc - i ;

runtime. mArgV = argv + i ;


LOGV ( "App process is starting with pid=%d, class=%s. /n " ,

getpid (), runtime. getClassName ());

runtime. start ();

}

}


Zygote 分裂出 system_server



p { margin-bottom: 0.21cm; }

public static void main ( String argv []) {

try {

// Start profiling the zygote initialization.

if ( argv [ 1 ] . equals ( "true" )) {

startSystemServer ();

}

Log. i ( TAG, "Accepting command socket connections" );


if ( ZYGOTE_FORK_MODE ) {

runForkMode ();

} else {

runSelectLoopMode ();

}

}


frameworks/base/core/java/com/android/internal/os/
ZygoteInit.java

p { margin-bottom: 0.21cm; }

private static boolean startSystemServer()

throws MethodAndArgsCaller, RuntimeException {

/* Hardcoded command line to start the system server */

try {

/* Request to fork the system server process */

pid = Zygote. forkSystemServer (

parsedArgs. uid , parsedArgs. gid ,

parsedArgs. gids , debugFlags, null );

} catch ( IllegalArgumentException ex) {

throw new RuntimeException (ex);

}

/* For child process */

if (pid == 0 ) {

handleSystemServerProcess(parsedArgs);

}

return true ;

}


1.如果大家还能看到这里请复习一下 Linux的两个系统调用 fork和exec


frameworks/base/services/java/com/android/server/SystemService.java

p { margin-bottom: 0.21cm; }

public static void main ( String [] args ) {

if ( SamplingProfilerIntegration. isEnabled ()) {

SamplingProfilerIntegration. start ();

timer = new Timer ();

timer . schedule ( new TimerTask () {

@ Override

public void run () {

SamplingProfilerIntegration. writeSnapshot ( "system_server" );

}

} , SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL );

}

VMRuntime. getRuntime () . setTargetHeapUtilization ( 0 .8f );

System . loadLibrary ( "android_servers" );

init1 ( args );------------------>

}


public static final void init2 () {

Log. i ( TAG, "Entered the Android system server!" );

Thread thr = new ServerThread ();

thr. setName ( "android.server.ServerThread" );-------------->

thr.start();

}



红色的部分最后会执行我们上面列出的jni代码

frameworks/base/cmds/system_server/
library/system_init.cpp


Init2 这里启动了java的system service


p { margin-bottom: 0.21cm; }

class ServerThread extends Thread {

@ Override

public void run () {


Log. i ( TAG, "System Content Providers" );

ActivityManagerService. installSystemProviders ();


Log. i ( TAG, "Battery Service" );

battery = new BatteryService ( context );

ServiceManager. addService ( "battery" , battery );


Log. i ( TAG, "Hardware Service" );

hardware = new HardwareService ( context );

ServiceManager. addService ( "hardware" , hardware );


Log. i ( TAG, "Alarm Manager" );

AlarmManagerService alarm = new AlarmManagerService ( context );

ServiceManager. addService ( Context . ALARM_SERVICE , alarm );


}

}

 

原创文章欢迎转载,转载请注明住处rickleaf

7. 整理Android系统启动流程

 

至此System Service的服务环境启动起来了


  

 

 

8. 最后引用霍金的一句话:“懂与不懂都是收获”

http://blog.csdn.net/rickleaf/article/details/6369720

Records:271234