且听风吟

Don't panic! I'm a programmer.

Android Vold 分析

| Comments

Vold(Volume Daemon) 主要负责管理和控制外部存储设备,这些控制包括SD卡设备插拔,挂载,格式化等等. Vold在Android 平台上的作用相当于udev在Linux平台上的功能。

Vold的架构

Vold的架构大致如图:

/images/vold-architecture.png

  • Kernel:detect device hot-plug, load drivers
  • Sysfs: generate events and send it to user space
  • NetlinkManager: establish connection to sysfs, listen to uevents and process it
  • VolumeManager: disk volume operation
  • CommandListener: wait for commands from MountService
  • NativeDaemonConnector:establish connection between MountService and vold daemon

其中,一张SD卡对应一个DirectVolume对象。

Vold模块的类图如下:

/images/vold-class-diagram.png

Vold startup

vold的启动分为两个过程,一是vold daemon的启动,二是Java层MountService的启动。

启动Vold daemon

Vold daemon由init进程启动,参见init.rc片段:

/images/vold-initrc.png

vold入口函数位于/system/vold/mail.cpp,启动后处理流程如下:

/images/vold-startup-sequence.png

1.Create directory /dev/block/vold 1.Create singleton instance of VolumeManager , NetlinkManager andCommandListener 1.Process configuration file /system/etc/vold.fstab 1.NetlinkManager creates connection between kernel and vold 1.Manually trigger uevents by writing “add\n” to file in /sys/block 1.CommandListener starts listening commands from framework

启动MountService

1.MountService initialized by SystemServer on device starts up 1.Create connection to vold

After MountService is up, the vold system is ready.

Send commands from framework

以App层通过MountService 挂载一个volume为例,说明从上层向下层的调用流程。

/images/vold-send-commands-from-framework.png

  1. NativeDaemonConnector::sendCommand()
  2. SocketListener::runListener() <—vold accepting connections here
  3. FrameworkListener::onDataAvailable()
  4. FrameworkListener::dispatchCommand()
  5. FrameworkCommand::runCommand()—-CommandListener::VolumeCmd::runCommand()
  6. VolumeManager::mountVolume()
  7. Volume::mountVol()

Process events from kernel

处理kernel发出的挂载事件:

/images/vold-process-events-from-kernel.png

  1. SocketListener::runListener <—listening events from kernel
  2. NetlinkListener::onDataAvailable()
  3. NetlinkHandler::onEvent()
  4. VolumeManager::handleBlockEvent , VolumeManager::handleSwitchEvent , VolumeManager::handleUsbCompositeEvent
  5. DirectVolume::handleBlockEvent()
  6. DirectVolume::handleDiskAdded, DirectVolume::handlePartitionAdded, DirectVolume::handleDiskRemoved, DirectVolume::handlePartitionRemoved,DirectVolume::handleDiskChanged, DirectVolume::handlePartitionChanged,or ignore non add/remove/change event

Reference

Ubuntu10.04安装无线网卡ath9k_htc驱动

| Comments

我的USB无线网卡型号为TL_WN721N,插在Ubuntu10.04上没有反应。据说10.10以后插上就可以用了,没有测试。

第一步 确定ath9k_htc驱动支持你的网卡型号
这里 列出了支持的型号列表,你可以通过lsusb查看自己的网卡型号是否在列表当中。

第二步 安装firmwire
这里下载htc_9271.fw,拷贝到/lib/firmwire下

第三步 安装compact wireless
安装compact wireless有打包好的deb包,从这里下载GUI Program to install ath9k_htc,安装完后直接运行,等待安装完成重启机器就可以了。 爱折腾的也可以到这里下载 最新的compact wireless驱动,然后编译安装:

sudo make
sudo make install
sudo make unload
sudo make load ath9k_htc

重启机器即可。

Reference * http://forum.ubuntu.com.cn/viewtopic.php?f=116&t=326568&p=2388841 * http://blog.chinaunix.net/space.php?uid=20620288&do=blog&id=2691282

Compile CyanogenMod (Linux) for HTC Desire

| Comments

出现如下问题:
1.repo init时报错:

......
object 30d452905f166b316152f236422f85c8aa75a2d0
type commit
tag v1.7.5
tagger Shawn O. Pearce <sop@google.com> 1307663540 -0700

repo 1.7.5

gpg: Signature made Fri 10 Jun 2011 07:52:20 AM CST using DSA key ID 920F5C65
gpg: Can't check signature: public key not found
error: could not verify the tag 'v1.7.5'

出错原因是曾使用repo sync从其它库sync过代码,删掉~/.repoconfig即可:

$ rm -rf ~/.repoconfig
$ ./repo init -u git://github.com/CyanogenMod/android.git -b gingerbread
gpg: keyring `/home/calvin/.repoconfig/gnupg/secring.gpg' created
gpg: keyring `/home/calvin/.repoconfig/gnupg/pubring.gpg' created
gpg: /home/calvin/.repoconfig/gnupg/trustdb.gpg: trustdb created
gpg: key 920F5C65: public key "Repo Maintainer <repo@android.kernel.org>" imported
gpg: Total number processed: 1
gpg:               imported: 1

Getting repo ...
   from http://android.git.kernel.org/tools/repo.git
。。。。

2.repo sync时报错:

......
Fetching projects:   1% (3/285) 
Initializing project platform/bootable/bootloader/legacy ...
android.git.kernel.org[0: 130.239.17.13]: errno=Connection refused
android.git.kernel.org[0: 149.20.4.77]: errno=Connection timed out
android.git.kernel.org[0: 199.6.1.173]: errno=Connection refused
android.git.kernel.org[0: 2001:4f8:8:10:1972:112:1:0]: errno=Network is unreachable
android.git.kernel.org[0: 2001:500:60:10:1972:112:1:0]: errno=Network is unreachable
android.git.kernel.org[0: 2001:6b0:e:4017:1972:112:1:0]: errno=Network is unreachable
android.git.kernel.org[0: 2001:4f8:1:10:1972:112:1:0]: errno=Network is unreachable
fatal: unable to connect a socket (Network is unreachable)
error: Cannot fetch platform/bootable/bootloader/legacy

连接服务器时的问题,解决:
修改.repo/manifest.xml中

  <remote  name="korg"
           fetch="git://android.git.kernel.org/"
           review="review.source.android.com" />

  <remote  name="korg"
           fetch="http://android.git.kernel.org/"
           review="review.source.android.com" />

Android Monkey & Jni相关

| Comments

首先,android平台应用程序可能产生以下四种crash: * App层: Force close crash 和 ANR crash * Native层: Tombstone crash * Kernel层: Kernel panic 比较难定位,可以查看/proc/last_kmsg来辅助定位。

最近需要通过monkey工具测试Tombstone类型的crash,抓取log并分析。通过monkey测试,如果要抓取native类型的crash,需要加上—monitor-native-crash参数:

seed=$(date +%Y%m%d%H%m%S)
monkey -s $seed --pkg-whitelist-file ${your-package-list} --monitor-native-crashes --kill-process-after-error -v -v -v 2000000000

这样,monkey在跑出crash后,在/data/system/dropbox 和 /data/tombstones目录下会生成相关日志,moneky会停止发送事件流并退出测试.值得注意的是,/data/tombstones文件夹下只会保存10个日志,超过10个后,最早创建的会被替换。而monkey是通过监视这个文件夹下的文件数量变化来判断是否有tombtone类型的crash产生的。因此,当/data/tombstones文件夹下超过10个文件后,如果再有tombstone crash产生的话,monkey是不能检测到的,它会继续发送事件流。为了避免这个问题,可以在每次运行monkey之前先清空一下这个文件夹。

另外,在settings.db中的secure表中有三个字段:dropbox:data_app_wtf,dropbox:data_app_anr,dropbox:data_app_crash。如果设置为enabled,每当有app crash之后,在/data/system/dropbox这个文件夹下都会产生相关的日志信息,这对于分析调试问题都是第一手的信息。

如何制造tombstone类型的crash?

这需要通过jni调用一个native的so文件,在本地代码中抛出异常即可。 可以编写如下代码tombstone_gen.cpp:

int main(int argc, char **argv) {
  int *p=0;
  *p=1;    //will seg fault
  return 0;
}

参照development/samples/SimpleJNI的示例,运行build出的apk即可。

关于jni调用,也有几个问题:

1.FindClass,RegisterNatives等找不到:

    target thumb C: libtombstonec <= development/samples/AndroidDemos/jni/tombstone_gen.c
    development/samples/AndroidDemos/jni/tombstone_gen.c: In function 'registerNativeMethods':
    development/samples/AndroidDemos/jni/tombstone_gen.c:48: error: request for member 'FindClass' in something not a structure or union
    development/samples/AndroidDemos/jni/tombstone_gen.c:53: error: request for member 'RegisterNatives' in something not a structure or union
    development/samples/AndroidDemos/jni/tombstone_gen.c: In function 'JNI_OnLoad':
    development/samples/AndroidDemos/jni/tombstone_gen.c:97: error: request for member 'GetEnv' in something not a structure or union
    make: *** [out/target/product/generic/obj/SHARED_LIBRARIES/libtombstonec_intermediates/tombstone_gen.o] Error 1

问题原因在于:  
如果是C程序,要用`(*env)->`  
如果是C++要用 `env->`  
因此有两种解决方法:  
* 将 (*env)-> 改为 env->  
* 将c文件改为cpp文件,改为c++的方式编译。  

2.运行时异常java.lang.UnsatisfiedLinkError

tombstone_gen.cpp中

    static const char *classPathName = "com/android/demo/AndroidDemos";

类名有误,导致类链接错误。

最后,关于jni中JNINativeMethod相关解释:
http://hi.baidu.com/zhlg_hzh/blog/item/f0d782081f2f45d963d986f5.html

Vim Tips

| Comments

移动

0 移动到行首
$ 移动到行末
CTRL+ o 回到上一视图
CTRL+ i 前进到下一视图
ZZ 保存更改并退出

fx 在当前行向前移动光标到下一个字符x (很明显, x可以是任意你想要移动到的字符). 这是一个超级有用的命令. 你可以输入;来重复前一个f命令. tx 和上面的命令基本相同, 除了移动光标到字符x之前而不是x字符本身. (这个真的很有用)
Fx 在当前行向后移动光标到上一个字符x.

zz 将当前行置于屏幕中间
zt 将当前行置于屏幕顶端
zb 将当前行置于屏幕底端
CTRL+e 移动光标所在行的位置,比如我光标在第10行,我想光标不动,但是所在行向上移
CTRL+y 同上,但是向下移
% match of next brace, bracket, comment

在长句子内移动
在你编辑电子邮件或其他段落式文本的时候, 你可能注意到移动方向跳的行数比你料想的多. 这是因为你的段落对vim来说在很长的同一个行里. 用h, j, k, l移动前输入g可以让移动相对于屏幕看上去的行数而不是vim内部的行数.

复制-粘贴

1.选定文本块,使用v进入可视模式;移动光标键选定内容
1.复制选定块到缓冲区,用y;
复制整行,用yy;
复制到系统剪切板 “+y
剪切到系统剪切板 ”+x
1.剪切选定块到缓冲区,用d;剪切整行用dd
1.粘贴缓冲区中的内容,用p

vim文件间复制粘贴
http://www.cnblogs.com/jianyungsun/archive/2011/03/19/1988855.html
使用 yaw 命令复制当前光标所在单词

用vi 复制第5行到第10行并粘贴到第12行之后?
:5,10 co 12

删除插入(s与c)

s删除光标当前字符,进入插入模式,等同于 x i
S删除当前行 进入插入模式,等同于 dd i
c change 同样是替代插入,不过c要与其他指令配合使用
如 cw 删除一个单词后i ,
c$ =C = D i 删除光标位置到行末后i
cc= S
d$= D
使用 yaw 命令复制当前光标所在单词

删除
向后删除一个词 dw 两个词 d2w
向后删除到行尾 d$
删除整行 dd
df c 向后删至字符c出现的位置(包括该字符)
dt c 向后删至字符c出现的位置(不包括该字符)
d /text 向后删至内容text出现的位置(不包括该text内容)

如果你在一个单词的中间而又想删掉这个单词,在你用 “dw” 前,你必须先移到这个单词
的开始处。这里还有一个更简单的方法:”daw”。
“daw” 的 “d” 是删除操作符。”aw” 是一个文本对象。提示:”aw” 表示 “A Word” (一个
单词),这样,”daw” 就是 “Delete A Word” (删除一个单词)。确切地说,该单词后的空
格字符也被删除掉了。
diw 删除光标上的单词 (不包括空白字符)
daw 删除光标上的单词 (包括空白字符)

还有句子的 is 也是一个文本对象 对句子进行操作。
另外还可以 ciw diw viw

如果光标位于”“,[],()之间,还可以
vi”
ci”
参考 :help object-select

编辑

打开新文件 :e filename
i 在游標所在字元前開始輸入文字(insert)。
a 在游標所在字元後開始輸入文字(append)。
o 在游標所在行下開一新行來輸入文字(open)。
I 在行首開始輸入文字。此之行首指第一個非空白字元處,要從真正的第一個字
元處開始輸人文字,可使用 0i 或 gI(Vim 才有)。
A 在行尾開始輸入文字。這個好用,您不必管游標在此行的什麼地方,只要按 A
就會在行尾等著您輸入文字。
O 在游標所在行上開一新行來輸入文字。
J 將下一行整行接至本行(Joint)。
撤销 u
在一行上撤销 U
重做 CTRL r
替换字母 rx(x为要替换的字母)
改动单词直到单词末尾 ce
将光标移到某一个字母上,输入ce,然后敲入正确的单词
与此类推,还有cw,c$
Typing a capital R enters Replace mode until ESC is pressed.

. 重复前次的编辑动作
`. 回到上次编辑的位置

快速选中括号里的文本
vi{ 选中{}中间的内容,不包括{}
va{ 选中{}中间内容,包括{}
vi( 选中()中间内容
vi[ 选中[]中间内容
vit 选中中间的内容,编辑XML文件时特别有用
vi” 选中”“中间内容
vi’ 选中’‘中间的内容

vis 选中一个句子
viw 选中一个单词
vip 选中一个段落

同时适用于c,y,d等motion。
参考:h text-objects

编辑二进制文件
打开文件:vim -b file
用vim -b datafile打开文件,用:%!xxd把二进制文件转换
编辑完后用:%!xxd -r命令把编辑状态的文本转换成二进制数据
具体描述可用:help 23.4察看帮助文档

切换最近编辑的两个buffer
CTRL-6 或 CTRL-^ 或 :e #

大小写转换:
gu或者gU或者~

行编辑
:3,.d 从3行删除到当前行
:.,3d 从当前行删除到3行
:3,.+3d 从3行删除到当前行后第三行
:3,.-1d 从3行删除到当前行前一行
:.,3s/adb/def/g 将当前行到第三行之间的abc替换为def
:345w file 将第 345 行写入 file 文件
3,5w file 将第 3 行至第 5 行写入 file 文件
:r file 读取 file 文件的内容,插入当前光标所在行的后面
:e file 编辑新文件 file 代替原有内容
:f file 将当前文件重命名为 file
:f 打印当前文件名称和状态,如文件的行数、光标所在的行号等

命令补全

When typing a : command, press CTRL-D to see possible completions.
Press TAB to use one completion.

使用关键字自动完成(补全)
vim 有个非常好的关键字自动完成系统. 就是说你可以只输入很长的词的一部分, 按一个键, vim帮你把这个词自动补全. 比如说在你的代码中有一个变量叫iAmALongAndAwkwardVarName, 你可能不愿意每次用这个变量都把整个词打一遍, 这时候就可以用自动完成功能.
要使用关键字自动完成, 只要输入一个字串的前几个字母 (比如 iAmAL) 然后按 Ctrl-N或者Ctrl-P. 如果vim没有选择你需要的字串, 继续按Ctrl-N或者Ctrl-P – vim会遍历所有和你输入的前几个字母匹配的字串.

搜索替换

  1. CTRL-G displays your location in the file and the file status.
    G moves to the end of the file.
    number G moves to that line number.
    gg moves to the first line.

  2. Typing / followed by a phrase searches FORWARD for the phrase.
    Typing ? followed by a phrase searches BACKWARD for the phrase.
    After a search type n to find the next occurrence in the same direction
    or N to search in the opposite direction.
    CTRL-O takes you back to older positions, CTRL-I to newer positions.

  3. Typing % while the cursor is on a (,),[,],{, or } goes to its match.

  4. To substitute new for the first old in a line type :s/old/new 这个命令只替换当前行的内容
    To substitute new for all ‘old’s on a line type :s/old/new/g
    To substitute phrases between two line #’s type :#,#s/old/new/g
    To substitute all occurrences in the file type :%s/old/new/g
    To ask for confirmation each time add ‘c’ :%s/old/new/gc

注:
%代表当前编辑的内容
#代表前一个编辑的内容
如:
:%!xxd 表示将当前所有内容用xxd命令处理

搜索精确匹配
如xxx是要查找的内容,输入
/\<xxx\>
要在<> 前加\进行转义,告诉Vim <> 这个不是要查找的内容。
\< 表示只匹配单词的开头,\>表示只匹配单词末尾,点.表示匹配任何字符。
*搜索游标所在出的单词(精确匹配)
#反向搜索游标所在出的单词(精确匹配)

快速替换游标处的单词
把光标置于thisisaverylongword之上, 然后

:%s/<CTRL+R><CTRL+W>/ABCD/g .  

在命令格式下, CTRL+R代表插入寄存器, CTRL+W代表当前单词.
也可以通过寄存器的方式来使用.
使用v选择一个区域, 然后 “ay , 存取寄存器a,然后

:%s/<CTRL+R>a/ABCD/g  

ABCD 可以用同样的方法,使用寄存器.

重复修改:
“.” 是 Vim 中一个非常简单而有用的命令。它重复最后一次的修改操作。

    /four<Enter>    找到第一个 "four"  
    cwfive<Esc>     修改成 "five"  
    n               找下一个 "four"  
    .               重复修改操作  
    n               找下一个 "four"  
    .               重复修改  
                    如此类推......  

搜索完毕后取消高亮
:noh 或者 :nohlsearch

窗口和标签

CTRL+w w 切换窗口
:tabedit file 在新tab中打开文件
:tab split 将当前窗口缓冲区内容在新tab中打开
:tabonly 关闭其他所有标签
:tabc 关闭当前标签页
:tabc 3 关闭第三个标签页
gt 切换tab显示
2gt 显示第二个tab

更改窗口宽度和高度
Crtl+w > 将当前窗口向右边扩展一行,将 > 换成 < 就是向左边扩展一行。也可以在之前使用数字代表扩展的行数
5 Ctrl+w > ,向右边扩展5行。
Ctrl+w + 更改窗口高度。+ 增加高度,- 减小高度。

:close 关闭窗口
实际上,任何退出编辑的命令都可以关闭窗口,象 “:quit” 和 “ZZ” 等。但 “close”可以避免你在剩下一个窗口的时候不小心退出 Vim 了。

:only 关闭所有其它窗口
如果你已经打开了一整套窗口,但现在只想编辑其中一个,可以使用这个命令。
这个命令关闭除当前窗口外的所有窗口。如果要关闭的窗口中有一个没有存盘,Vim 会
显示一个错误信息,并且那个窗口不会被关闭。

注释python代码

1.Ctrl + V 进入 VIM 的块操作模式
1.使用 j, k 键上下移动,选中这三行,需要确保每行的首列包含在选中块中。
1.键入 r ,即在替换行首字符。 输入 Python 的注释符 ‘#’。
1.按下 ESC 键。

删除重复行

:sort u 参见:help sort

格式化文本

参见:h formatoptions, :h textwidth gq 对行内长文本进行换行 gq{ 对一个段落进行自动换行

其他

K 显示游标所在的单词相关的man page
:r!command 在游标处插入command的执行输出结果,如date命令将插入当前时间

对齐文本
以某一个分隔符为界在垂直方向上对齐文本,如:

name|age|sex  
Alice|23|female  
BobBobBobBob|25|male  

对齐后:

name         | age | sex  
Alice        | 23  | female  
BobBobBobBob | 25  | male  

这个功能在写代码时对齐变量十分有用,通过Align和Tabular两个插件可以轻松完成。
:%Align | 将全文对齐
:'<,'>Align = + - = | 将选中的文本按+,-,=或者|对齐
更多选项参考:help align

另外,通过外部命令column也可以实现:
:%!column -t -s '|'

VIM中按w时光标移动总要停顿一会儿才会响应
原因是我map了一个以w开头的按键,导致每次按w后,vim都要等待一下判断是执行w还是继续接收输入。去掉这个map就好了。

在线的colorscheme配色网站
http://bytefluent.com/vivify/

一些plugin:

利用onUserLeaveHint发送后台运行通知

| Comments

背景

用户按下Home键将程序置于后台运行或者应用启动其他activity,比如系统浏览器,短信等,需要向系统发送通知,用户做完别的操作后,点击通知栏,回到应用。

问题

在什么时机发送通知? 用户按下Home的事件在应用层时捕捉不到的,因此只能从activity生命周期方法着手。

方法一

系统所有activity继承一个BaseActivity,在BaseActivity中维护一个当前可见的activity数组:

protected static ArrayList sVisibleActivities = new ArrayList();

在onResume中,将当前activity保存,同时清除所有通知:

protected void onResume()
{
if (!sVisibleActivities.contains(this))
{
sVisibleActivities.add(this);
}

// 清除系统消息
mNotificationManager.cancel(R.id.notify);
}

在onStop中,清除保存的当前activity:
protected void onStop()
{
if (sVisibleActivities.contains(this))
{
sVisibleActivities.remove(this);
}

// 如果当前没有可见的activity,则发送系统通知
if (sVisibleActivities.isEmpty())
{
sendBackgroundNotify();
}

super.onStop();
}

这种方式在大多数情况下工作良好,可以达到需求,但是问题时,当前台的activity被至于后台时,onStop()方法不一定会被调用,因此通知有可能不会被发出!

方法二

几经周折,发现activity有一个生命周期方法可以达到目的:

protected void onUserLeaveHint ()

Since: API Level 3
Called as part of the activity lifecycle when an activity is about to go into the background as the result of user choice.
For example, when the user presses the Home key, onUserLeaveHint() will be called, but when an incoming phone call causes the in-call Activity to be automatically brought to the foreground,
 onUserLeaveHint() will not be called on the activity being interrupted. In cases when it is invoked, this method is called right before the activity's onPause() callback.
This callback and onUserInteraction() are intended to help activities manage status bar notifications intelligently; specifically, for helping activities determine the proper time to cancel a notfication.

从文档来看,这个方法似乎就是为了按下Home键时这样的场景设计的。
这样,在onUserLeaveHint里发出系统通知即可。
但是问题又来了,如果启动应用,从一个activity依次调用startActivity,finish关闭自己,启动一个新的activity时,onUserLeaveHint也会被调用….

再次翻阅文档,发现Intent中的一个Flag:

    public static final int FLAG_ACTIVITY_NO_USER_ACTION

    Since: API Level 3
    If set, this flag will prevent the normal onUserLeaveHint() callback from occurring on the current frontmost activity before it is paused as the newly-started activity is brought to the front.

    Typically, an activity can rely on that callback to indicate that an explicit user action has caused their activity to be moved out of the foreground.
    The callback marks an appropriate point in the activity's lifecycle for it to dismiss any notifications that it intends to display "until the user has seen them," such as a blinking LED.
    If an activity is ever started via any non-user-driven events such as phone-call receipt or an alarm handler, this flag should be passed to Context.startActivity, ensuring that the pausing activity does not think the user has acknowledged its notification.

这正是我想要的,这样,在启动activity时,往intent中加上这个flag,onUserLeaveHint就不会再被调用了,hoory…

在eclipse中调试Android源代码

| Comments

1.编译android 代码树,编译sdk

编译注意:  
1.gcc的版本过高,由于android源码编译要求为4.3,如果gcc版本为4.4,那么编译可能会失败,我的系统是ubuntu 10.04,默认的gcc版本为4.4,gcc-4.4太严格,那么怎样从gcc-4.4降到gcc- 4.3呢?  
    1.安装gcc-4.3  
          `$ sudo apt-get install gcc-4.3 g++-4.3`  
    2.修复gcc相关链接  

        $ cd /usr/bin
        $sudo ln -snf gcc-4.3 gcc
        $sudo ln -snf g++-4.3 g++
        $sudo ln -snf cpp-4.3 cpp

    将gcc,g++链接至4.3版本即可。  

2.JDK 5.0, update 12 or higher.Java 6 is not supported, because of incompatibilities with @Override.  
3.安装编译必须的软件包  

    $ sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl sun-java5-jdk zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev
参见http://source.android.com/source/download.html

2、拷贝development/ide/eclipse/.classpath到.classpath.

3、启动 eclipse ,按照这个步骤 File->New->Java Project (不是 Android Project) –>Create project from existing source 选到代码树根目录 .

4、在经过长时间等待之后, source code将被导入project,正常情况下应该没有error。

5启动模拟器

[calvin@calvin-desktop ~/android/source-code/android_1.5_Sourcecode 10:28:03 ] $ . build/envsetup.sh
[calvin@calvin-desktop ~/android/source-code/android_1.5_Sourcecode 10:28:18 ] $ lunch 1
[calvin@calvin-desktop ~/android/source-code/android_1.5_Sourcecode 10:30:31 ] $ ./out/host/linux-x86/bin/emulator

6、在ddms中选中要调试的进程

7、在source code中设置断点

8、在eclipse里, Run->Debug Configuration->Remote Java Application->New, 设置 Connection port to 8700 (DDMS’s 默认端口),即可正常调试了

APN切换后数据连接过程

| Comments

注册观察者回调

GsmDataConnectionTracker在实例化时,会注册一个观察者,监视apn数据库的变化。

p.getContext().getContentResolver().registerContentObserver(
            Telephony.Carriers.CONTENT_URI, true, apnObserver);

当用户通过UI改变apn并保存后,Settings app将更新后的数据写入apn数据库。

GsmDataConnectionTracker切换APN

Settings app更新apn数据库后,GsmDataConnectionTracker注册的ApnChangeObserver的onChange被调用,发送EVENT_APN_CHANGED消息:

sendMessage(obtainMessage(EVENT_APN_CHANGED));

接着onApnChanged()@GsmDataConnectionTracker.java被调用

  -trySetupData(Phone.REASON_APN_CHANGED)@GsmDataConnectionTracker.java
    --setupData(String reason)@GsmDataConnectionTracker.java

    private boolean setupData(String reason) {
    ApnSetting apn;
    GsmDataConnection pdp;

    apn = getNextApn();
    if (apn == null) return false;

    //获取一个状态为inactive的pdp连接对象
    pdp = findFreePdp();
    if (pdp == null) {
        if (DBG) log("setupData: No free GsmDataConnection found!");
        return false;
    }
    mActiveApn = apn;
    mActivePdp = pdp;

    Message msg = obtainMessage();
    msg.what = EVENT_DATA_SETUP_COMPLETE;
    msg.obj = reason;

    //开始激活这个pdp
    //在android2.0.1版本时,有一个PdpConnection.java来进行连接,2.2时这个类被删掉了,connect的功能合并到GsmDataConnection里面
    pdp.connect(msg, apn);

    //设置这个pdp连接状态为INITING
    setState(State.INITING);
    if (DBG) log("setupData for reason: "+reason);

    //通知上层应用数据连接状态改变
    phone.notifyDataConnection(reason);
    return true;
}

其中,在这一层,数据连接共七个状态:

    IDLE,
    INITING,
    CONNECTING,
    SCANNING,
    CONNECTED,
    DISCONNECTING,
    FAILED

对上层应用来说,这七个状态划分为四种状态(getDataConnectionState()@GSMPhone.java):

CONNECTED, CONNECTING, DISCONNECTED, SUSPENDED;

分别对应TelephonyManager的四种连接状态。

激活PDP连接

开始激活PDP连接时,设置状态为State.INITING,调用phone.notifyDataConnection(reason)发出通知,后续调用过程为:

    notifyDataConnection(String reason)@PhoneBase.java
      --notifyDataConnection(Phone sender, String reason)@DefaultPhoneNotifier.java
        .
        .   这里需要经过IPC调用
        .
        notifyDataConnection()@TelephonyRegistry.java
          --onDataConnectionStateChanged()

当连接成功后,onDataSetupComplete()@GsmDataConnectionTracker.java被调用 通过phone.notifyDataConnection(reason);回调应用层的onDataConnectionStateChanged()方法。

ps:可以通过adb logcat -b radio查看激活数据连接时,radio层的log输出。

Android关机界面代码

| Comments

Android中长按Power键默认会弹出对话框让用户选择“飞行模式”,“静音”,“关机”等功能,弹出对话框的代码位于: frameworks\policies\base\phone\com\android\internal\policy\impl\PhoneWindowManager.java

private final Runnable mPowerLongPress = new Runnable() {
    public void run() {
        // The context isn't read
        if (mLongPressOnPowerBehavior < 0) {
            mLongPressOnPowerBehavior = mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_longPressOnPowerBehavior);
        }
        switch (mLongPressOnPowerBehavior) {
        case LONG_PRESS_POWER_NOTHING:
            break;
        case LONG_PRESS_POWER_GLOBAL_ACTIONS:
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
            showGlobalActionsDialog();
            break;
        case LONG_PRESS_POWER_SHUT_OFF:
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
            ShutdownThread.shutdown(mContext, true);
            break;
        }
    }
};

void showGlobalActionsDialog() {
    if (mGlobalActions == null) {
        mGlobalActions = new GlobalActions(mContext);
    }
    final boolean keyguardShowing = mKeyguardMediator.isShowingAndNotHidden();
    mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
    if (keyguardShowing) {
        // since it took two seconds of long press to bring this up,
        // poke the wake lock so they have some time to see the dialog.
        mKeyguardMediator.pokeWakelock();
    }
}

HTTP/1.1 Cache-Control的理解

| Comments

网页的缓存是由HTTP消息头中的“Cache-control”来控制的,常见的取值有 * private * no-cache * max-age * must-revalidate

等,默认为private。其作用根据不同的重新浏览方式分为以下几种情况:

(1) 打开新窗口
如果指定cache-control的值为private、no-cache、must-revalidate,那么打开新窗口访问时都会重新访问服务器。而如果指定了max-age值,那么在此值内的时间里就不会重新访问服务器,例如:

Cache-control: max-age=5

表示当访问此网页后的5秒内再次访问不会向服务器发起请求

(2) 在地址栏输入地址回车
如果值为private或must-revalidate,则只有第一次访问时会访问服务器,以后就不再访问。如果值为no-cache,那么每次都会访问。如果值为max-age,则在过期之前不会重复访问。

(3) 按后退按扭
如果值为private、must-revalidate、max-age,则不会重访问,而如果为no-cache,则每次都重复访问

(4) 按刷新按扭
无论为何值,都会重复访问

cache-control字段取值含义: /images/http_cache_control.jpg

Reference:
http://www.blogjava.net/dashi99/archive/2008/12/30/249207.html