android源码分析精典
Android开发—SeeJoPlayer视频播放器源码解析
2020.03.27 v1.2.0 beta版:更新说明:一、完美支持android1.五、android1.六、android2.0、android2.0一、android2.1平台;2、完美支持320×480、480×800、480×854等各类分辨率(自适应屏幕分辨率);3、支持在线音视频播放,支持URL input和从浏览器调用SeeJoPlayer播放器播放在线音视频;4、自动转为横屏播放,为用户提供更好的观看体验;5、修改了没有SD卡程序出错的Bug;6、美化了视频播放列表和操作说明的界面。
第一部份:功能介绍SeeJoPlayer的优势要紧在相对还算美观的界面和便利的交互操作上。
先说操作吧,它支持:一、全屏切换: 双击屏幕二、播放/暂停: 长按屏幕3、静音/恢复: 长按音量按钮4、播放列表: 操纵面板最右边的按钮(暂不支持编辑功能)五、音量调剂: 单击音量按钮,在弹出的音量显示区域触摸改变音量这些操作和PC上的播放器较为类似,希望大伙儿能用得适应。
至于界面的话,多说无益,直接上图吧:第二部份:源码解析一、VideoView与视频比例缩放:咱们能够很方便的取得VideoView的源代码,最简单的方式是直接在上找“.java”。
因此重写VideoView的进程其实只是在原先的基础上进行一些修改罢了,并非一个很麻烦的工作。
什么缘故Android自带的VideoView会维持视频的长宽比而不能让咱们很方便的自概念比例呢?我猜想可能Google 做Android也是一个很仓促的工程,许多代码并无考虑得太成熟。
VideoView的源码中有如此一段代码:1 @Override2 3 4 5 6 7 8 9101112131415161718192021222324252627protectedvoid onMeasure(int widthMeasureSpec, int heightMeasureSpec){//Log.i("@@@@", "onMeasure");int width = getDefaultSize(mVideoWidth, widthMeasureSpec);int height = getDefaultSize(mVideoHeight, heightMeasureSpec);if(mVideoWidth >0&& mVideoHeight >0){if( mVideoWidth * height > width * mVideoHeight ){//Log.i("@@@", "image too tall, correcting");height = width * mVideoHeight / mVideoWidth;}elseif( mVideoWidth * height < width * mVideoHeight ){//Log.i("@@@", "image too wide, correcting");width = height * mVideoWidth / mVideoHeight;}else{//Log.i("@@@", "aspect ratio is correct: " +//width+"/"+height+"="+//mVideoWidth+"/"+mVideoHeight);}}//Log.i("@@@@@@@@@@", "setting size: " + width + 'x' + height); setMeasuredDimension(width, height);}这确实是什么缘故长宽比不能改变的缘故了。
Android日志系统Logcat源代码简要分析
在前面两篇文章Android日志系统驱动程序Logger源代码分析和Android应用程序框架层和系统运行库层日志系统源代码中,介绍了Android内核空间层、系统运行库层和应用程序框架层日志系统相关的源代码,其中,后一篇文章着重介绍了日志的写入操作。
为了描述完整性,这篇文章着重介绍日志的读取操作,这就是我们在开发Android应用程序时,经常要用到日志查看工具Logcat了。
Logcat工具内置在Android系统中,可以在主机上通过adb logcat命令来查看模拟机上日志信息。
Logcat工具的用法很丰富,因此,源代码也比较多,本文并不打算完整地介绍整个Logcat工具的源代码,主要是介绍Logcat读取日志的主线,即从打开日志设备文件到读取日志设备文件的日志记录到输出日志记录的主要过程,希望能起到一个抛砖引玉的作用。
Logcat工具源代码位于system/core/logcat目录下,只有一个源代码文件logcat.cpp,编译后生成的可执行文件位于out/target/product/generic/system/bin目录下,在模拟机中,可以在/system/bin目录下看到logcat工具。
下面我们就分段来阅读logcat.cpp源代码文件。
一. Logcat工具的相关数据结构。
这些数据结构是用来保存从日志设备文件读出来的日志记录:view plain1.struct queued_entry_t {2.union {3. unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));4.struct logger_entry entry __attribute__((aligned(4)));5. };6. queued_entry_t* next;7.8. queued_entry_t() {9. next = NULL;10. }11.};12.13.struct log_device_t {14.char* device;15.bool binary;16.int fd;17.bool printed;18.char label;19.20. queued_entry_t* queue;21. log_device_t* next;22.23. log_device_t(char* d, bool b, char l) {24. device = d;25. binary = b;26. label = l;27. queue = NULL;28. next = NULL;29. printed = false;30. }31.32.void enqueue(queued_entry_t* entry) {33.if (this->queue == NULL) {34.this->queue = entry;35. } else {36. queued_entry_t** e = &this->queue;37.while (*e && cmp(entry, *e) >= 0) {38. e = &((*e)->next);39. }40. entry->next = *e;41. *e = entry;42. }43. }44.};其中,宏LOGGER_ENTRY_MAX_LEN和struct logger_entry定义在system/core/include/cutils/logger.h文件中,在Android应用程序框架层和系统运行库层日志系统源代码分析一文有提到,为了方便描述,这里列出这个宏和结构体的定义:view plain1.struct logger_entry {2. __u16 len; /* length of the payload */3. __u16 __pad; /* no matter what, we get 2 bytes of padding */4. __s32 pid; /* generating process's pid */5. __s32 tid; /* generating process's tid */6. __s32 sec; /* seconds since Epoch */7. __s32 nsec; /* nanoseconds */8.char msg[0]; /* the entry's payload */9.};10.11.#define LOGGER_ENTRY_MAX_LEN (4*1024)从结构体struct queued_entry_t和struct log_device_t的定义可以看出,每一个log_device_t都包含有一个queued_entry_t队列,queued_entry_t就是对应从日志设备文件读取出来的一条日志记录了,而log_device_t则是对应一个日志设备文件上下文。
Android应用程序绑定服务(bindService)的过程源代码分析
Android应用程序组件Service与Activity一样,既可以在新的进程中启动,也可以在应用程序进程内部启动;前面我们已经分析了在新的进程中启动Service的过程,本文将要介绍在应用程序内部绑定Service的过程,这是一种在应用程序进程内部启动Service的方法。
在前面一篇文章Android进程间通信(IPC)机制Binder简要介绍和学习计划中,我们就曾经提到,在Android系统中,每一个应用程序都是由一些Activity和Service组成的,一般Service运行在独立的进程中,而Activity有可能运行在同一个进程中,也有可能运行在不同的进程中;在接下来的文章中,Android系统在新进程中启动自定义服务过程(startService)的原理分析一文介绍了在新的进程中启动Service的过程,Android应用程序启动过程源代码分析一文介绍了在新的进程中启动Activity的过程,而Android应用程序内部启动Activity 过程(startActivity)的源代码分析一文则介绍了在应用程序进程内部启动Activity的过程;本文接过最后一棒,继续介绍在应用程序进程内部启动Service的过程,这种过程又可以称在应用程序进程内部绑定服务(bindService)的过程,这样,读者应该就可以对Android应用程序启动Activity和Service有一个充分的认识了。
这里仍然是按照老规矩,通过具体的例子来分析Android应用程序绑定Service的过程,而所使用的例子便是前面我们在介绍Android 系统广播机制的一篇文章Android系统中的广播(Broadcast)机制简要介绍和学习计划中所开发的应用程序Broadcast了。
我们先简单回顾一下这个应用程序实例绑定Service的过程。
在这个应用程序的MainActivity的onCreate函数中,会调用bindService 来绑定一个计数器服务CounterService,这里绑定的意思其实就是在MainActivity内部获得CounterService的接口,所以,这个过程的第一步就是要把CounterService启动起来。
android7.x Launcher3源码解析(3)---workspace和allapps加载流程
android7.x Launcher3源码解析(3)---workspace和allapps加载流程1、整体流程先上一张整体的流程图吧。
(图片看不清可以下载下来看或者右击新开个页面查看图片)先从Launcher.Java的onCreate方法开始,protected void onCreate(Bundle savedInstanceState) {......//建立LauncherAppState对象LauncherAppState.setApplicationContext(getApplicationContext());LauncherAppState app = LauncherAppState.getInstance();......//建立LauncherModel对象mModel = app.setLauncher(this);//一些其他对象初始化......setContentView(uncher);setupViews();if (!mRestoring) {if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {// If the user leaves launcher, then we should just load items asynchronously when// they return.mModel.startLoader(PagedView.INV ALID_RESTORE_PAGE);} else {// We only load the page synchronously if the user rotates (or triggers a// configuration change) while launcher is in the foregroundmModel.startLoader(mWorkspace.getRestorePage());}}......}重点调用了LauncherModel的startLoader的方法,startLoader里面,最重要的就是启动了LoaderTask,mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);我们接着分析LoaderTask的run方法。
深入理解Android5源代码
深⼊理解Android5源代码深⼊理解Android 5 源代码1 Android系统介绍1.1 Android系统成功的秘诀1.1.1 获取了业界的⼴泛⽀持1.1.2 研发阵容强⼤1.1.3 为开发⼈员“精⼼定制”1.1.4 开源1.2 剖析Android系统架构1.2.1 底层操作系统层(OS)1.2.2 各种库(Libraries)和Android运⾏环境(RunTime)1.2.3 ApplicationFramework(应⽤程序框架)1.2.4 顶层应⽤程序(Application)1.3 五⼤组件1.3.1 Activity界⾯1.3.2 Intent和IntentFilters切换1.3.3 Service(服务)1.3.4 BroadcastReceiver发送⼴播1.3.5 ⽤ContentProvider存储数据1.4 进程和线程1.4.1 什么是进程1.4.2 什么是线程2 获取并编译Android源代码2.1 获取Android源代码2.1.1 在Linux系统中获取Android源代码2.1.2 在Windows平台获取Android源代码2.2 分析Android源代码结构2.2.1 总体结构2.2.2 应⽤程序部分2.2.3 应⽤程序框架部分2.2.4 系统服务部分2.2.5 系统程序库部分2.2.6 硬件抽象层部分2.3 Android源代码提供的接⼝2.3.1 暴露接⼝和隐藏接⼝2.3.2 调⽤隐藏接⼝2.4 编译源代码2.4.1 搭建编译环境2.4.2 在模拟器中运⾏2.5 编译源代码⽣成SDK3 分析Java Native Interface系统3.1 JNI基础3.1.1 JNI的功能结构3.1.2 JNI的调⽤层次3.1.3 分析JNI的本质3.2 分析MediaScanner3.2.1 分析Java层3.2.2 分析JNI层3.2.3 分析Native(本地)层3.3 分析Camera系统的JNI3.3.1 Java层预览接⼝3.3.2 注册预览的JNI函数3.3.3 C/C++层的预览函数4 分析HAL系统4.1 HAL基础4.1.1 推出HAL的背景4.1.2 HAL的基本结构4.2 分析HAL module架构4.2.1 hw_module_t4.2.2 结构hw_module_methods_t的定义4.2.3 hw_device_t结构4.3 分析⽂件hardware.c4.3.1 寻找动态链接库的地址4.3.2 数组variant_keys4.3.3 载⼊相应的库4.3.4 获得hw_module_t结构体4.4 分析硬件抽象层的加载过程4.5 分析硬件访问服务4.5.1 定义硬件访问服务接⼝4.5.2 具体实现4.6 分析Android官⽅实例4.6.1 获取实例⼯程源代码4.6.2 直接调⽤Service⽅法的实现代码4.6.3 通过Manager调⽤Service的实现代码4.7 HAL和系统移植4.7.1 移植各个Android部件的⽅式4.7.2 设置设备权限4.7.3 init.rc初始化4.7.4 ⽂件系统的属性5 分析IPC通信机制5.1 Binder机制概述5.2 分析Binder驱动程序5.2.1 分析数据结构5.2.2 分析设备初始化5.2.3 打开Binder设备⽂件5.2.4 内存映射5.2.5 释放物理页⾯5.2.6 分配内核缓冲区5.2.7 释放内核缓冲区5.2.8 查询内核缓冲区5.3 Binder封装库5.3.1 类BBinder5.3.2 类BpRefBase5.3.3 类IPCThreadState5.4 初始化Java层Binder框架5.5 分析MediaServer的通信机制5.5.1 MediaServer的⼊⼝函数5.5.2 ProcessState5.5.3 defaultServiceManager5.5.4 注册MediaPlayerService5.5.5 分析StartThread Pool和join Thread Pool6 分析Binder对象和Java接⼝6.1 分析实体对象(binder_node)6.2 分析本地对象(BBinder)6.3 分析引⽤对象(binder_ref)6.4 分析代理对象(BpBinder)6.5 分析Java接⼝6.5.1 获取Service Manager6.5.2 分析ActivityManagerService的Java层7 分析ServiceManager和MessageQueue 7.1 分析ServiceManager7.1.1 分析主⼊⼝函数7.1.2 打开Binder设备⽂件7.1.3 注册处理7.1.4 创建Binder实体对象7.1.5 尽职的循环7.1.6 将信息注册到ServiceManager7.1.7 分析MediaPlayerService和Client7.2 获得Service Manager接⼝7.3 分析MessageQueue7.3.1 创建MessageQueue7.3.2 提取消息7.3.3 分析函数nativePollOnce8 init进程和Zygote进程8.1 分析init进程8.1.1 分析⼊⼝函数8.1.2 分析配置⽂件8.1.3 分析Service8.1.4 解析on字段的内容8.1.5 init控制Service8.1.6 控制属性服务8.2 分析Zygote(孕育)进程8.2.1 Zygote基础8.2.2 分析Zygote的启动过程9 System进程和应⽤程序进程9.1 分析System进程9.1.1 启动System进程前的准备⼯作9.1.2 分析SystemServer9.1.3 分析EntropyService9.1.4 分析DropBoxManagerService9.1.5 分析DiskStatsService9.1.6 分析DeviceStorageManagerService(监测系统内存存储空间的状态)9.1.7 分析SamplingProfilerService9.2 分析应⽤程序进程9.2.1 创建应⽤程序9.2.2 启动线程池9.2.3 创建信息循环10 分析Activity组件10.1 Activity基础10.1.1 Activity状态10.1.2 剖析Activity中的主要函数10.2 分析Activity的启动源代码10.2.1 Launcher启动应⽤程序10.2.2 返回ActivityManagerService的远程接⼝10.2.3 解析intent的内容10.2.4 分析检查机制10.2.5 执⾏Activity组件的操作10.2.6 将Launcher推⼊Paused状态10.2.7 处理消息10.2.8 暂停完毕10.2.9 建⽴双向连接10.2.10 启动新的Activity10.2.11 通知机制10.2.12 发送消息11 应⽤程序管理服务——PackageManagerService分析11.1 PackageManagerService概述11.2 系统进程启动11.3 开始运⾏11.4 扫描APK⽂件11.5 解析并安装⽂件11.6 启动系统默认Home应⽤程序Launcher11.6.1 设置系统进程11.6.2 启动Home应⽤程序11.6.3 启动uncher11.6.4 加载应⽤程序11.6.5 获得Activity12 Content Provider存储机制12.1 Content Provider基础12.1.1 ContentProvider在应⽤程序中的架构12.1.2 ContentProvider的常⽤接⼝12.2 启动Content Provider12.2.1 获得对象接⼝12.2.2 存在校验12.2.3 启动Android应⽤程序12.2.4 根据进程启动Content Provider 12.2.5 处理消息12.2.6 具体启动12.3 Content Provider数据共享12.3.1 获取接⼝12.3.2 创建CursorWindow对象12.3.3 数据传递12.3.4 处理进程通信的请求12.3.5 数据操作13 分析⼴播机制源代码13.1 Broadcast基础13.2 发送⼴播信息13.2.1 intent描述指⽰13.2.2 传递⼴播信息13.2.3 封装传递13.2.4 处理发送请求13.2.5 查找⼴播接收者13.2.6 处理⼴播信息13.2.7 检查权限13.2.8 处理的进程通信请求13.3 分析BroadCastReceiver13.3.1 MainActivity的调⽤13.3.2 注册⼴播接收者13.3.3 获取接⼝对象13.3.4 处理进程间的通信请求14 分析电源管理系统14.1 Power Management架构基础14.2 分析Framework层14.2.1 ⽂件PowerManager.java14.2.2 提供PowerManager功能14.3 JNI层架构分析14.3.1 定义了两层之间的接⼝函数14.3.2 与Linux Kernel层进⾏交互14.4 Kernel(内核)层架构分析14.4.1 ⽂件power.c14.4.2 ⽂件earlysuspend.c14.4.3 ⽂件wakelock.c14.4.4 ⽂件resume.c14.4.5 ⽂件suspend.c14.4.6 ⽂件main.c14.4.7 proc⽂件14.5 wakelock和early_suspend14.5.1 wakelock的原理14.5.2 early_suspend的原理14.5.3 Android休眠14.5.4 Android唤醒14.6 Battery电池系统架构和管理14.6.1 实现驱动程序14.6.2 实现JNI本地代码14.6.3 Java层代码14.6.4 实现Uevent部分14.7 JobScheduler节能调度机制14.7.1 JobScheduler机制的推出背景14.7.2 JobScheduler的实现14.7.3 实现操作调度14.7.4 封装调度任务15 分析WindowManagerService系统15.1 WindowManagerService基础15.2 计算Activity窗⼝的⼤⼩15.2.1 实现View遍历15.2.2 函数relayoutWindow15.2.3 函数relayoutWindow15.2.4 拦截消息的处理类15.2.5 判断是否计算过16 分析电话系统16.1 Android电话系统详解16.1.1 电话系统简介16.1.2 电话系统结构16.1.3 驱动程序介绍16.1.4 RIL接⼝16.1.5 分析电话系统的实现流程16.2 电话系统中的⾳频模块16.2.1 ⾳频系统结构16.2.2 分析⾳频系统的层次16.3 分析拨号流程16.3.1 拨号界⾯16.3.2 实现Phone应⽤16.3.3 Call通话控制16.3.4 静态⽅法调⽤16.3.5 通话管理16.3.6 dial拨号16.3.7 状态跟踪16.3.8 RIL消息“出/⼊”⼝16.3.9 显⽰通话主界⾯17 分析短信系统17.1 短信系统的主界⾯17.2 发送普通短信17.3 发送彩信17.4 接收短信17.4.1 Java应⽤层的接收流程17.4.2 Framework层的处理过程18 Sensor传感器系统详解18.1 Android传感器系统概述18.2 Java层详解18.3 Frameworks层详解18.3.1 监听传感器的变化18.3.2 注册监听18.4 JNI层详解18.4.1 实现Native(本地)函数18.4.2 处理客户端数据18.4.3 处理服务端数据18.4.4 封装HAL层的代码18.4.5 处理消息队列18.5 HAL层详解19 分析SEAndroid系统19.1 SEAndroid概述19.1.1 内核空间19.1.2 ⽤户空间19.2 ⽂件安全上下⽂19.2.1 设置打包在ROM⾥⾯的⽂件的安全上下⽂19.2.2 设置虚拟⽂件系统的安全上下⽂19.2.3 设置应⽤程序数据⽂件的安全上下⽂19.3 进程安全上下⽂19.3.1 为独⽴进程静态地设置安全上下⽂19.3.2 为应⽤程序进程设置安全上下⽂20 分析ART系统20.1 对⽐Dalvik VM和ART20.2 启动ART20.2.1 运⾏app_process进程20.2.2 准备启动20.2.3 创建运⾏实例20.2.4 注册本地JNI函数20.2.5 启动守护进程20.2.6 解析参数20.2.7 初始化类、⽅法和域20.3 分析主函数main20.4 查找⽬标类20.4.1 函数LookupClass()20.4.2 函数DefineClass()20.4.3 函数InsertClass()20.4.4 函数LinkClass()20.5 类操作20.6 实现托管操作20.7 加载OAT⽂件20.7.1 产⽣OAT20.7.2 创建ART虚拟机20.7.3 解析启动参数并创建堆20.7.4 ⽣成指定⽬录⽂件20.7.5 加载OAT⽂件20.7.6 解析字段思维导图防⽌博客图床图⽚失效,防⽌图⽚源站外链:思维导图在线编辑链接:。
Android的全局键(home键_长按耳机键)详解【android源码解析八】
下面我详细展开来说明一下:
一、先说Home键的实现的大致流程,即---->为什么点击Home键,都进入到launcher的待机界面;
(1)Home键的定义在
step1: frameworks/base/core/java/android/view/KeyEvent.java这个类中,在KeyEvent.java这个类中有个static的静态块:
step3: 而Home键对应的值如下:
/** Key code constant: Home key. * This key is handled by the framework and is never delivered to applications. */ public static final int KEYCODE_HOME = 3;
转载请标明出处:
(一)可以在frameworks层的KeyEvent.java这个类中定义一个值,在PhoneWindowManager.java这个类中做处理就可以了。(Home键就是这么实现的)。效果图如下:
(二)可以利用广播的形式,frameworks层PhoneWindow.java这个类的onKeyDown( )对这个实体键发广播,上层接受这个广播来处理也可以达到这个效果。耳机键就是利用广播来接受的。无论在哪个界面长按耳机键,都会进入到音乐的界面。(长按耳机键的)效果图如下:
/** {@inheritDoc} */ @Override public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) { final boolean keyguardOn = keyguardOn()
分享45个android实例源码
分享45个android实例源码,很好很强大/android-20978-1-1.htmlandriod闹钟源代码/android-20974-1-1.htmlandroid源码分享之指南针程序/android-20973-1-1.html重力感应的测试程序andriod源代码/android-20972-1-1.htmlandroid源码分享之时光日志个人日程管理/android-20969-1-1.htmlOpenGL的一个简单的例子/android-20968-1-1.html文件管理器-android源代码/android-20960-1-1.htmlAndroid Txt文本阅读器源码/android-20959-1-1.htmlAndroid远程登录含有loading登录效果~~完整代码和超级详细注释/android-20958-1-1.htmlAndroid 手电筒源码/android-20957-1-1.htmlAndroid操作数据库实例/android-20954-1-1.htmlandroid 画图程序/android-20953-1-1.htmlAndroid 天气预报加widget源码/android-20950-1-1.htmlAndroid 指南针程序/android-20949-1-1.htmlAndroid 个人记账程序源码Android游戏的心跳效果/android-20939-1-1.htmlAndroid PDF 阅读器源码/android-20858-1-1.htmlAndroid SqliteManager 源码/android-20857-1-1.htmlandroid 多点触控实例源码/android-20856-1-1.htmlAndroid 条码扫描程序源码/android-20855-1-1.htmlEditText插入QQ表情源码/android-20854-1-1.htmlAsyncTask进度条加载网站数据到ListView /android-20834-1-1.htmlandroid连接SQLite数据库-----增加改查+分页/android-20833-1-1.htmlAndroid 一个批量删除联系人的Demo/android-20832-1-1.htmlTXT 文本阅读器源码(android源码分享)/android-20827-1-1.htmlandroid 查询工具源代码/android-20824-1-1.htmlandroid进度条对话框Demo/android-20823-1-1.htmlAndroid实现渐显按钮的左右滑动效果/android-20752-1-1.html android天气预报源码Android 文件浏览器源码/android-20976-1-1.htmlandroid源码分享之私密通讯录源码/android-20975-1-1.htmlAndroid自定义泡泡效果源码/android-20956-1-1.htmlandroid 获取Gps信息的程序源码/android-20955-1-1.htmlandroid 超炫的图片浏览器/android-20952-1-1.htmlandroid 加载时闪烁点样式的启动画面/android-20951-1-1.html实现基于Android的英文电子词典/android-20948-1-1.html基于Android 的英文电子词典/android-20947-1-1.htmlandroid 源码之英语单词记忆程序源码/android-20936-1-1.htmlandorid 源码北京公交线路查询(离线)/android-20938-1-1.htmlAndroid 计算器源码/android-20935-1-1.html带文字的ProgressBar Demo源码/android-20831-1-1.htmlandroid自定义时钟(三种方法实现,秒针效果,详细注解)/android-20830-1-1.htmlAndroid 秒表源码分享/android-20829-1-1.htmlAndroid源代码定时情景模式切换/android-20828-1-1.htmlandroid 公交查询/android-20826-1-1.htmlandroid源码分享之带手势划动功能的日历源码/android-20825-1-1.html。
Android7.0Phone应用源码分析(一)phone拨号流程分析
Android7.0Phone应⽤源码分析(⼀)phone拨号流程分析1.1 dialer拨号拨号盘点击拨号DialpadFragment的onClick⽅法会被调⽤public void onClick(View view) {int resId = view.getId();if (resId == R.id.dialpad_floating_action_button) {view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);handleDialButtonPressed();} else if (resId == R.id.deleteButton) {keyPressed(KeyEvent.KEYCODE_DEL);} else if (resId == R.id.digits) {if (!isDigitsEmpty()) {mDigits.setCursorVisible(true);}} else if (resId == R.id.dialpad_overflow) {mOverflowPopupMenu.show();} else {Log.wtf(TAG, "Unexpected onClick() event from: " + view);return;}}handleDialButtonPressed⽅法处理具体的拨号事件private void handleDialButtonPressed() {........................DialerUtils.startActivityWithErrorToast(getActivity(), intent);hideAndClearDialpad(false);}跟踪DialerUtils的startActivityWithErrorToast⽅法,内部判断了⼀些是否有拨号权限的判断后,最后调⽤TelecomManagerCompat的placeCall事件public static void placeCall(@Nullable Activity activity,@Nullable TelecomManager telecomManager, @Nullable Intent intent) {if (activity == null || telecomManager == null || intent == null) {return;}if (CompatUtils.isMarshmallowCompatible()) {telecomManager.placeCall(intent.getData(), intent.getExtras());return;}activity.startActivityForResult(intent, 0);}这⾥根据当前系统版本如果是⼤于等于6.0,则调⽤TelecomManager的placeCall,否则直接调⽤startActivity呼出该intent看看TelecomManager的placeCall⽅法android.telecom. TelecomManagerpublic void placeCall(Uri address, Bundle extras) {ITelecomService service = getTelecomService();if (service != null) {if (address == null) {Log.w(TAG, "Cannot place call to empty address.");}try {service.placeCall(address, extras == null ? new Bundle() : extras, mContext.getOpPackageName());} catch (RemoteException e) {Log.e(TAG, "Error calling ITelecomService#placeCall", e);}}}通过aidl接⼝调⽤ITelecomService的placeCall⽅法1.2 telecomService处理拨号事件TelecomServiceImpl⾥的mBinderImpl变量是ITelecomService的具体实现类com.android.server.telecom. TelecomServiceImplprivate final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {@Overridepublic void placeCall(Uri handle, Bundle extras, String callingPackage) {………………………………………………………………final UserHandle userHandle = Binder.getCallingUserHandle();long token = Binder.clearCallingIdentity();final Intent intent = new Intent(Intent.ACTION_CALL, handle);if (extras != null) {extras.setDefusable(true);intent.putExtras(extras);mUserCallIntentProcessorFactory.create(mContext, userHandle).processIntent(intent, callingPackage, hasCallAppOp && hasCallPermission);}}这⾥创建了⼀个UserCallIntentProcessor对象,并调⽤其processIntent事件处理,前⾯提到android 6.0以下⽤startActivity启动拨号,三⽅应⽤拨号都是⽤这种⽅式,启动的也是telecom⾥的UserCallActivity类,同样也是通过UserCallIntentProcessor处理相关事件erCallIntentProcessorpublic void processIntent(Intent intent, String callingPackageName,boolean canCallNonEmergency) {// Ensure call intents are not processed on devices that are not capable of calling.if (!isVoiceCapable()) {return;}String action = intent.getAction();if (Intent.ACTION_CALL.equals(action) ||Intent.ACTION_CALL_PRIVILEGED.equals(action) ||Intent.ACTION_CALL_EMERGENCY.equals(action)) {processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);}}进⼊processOutgoingCallIntent⽅法private void processOutgoingCallIntent(Intent intent, String callingPackageName,boolean canCallNonEmergency) {………………………………………………………………sendBroadcastToReceiver(intent);}内部校验⼀些是否能拨号权限以及其它操作限制看是否需要直接弹框拒绝,如果都通过了最后会调⽤sendBroadcastToReceiver⽅法发送⼴播private boolean sendBroadcastToReceiver(Intent intent) {intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);intent.setClass(mContext, PrimaryCallReceiver.class);Log.d(this, "Sending broadcast as user to CallReceiver");mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);return true;}该⼴播直接指定发给PrimaryCallReceiver处理ponents. PrimaryCallReceiverpublic void onReceive(Context context, Intent intent) {Log.startSession("PCR.oR");synchronized (getTelecomSystem().getLock()) {getTelecomSystem().getCallIntentProcessor().processIntent(intent);}Log.endSession();}接着调⽤CallIntentProcessor. processIntent(intent)com.android.server.telecom. CallIntentProcessorpublic void processIntent(Intent intent) {final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);Trace.beginSection("processNewCallCallIntent");if (isUnknownCall) {processUnknownCallIntent(mCallsManager, intent);} else {processOutgoingCallIntent(mContext, mCallsManager, intent);}Trace.endSection();}如果是未知号码如空号由processUnknownCallIntent⽅法处理否则调⽤processOutgoingCallIntent⽅法static void processOutgoingCallIntent(Context context, CallsManager callsManager, Intent intent) {………………………………………………………………// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returnsCall call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);if (call != null) {NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(context, callsManager, call, intent, new PhoneNumberUtilsAdapterImpl(), isPrivilegedDialer);final int result = broadcaster.processIntent();final boolean success = result == DisconnectCause.NOT_DISCONNECTED;if (!success && call != null) {disconnectCallAndShowErrorDialog(context, call, result);}}}⽅法内部获取⼀些拨号参数,⽐如是否视频通话,调⽤者是否是默认拨号盘应⽤等,然后调⽤callsManager的startOutgoingCall⽅法得到⼀个call对象,这是⼀个很重要的⽅法,来看看它的实现:Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,UserHandle initiatingUser) {boolean isReusedCall = true;Call call = reuseOutgoingCall(handle);// 创建⼀个call对象if (call == null) {call = new Call(getNextCallId(), mContext,this,mLock,mConnectionServiceRepository,mContactsAsyncHelper,mCallerInfoAsyncQueryFactory,handle,null/* gatewayInfo */,null/* connectionManagerPhoneAccount */,null/* phoneAccountHandle */,Call.CALL_DIRECTION_OUTGOING /* callDirection */,false/* forceAttachToExistingConnection */,false/* isConference */);call.setInitiatingUser(initiatingUser);call.initAnalytics();isReusedCall = false;}...... ...... ............ ...... ......List<PhoneAccountHandle> accounts = constructPossiblePhoneAccounts(handle, initiatingUser); // 获取当前激活的卡列表Log.v(this, "startOutgoingCall found accounts = " + accounts);if (phoneAccountHandle != null) {if (!accounts.contains(phoneAccountHandle)) {phoneAccountHandle = null;}}// 获取当前应该使⽤哪张卡呼出if (phoneAccountHandle == null && accounts.size() > 0 && !call.isEmergencyCall()) {// No preset account, check if default exists that supports the URI scheme for the// handle and verify it can be used.if(accounts.size() > 1) {// 双激活卡下取通话主卡账户,没有通话主卡则为空PhoneAccountHandle defaultPhoneAccountHandle = mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme(), initiatingUser);if (defaultPhoneAccountHandle != null && accounts.contains(defaultPhoneAccountHandle)) {phoneAccountHandle = defaultPhoneAccountHandle;}} else {// Use the only PhoneAccount that is available// 单激活卡直接取该卡账户)phoneAccountHandle = accounts.get(0);}}call.setTargetPhoneAccount(phoneAccountHandle); // 设置当前通话账户boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);// 检查当前是否允许呼出该电话,⽐如当前已有⼀通电话在正在呼出,// 这时候不允许再呼出⼀路通话(紧急号码除外)if (!isPotentialInCallMMICode && (!isReusedCall &&!makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {// just cancel at this point.Log.i(this, "No remaining room for outgoing call: %s", call);if (mCalls.contains(call)) {// This call can already exist if it is a reused call,// See {@link #reuseOutgoingCall}.call.disconnect();}return null;}// 是否需要弹出双卡选择框(双卡下没有指定账户呼出⾮紧急号码且当前⽆通话主卡)boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 && !call.isEmergencyCall();if (needsAccountSelection) {// 设置当前call状态为等待账户选择// This is the state where the user is expected to select an accountcall.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");// Create our own instance to modify (since extras may be Bundle.EMPTY)extras = new Bundle(extras);extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);} else {// 设置当前call状态为正在连接call.setState(CallState.CONNECTING, phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());}setIntentExtrasAndStartTime(call, extras);// Do not add the call if it is a potential MMI code.if((isPotentialMMICode(handle)||isPotentialInCallMMICode)&& !needsAccountSelection){call.addListener(this);} else if (!mCalls.contains(call)) {// We check if mCalls already contains the call because we could potentially be reusing// a call which was previously added (See {@link #reuseOutgoingCall}).addCall(call);// 添加当前call到call列表}return call;}看看addCall的具体实现:private void addCall(Call call) {Trace.beginSection("addCall");Log.v(this, "addCall(%s)", call);call.addListener(this);mCalls.add(call);// Specifies the time telecom finished routing the call. This is used by the dialer for// analytics.Bundle extras = call.getIntentExtras();extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,SystemClock.elapsedRealtime());updateCallsManagerState();// onCallAdded for calls which immediately take the foreground (like the first call).for (CallsManagerListener listener : mListeners) {if (Log.SYSTRACE_DEBUG) {Trace.beginSection(listener.getClass().toString() + " addCall");}listener.onCallAdded(call);if (Log.SYSTRACE_DEBUG) {Trace.endSection();}}Trace.endSection();}这⾥会遍历call状态变化的观察者并逐个回调通知,这⾥的观察者⽐较多,在callsManager创建的时候注册监听的mListeners.add(mInCallWakeLockController);mListeners.add(statusBarNotifier);mListeners.add(mCallLogManager);mListeners.add(mPhoneStateBroadcaster);mListeners.add(mInCallController);mListeners.add(mCallAudioManager);mListeners.add(missedCallNotifier);mListeners.add(mHeadsetMediaButton);mListeners.add(mProximitySensorManager);这⾥说⼀下mInCallController这个对象,是⼀个InCallController实例,内部封装了与incallui服务的相关操作,实际上就是⼀个远程服务代理类,当callsmanager添加⼀路call时,回调InCallController的onCallAdded⽅法,如下:public void onCallAdded(Call call) {if (!isBoundToServices()) {bindToServices(call);} else {adjustServiceBindingsForEmergency();Log.i(this, "onCallAdded: %s", System.identityHashCode(call));// Track the call if we don't already know about it.addCall(call);for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) {ComponentName componentName = entry.getKey();IInCallService inCallService = entry.getValue();ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call, true/* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar());try {inCallService.addCall(parcelableCall);} catch (RemoteException ignored) {}}}}最后调⽤inCallService的addCall⽅法告诉incallui当前添加了⼀路通话,incallui收到后会拉起界⾯,具体过程在此就不详述了,OK回到前⾯startOutgoingCall的结尾在成功返回⼀个call对象之后,新建⼀个NewOutgoingCallIntentBroadcaster对象,⽤processIntent⽅法处理请求com.android.server.telecom. NewOutgoingCallIntentBroadcasterpublic int processIntent() {………………………………………………………………final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);rewriteCallIntentAction(intent, isPotentialEmergencyNumber);action = intent.getAction();boolean callImmediately = false;if (Intent.ACTION_CALL.equals(action)) {if (isPotentialEmergencyNumber) {if (!mIsDefaultOrSystemPhoneApp) {launchSystemDialer(intent.getData());return DisconnectCause.OUTGOING_CANCELED;} else {callImmediately = true;}}} else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {if (!isPotentialEmergencyNumber) {return DisconnectCause.OUTGOING_CANCELED;}callImmediately = true;} else {return DisconnectCause.INVALID_NUMBER;}if (callImmediately) {String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;boolean speakerphoneOn = mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);int videoState = mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,VideoProfile.STATE_AUDIO_ONLY);mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null, speakerphoneOn, videoState);}UserHandle targetUser = mCall.getInitiatingUser();broadcastIntent(intent, number, !callImmediately, targetUser);return DisconnectCause.NOT_DISCONNECTED;}该⽅法主要处理三种类型的call:普通call Intent.ACTION_CALL系统call Intent.ACTION_CALL_PRIVILEGED紧急呼叫call Intent.ACTION_CALL_EMERGENCY普通call任何应⽤都可以发起,第三⽅应⽤拨号都是使⽤该intent系统call只有系统应⽤才能使⽤紧急呼叫call 同样只有系统应⽤才能使⽤,并且可以在⽆卡状态下呼出对于⼀个Intent.ACTION_CALL_PRIVILEGED的拨号请求,会根据当前号码是否为紧急号码来转化该intent private void rewriteCallIntentAction(Intent intent, boolean isPotentialEmergencyNumber) {String action = intent.getAction();if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {if (isPotentialEmergencyNumber) {action = Intent.ACTION_CALL_EMERGENCY;} else {action = Intent.ACTION_CALL;}intent.setAction(action);}}如果是紧急号码则转化为Intent.ACTION_CALL_EMERGENCY如果不是紧急号码则转化为Intent.ACTION_CALL所以实际上处理call只有两种情况Intent.ACTION_CALL和Intent.ACTION_CALL_EMERGENCY1.对于Intent.ACTION_CALL的处理:如果当前是紧急号码,会校验调⽤者是否为系统默认拨号盘如果是则置变量callImmediately为true,后续直接呼出该电话如果不是则拉起系统默认拨号盘,当前⽅法调⽤返回DisconnectCause.OUTGOING_CANCELED2. 对于Intent.ACTION_CALL_EMERGENCY的处理:直接设置变量callImmediately为true,直接呼出该电话综上所述紧急拨号会直接调⽤CallsManager的placeOutgoingCall⽅法后再进⼊broadcastIntent⽅法,看看该⽅法实现private void broadcastIntent(Intent originalCallIntent, String number,boolean receiverRequired, UserHandle targetUser) {Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);if (number != null) {broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);}broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);checkAndCopyProviderExtras(originalCallIntent, broadcastIntent);mContext.sendOrderedBroadcastAsUser(broadcastIntent,targetUser,android.Manifest.permission.PROCESS_OUTGOING_CALLS,AppOpsManager.OP_PROCESS_OUTGOING_CALLS,receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null,null, // schedulerActivity.RESULT_OK, // initialCodenumber, // initialData: initial value for the result data (number to be modified)null); // initialExtras}发送⼀个Intent.ACTION_NEW_OUTGOING_CALL⼴播,对于⾮紧急拨号,才会⽣成⼀个NewOutgoingCallBroadcastIntentReceive 实例来接收该⼴播NewOutgoingCallBroadcastIntentReceiver内部做了⼀些处理后最后还是调⽤到CallsManager的placeOutgoingCall⽅法,所以该⽅法是去电的关键⽅法com.android.server.telecom. CallsManagerpublic void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,boolean speakerphoneOn, int videoState) {………………………………………………………………if (call.isEmergencyCall()) {// Emergency -- CreateConnectionProcessor will choose accounts automatically// 如果是紧急号码,则取消已指定的通话卡账户call.setTargetPhoneAccount(null);new AsyncEmergencyContactNotifier(mContext).execute();}Final Boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(com.android.internal.R.bool.config_requireCallCapableAccountForHandle);if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {// If the account has been set, proceed to place the outgoing call.// Otherwise the connection will be initiated when the account is set by the user.// 如果是紧急号码或者已指定通话账户,则创建连接call.startCreateConnection(mPhoneAccountRegistrar);} else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false, call.getInitiatingUser()).isEmpty()) {// 如果当前没有激活的卡,则断开此连接// If there are no call capable accounts, disconnect the call.markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,"No registered PhoneAccounts"));markCallAsRemoved(call);}}该⽅法内部做了⼀些设置操作后,确认可以呼出则call的startCreateConnection⽅法com.android.server.telecom.callvoid startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {if (mCreateConnectionProcessor != null) {return;}mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, phoneAccountRegistrar, mContext);mCreateConnectionProcessor.process();}新建了⼀个CreateConnectionProcessor对象,处理连接请求com.android.server.telecom. CreateConnectionProcessorpublic void process() {Log.v(this, "process");clearTimeout();mAttemptRecords = new ArrayList<>();if (mCall.getTargetPhoneAccount() != null) {mAttemptRecords.add(new CallAttemptRecord(mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));}adjustAttemptsForConnectionManager();adjustAttemptsForEmergency();mAttemptRecordIterator = mAttemptRecords.iterator();attemptNextPhoneAccount();}进⼊attemptNextPhoneAccount⽅法private void attemptNextPhoneAccount() {………………………………………………………………if (mCallResponse != null && attempt != null) {PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;// 获取ConnectionServiceWrapper对象mService = mRepository.getService(phoneAccount.getComponentName(),phoneAccount.getUserHandle());if (mService == null) {attemptNextPhoneAccount();} else {mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);mCall.setConnectionService(mService);setTimeoutIfNeeded(mService, attempt);// 已成功获取ConnectionServiceWrapper对象,创建连接mService.createConnection(mCall, this);}} else {DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);notifyCallConnectionFailure(disconnectCause);}}这⾥的mService是ConnectionServiceWrapper实例,实际上就是⼀个包装了绑定远程服务的代理类,看看它的构造⽅法看看ConnectionServiceWrapper的构造函数ConnectionServiceWrapper(ComponentName componentName,ConnectionServiceRepository connectionServiceRepository,PhoneAccountRegistrar phoneAccountRegistrar,CallsManager callsManager,Context context,TelecomSystem.SyncRoot lock,UserHandle userHandle) {super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle);mConnectionServiceRepository = connectionServiceRepository;phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {// TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections// To do this, we must proxy remote ConnectionService objects});mPhoneAccountRegistrar = phoneAccountRegistrar;mCallsManager = callsManager;}这⾥的ConnectionService.SERVICE_INTERFACE就是"android.telecom.ConnectionService"也就是它所绑定的远程服务的action获取该对象后调⽤createConnection⽅法com.android.server.telecom. ConnectionServiceWrapperpublic void createConnection(final Call call, final CreateConnectionResponse response) {Log.d(this, "createConnection(%s) via %s.", call, getComponentName());BindCallback callback = new BindCallback() {@Overridepublic void onSuccess() {...... ...... ...... ............ ...... ...... ......try {mServiceInterface.createConnection(call.getConnectionManagerPhoneAccount(),callId,new ConnectionRequest(call.getTargetPhoneAccount(),call.getHandle(),extras,call.getVideoState(),callId),call.shouldAttachToExistingConnection(),call.isUnknown());}...... ...... ...... ......@Overridepublic void onFailure() {Log.e(this, new Exception(), "Failure to call %s", getComponentName());response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));}};mBinder.bind(callback, call);}这⾥的mBinder对象是ConnectionServiceWrapper的⽗类ServiceBinder⾥的⼀个内部类封装了绑定远程服务的⼀些操作,若当前还未绑定服务,则直接调⽤bindService获取远程服务的aidl接⼝,成功获取到aidl接⼝后将其赋值给mServiceInterface,如下:@Overrideprotected void setServiceInterface(IBinder binder) {if (binder == null) {// We have lost our service connection. Notify the world that this service is done.// We must notify the adapter before CallsManager. The adapter will force any pending// outgoing calls to try the next service. This needs to happen before CallsManager// tries to clean up any calls still associated with this service.handleConnectionServiceDeath();mCallsManager.handleConnectionServiceDeath(this);mServiceInterface = null;} else {mServiceInterface = IConnectionService.Stub.asInterface(binder);addConnectionServiceAdapter(mAdapter);}}最终不管是初次绑定还是之前已绑定服务,调⽤ mBinder.bind(callback, call)成功后都会回到Callback的onSuccess⽅法,接着调⽤远程服务的createConnection⽅法mServiceInterface.createConnection(call.getConnectionManagerPhoneAccount(),callId,new ConnectionRequest(call.getTargetPhoneAccount(),call.getHandle(),extras,call.getVideoState(),callId),call.shouldAttachToExistingConnection(),call.isUnknown());接下来的流程就到了远程服务的createConnection实现了1.3 telecomFramework处理连接请求查找IConnectionService的实现类,是ConnectionService的匿名内部类android.telecom. ConnectionServiceprivate final IBinder mBinder = new IConnectionService.Stub() {@Overridepublic void createConnection(PhoneAccountHandle connectionManagerPhoneAccount,String id,ConnectionRequest request,boolean isIncoming,boolean isUnknown) {SomeArgs args = SomeArgs.obtain();args.arg1 = connectionManagerPhoneAccount;args.arg2 = id;args.arg3 = request;args.argi1 = isIncoming ? 1 : 0;args.argi2 = isUnknown ? 1 : 0;mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();}}Handle处理消息最后进⼊createConnection⽅法private void createConnection(final PhoneAccountHandle callManagerAccount,final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown) {Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request): isIncoming ?onCreateIncomingConnection(callManagerAccount, request) : onCreateOutgoingConnection(callManagerAccount, request);//去电事件在这⾥Log.d(this, "createConnection, connection: %s", connection);if (connection == null) { //创建连接失败connection = Connection.createFailedConnection(new DisconnectCause(DisconnectCause.ERROR));}connection.setTelecomCallId(callId);if (connection.getState() != Connection.STATE_DISCONNECTED) {addConnection(callId, connection); //创建连接成功,添加到队列}Uri address = connection.getAddress();String number = address == null ? "null" : address.getSchemeSpecificPart();Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",Connection.toLogSafePhoneNumber(number),Connection.stateToString(connection.getState()),Connection.capabilitiesToString(connection.getConnectionCapabilities()),Connection.propertiesToString(connection.getConnectionProperties()));Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);mAdapter.handleCreateConnectionComplete(callId,request,new ParcelableConnection(request.getAccountHandle(),connection.getState(),connection.getConnectionCapabilities(),connection.getConnectionProperties(),connection.getAddress(),connection.getAddressPresentation(),connection.getCallerDisplayName(),connection.getCallerDisplayNamePresentation(),connection.getVideoProvider() == null ?null : connection.getVideoProvider().getInterface(),connection.getVideoState(),connection.isRingbackRequested(),connection.getAudioModeIsVoip(),connection.getConnectTimeMillis(),connection.getStatusHints(),connection.getDisconnectCause(),createIdList(connection.getConferenceables()),connection.getExtras(),connection.getUserData()));//MOTO Calling Code - IKPIM-1774 (ftr 33860)if (isUnknown) {triggerConferenceRecalculate();}}这⾥根据来电或去掉创建不同的Connection对象,去电⾛onCreateOutgoingConnection流程,该⽅法返回null,所以具体是由其⼦类实现的,也就是TelephonyConnectionService1.4 telephonyService创建呼出连接public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount,final ConnectionRequest request) {Log.i(this, "onCreateOutgoingConnection, request: " + request);............. ......................... .............// 这⾥会有很多情况会直接返回连接错误,⽐如未指定卡账户拨打语⾳信息,// 或者拨打的号码为空,指定卡被禁⽤(⾮紧急拨号)等final TelephonyConnection connection =createConnectionFor(phone, null, true/* isOutgoing */, request.getAccountHandle(),request.getTelecomCallId(), request.getAddress());............ .............placeOutgoingConnection(connection, phone, request);}return connection;}⽅法内部作了很多预处理,如果失败则直接返回错误连接对象failedConnection,如果成功则创建⼀个TelephonyConnection对象,看看该对象是如何创建的:private TelephonyConnection createConnectionFor(Phone phone, com.android.internal.telephony.Connection originalConnection,boolean isOutgoing,PhoneAccountHandle phoneAccountHandle,String telecomCallId, Uri address) {TelephonyConnection returnConnection = null;int phoneType = phone.getPhoneType();if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {returnConnection = new GsmConnection(originalConnection, telecomCallId);} else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {boolean allowsMute = allowsMute(phone);returnConnection = new CdmaConnection(originalConnection, mEmergencyTonePlayer, allowsMute, isOutgoing, telecomCallId);}if (returnConnection != null) {// Listen to Telephony specific callbacks from the connectionreturnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);returnConnection.setVideoPauseSupported(TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(phoneAccountHandle));boolean isEmergencyCall = (address != null && PhoneNumberUtils.isEmergencyNumber(address.getSchemeSpecificPart()));returnConnection.setConferenceSupported(!isEmergencyCall&& TelecomAccountRegistry.getInstance(this).isMergeCallSupported(phoneAccountHandle));}return returnConnection;如果是GSM,则创建GsmConnection对象,如果是CDMA,则创建CdmaConnection对象,获取到对象connection对象后,最后调⽤placeOutgoingConnection(connection, phone, request);进⼊呼出流程private void placeOutgoingConnection(TelephonyConnection connection, Phone phone, ConnectionRequest request) {String number = connection.getAddress().getSchemeSpecificPart();com.android.internal.telephony.Connection originalConnection;try {originalConnection= phone.dial(number, null, request.getVideoState(), request.getExtras());} catch (CallStateException e) {Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;if (e.getError() == CallStateException.ERROR_DISCONNECTED) {cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;}connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(cause, e.getMessage()));return;}………………………………………………………………。
Android 5.0 Camera系统源码分析(1):CameraService启动流程
Android 5.0 Camera系统源码分析(1):CameraService启动流程1. 前言本文将分析Android系统源码,从frameworks层到hal层,暂不涉及app层和kernel层。
由于某些函数比较复杂,在贴出代码时会适当对其进行简化。
本文属于自己对源码的总结,仅仅是贯穿代码流程,不会深入分析各个细节。
分析android系统源码,需要对android系统的某些知识点有所了解2. frameworks层Android的各个子模块的启动都是从它们的Service的启动开始的,所以我们将从CameraService的启动开始分析。
CameraService的启动就在MediaServer的main函数中,代码路径在:frameworks/av/media/mediaserver/main_mediaserver.cpp[cpp] view plain copyint main(int argc __unused, char** argv){......CameraService::instantiate();......}CameraService类定义如下:[cpp] view plain copyclass CameraService :public BinderService<CameraService>,public BnCameraService,public IBinder::DeathRecipient,public camera_module_callbacks_t{static char const* getServiceName() { return "media.camera"; }......}mediaserver的main函数中调用了CameraService的instantiate函数来创建实例,该函数的实现在其父类BinderService中实现[cpp] view plain copytemplate<typename SERVICE>class BinderService{static status_t publish(bool allowIsolated = false) {sp<IServiceManager> sm(defaultServiceManager());return sm->addService(String16(SERVICE::getServiceName()),new SERVICE(), allowIsolated);}static void instantiate() { publish(); }}1. instantiate函数只是简单的调用了publish函数2. publish函数先构造CameraService,再通过addService函数将它注册到ServiceManager当中,而getServiceName函数获取到的值为“media camera”。
Android超大图长图浏览库SubsamplingScaleImageView源码解析
Android超⼤图长图浏览库SubsamplingScaleImageView源码解析⼀开始没打算分析 SubsamplingScaleImageView 这个开源的图⽚浏览器的,因为这个库在我们 App 中使⽤了,觉得⾃⼰对这个库还是⽐较熟悉的,结果某天再看看到源码介绍的时候,才发现⾃⼰对其了解并不够深⼊,所以这才打算再细细看看源码的实现,同时记录⽅便以后回顾。
那么 SubsamplingScaleImageView 有啥优点呢?1. 采⽤ GestureDetector 进⾏⼿势控制,⽀持图⽚的点击,双击,滑动等来控制的放⼤缩⼩;2. 使⽤了 BitmapRegionDecoder,具有分块加载功能;3. ⽀持查看长图,超⼤图上⾯的优点简直就是⾮常实⽤,基本上拿来就可以直接⽤,简单省⼒。
下⾯就是要来分析,它是如何满⾜这些优点的。
源码分析⾸先附上源码地址:使⽤说明如果可以拿到图⽚的资源id,assert或者⽂件路径,直接使⽤下⾯⽅式进⾏使⽤:SubsamplingScaleImageView imageView = (SubsamplingScaleImageView)findViewById(id.imageView);imageView.setImage(ImageSource.resource(R.drawable.monkey));// ... or ...imageView.setImage(ImageSource.asset("map.png"))// ... or ...imageView.setImage(ImageSource.uri("/sdcard/DCIM/DSCM00123.JPG"));如果可以拿到 bitmap 就可以这么使⽤:SubsamplingScaleImageView imageView = (SubsamplingScaleImageView)findViewById(id.imageView);imageView.setImage(ImageSource.bitmap(bitmap));可以看到使⽤是⾮常简单的。
android源码解析------Media多媒体framework层分析
android源码解析------Media多媒体framework层分析1.packages\providers\MediaProvider :含以下 java 文件MediaProvider.javaMediaScannerReceiver.javaMediaScannerService.javaMediaThumbRequest.java2.查看该目录下AndroidMainfest.xml,从MediaScannerService.java 入手。
if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {//收到” 启动完毕“广播后,扫描内部存储/system/media目录,扫描手机内存中的媒体文件scan(context, MediaProvider.INTERNAL_VOLUME);}if (action.equals(Intent.ACTION_MEDIA_MOUNTED) &&externalStoragePath.equals(path)) {// 收到MOUNT 信息后,扫描外部存储,/mnt/sdcard,sdcard 挂载完毕后扫描扩展卡的媒体文件scan(context, MediaProvider.EXTERNAL_VOLUME);}if(action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) && path != null && path.startsWith(externalStoragePath + "/")) { // 收到请求启动单个文件的扫描工作,注意这个文件必须位于SD 卡上。
scanFile(context, path);}通过scan(...),scanFile(...)函数启动MediaScannerService.context.startService(newIntent(context,MediaScannerService.class).putExtras(args));3.MediaScannerService.javaonCreate(...)函数开启新的线程。
android包管理服务(PackageManagerService)源码分析
具 dexopt 进行优化处理;将解析出的 componet 添加到 pkg 的对应列表里; 对 apk 进行签名和证书校验,进行完整性验证。 8.将解析的每个 apk 的信息保存到 packages.xml 和 packages.list 文件里, packages.list 记录了如下数据:pkgName,userId,debugFlag,dataPath(包的数据路径)
在 Systemserver 启动 PackageManagerService.main
new PackageManagerDervice(), 并添加到 ServiceManager
建立 Installer 与 Installd 的 socket 联接
Байду номын сангаас
创建 PackageHandler 消息处理循环
Android PackageManagerService 分析(1.0) 概要
本篇主要分析了系统启动阶段包管理服务的启动流程,其中的几个接口在 apk 安装时也会 被调用。包管理服务启动时主要做的工作大致有如下几方面: 1.建立 java 层的 installer 与 c 层的 installd 的 socket 联接,使得在上层的 install,remove,dexopt 等功能最终由 installd 在底层实现 2. 建 立 PackageHandler 消 息 循 环 , 用 于 处 理 外 部 的 apk 安 装 请 求 消 息 , 如 adb install,packageinstaller 安装 apk 时会发送消息 3.解析/system/etc/permission 下 xml 文件(framework/base/data/etc/),包括 platform.xml 和系统 支持的各种硬件模块的 feature.主要工作: (1)建立底层 user ids 和 group ids 同上层 permissions 之间的映射;可以指定一个权限与几个 组 ID 对应。当一个 APK 被授予这个权限时,它也同时属于这几个组。 (2) 给一些底层用户分配权限,如给 shell 授予各种 permission 权限;把一个权限赋予一个 UID,当进程使用这个 UID 运行时,就具备了这个权限。 (3) library,系统增加的一些应用需要 link 的扩展 jar 库; (4) feature, 系 统 每 增 加 一 个 硬 件 , 都 要 添 加 相 应 的 feature. 将 解 析 结 果 放 入 mSystemPermissions,mSharedLibraries,mSettings.mPermissions,mAvailableFeatures 等几个集合 中供系统查询和权限配置使用。 4.检查/data/system/packages.xml 是否存在,这个文件是在解析 apk 时由 writeLP()创建的,里面记录了系统的 permissions,以及每个 apk 的 name,codePath,flags,ts,version,uesrid 等信息,这些信息主要通过 apk 的 AndroidManifest.xml 解析获取,解析完 apk 后将更新信息写入这个文件并保 存到 flash,下次开机直接从里面读取相关信息添加到内存相关列表中。当有 apk 升级,安装或删除时会更新这个文件。 5.检查 BootClassPath,mSharedLibraries 及/system/framework 下的 jar 是否需要 dexopt,需要的则通过 dexopt 进行优化 6.启动 AppDirObserver 线程监测/system/framework,/system/app,/data/app,/data/ app-private 目录的事件,主要监听 add 和 remove 事件。对于目录监听底层通过 inotify 机制实现,inotify 是一种文件系统的变化通知机制,如文件增加、删除 等事件可以立刻让用户态得知,它为用户态监视文件系统的变化提供了强大的支持。 当有 add event 时调用 scanPackageLI(File , int , int)处理; 当有 remove event 时调用 removePackageLI()处理; 7.对于以上几个目录下的 apk 逐个解析,主要是解析每个 apk 的 AndroidManifest.xml 文件,处理 asset/res 等资源文件,建立起每个 apk 的配置结构信息, 并将每个 apk 的配置信息添加到全局列表进行管理。调用 installer.install()进 行安装工作,检查 apk 里的 dex 文件是否需要再优化,如果需要优化则通过辅助工
Android_launcher源码全面分析
launcher:className="com.apical.radio.radioMainActivity" //点击图标时,需要启动的类 launcher:packageName="com.apical.radio" launcher:screen="1" launcher:x="0" l0auncher:y="0" /> //该应用的包名 //第1屏,0-4屏共5屏 //图标X位置,左上角第一个为0,向左递增,0-4共5个 //图标Y位置,左上角第一个为0,向下递增,0-2共3个
Launcher默认是有5个分屏,不过这个可以配置。同样,每行每列有多少图标也是可以配置 的。这里按我修改的是3行5列的界面排布(对应上面的效果图)。 一般配置APP的快捷方式,使用上面的属性标签就可以。
<appwidget //插件
launcher:className="de.dnsproject.clock_widget_main.Clock1AppWidgetProvider" //该应用的类 launcher:packageName="de.dnsproject.clock_widget_main" launcher:screen="1" launcher:x="2" launcher:y="1" launcher:spanX="3" launcher:spanY="2" /> //第1屏,0-4屏共5屏 //图标X位置,左上角第一个为0,向左递增,0-4共5个 //图标Y位置,左上角第一个为0,向下递增,0-2共3个 //在x方向上所占格数 //在y方向上所占格数 //该应用的包名
Android结合源码分析Power按键处理流程
Android结合源码分析Power按键处理流程 这是之前团队进⾏技术交流时,我选择的⼀个主题,那段时间解决power锁屏按键的bug,搞得头⼤,所以借此机会结合Android8.0源码去分析Power键的处理流程,也将此分享出来,希望对⼤家有所帮助,本⽂为博主原创⽂章,有不对的地⽅,欢迎⼤家指正!作者:版权声明:本⽂为博主原创⽂章,转载请注明出处,谢谢! Android系统中,⼀般的按键都可以在应⽤中处理,但是,对于系统级别的按键上层应⽤是⽆法收到消息的,也就是说,你的APP是⽆法直接处理的。
针对这种系统级的按键事件,都是在Event事件分发前处理。
Event事件分发后,只有包含有Activity的APP才能处理事件;若APP⽆法处理,则需要在PhoneWindowManager中处理。
本⽂所讲的Power键则属于该种情况。
即⽤户触发Power键,底层收到按键会回调InputMonitor的函数dispatchUnhandledKey()。
⼀、为何最终处理者是PhoneWindowManager? 通过上⽂可知最终事件的处理是由PhoneWindowManager完成的;那么,按键后,系统是如何传递到PhoneWindowManager?下⾯就从源码的⾓度分析⼀下该过程。
WindowManagerService:Framework 最核⼼的服务之⼀,负责窗⼝管理。
InputManagerService:输⼊管理服务。
上述两个服务与Power按键相关,但是两者是如何关联的,就要从它们的创建说起.我们都知道,Android系统中的核⼼进程是system_server,对应SystemServer类,在其run()⽅法中会启动⼀堆的service,当然包括上述两个服务。
具体源码分析如下: 1、先创建inputManager,再创建WindowManagerService对象时,可发现作为参数引⽤了上述inputManager,且创建了PhoneWindowManager实例:源码路径:frameworks/base/services/java/com/android/server/SystemServer.javaprivate void startOtherServices() {......inputManager = new InputManagerService(context); //输⼊系统服务【step_SystemServer_1】...... //【step_SystemServer_2】wm = WindowManagerService.main(context, inputManager,mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,!mFirstBoot, mOnlyCore, new PhoneWindowManager()); //PhoneWindowManager实例ServiceManager.addService(Context.WINDOW_SERVICE, wm);ServiceManager.addService(Context.INPUT_SERVICE, inputManager);} 源码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.javaprivate WindowManagerService(Context context, InputManagerService inputManager,boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,WindowManagerPolicy policy) {......mPolicy = policy; //实例: PhoneWindowManager对象【step_InputMonitor_2】 ......} 2、启动inputManager之前,设置了⼀个回调接⼝: //消息分发之前回调--->查看InputManagerService inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); 3、InputMonitor.java:【底层是C/C++相关的,博主对C也不太了解,此处就不作分析了】 底层收到按键会回调InputManagerService的dispatchUnhandledKey()--->InputMonitor的函数dispatchUnhandledKey()。
Android12系统源码分析:NativeTombstoneManager
Android12系统源码分析:NativeTombstoneManagerAndroid12系统源码分析:NativeTombstoneManager概述android12新增的system_server进程(LocalService)本地服务,⽤于管理native tombstones。
该服务在开机Systemerver初始化流程启动,添加到LocalService,然后启动⼀个ServiceThread线程⽤于(mHandler.post)处理本服务的业务。
NativeTombstoneManager的功能主要是:监听/data/tombstones ⽬录⽂件变动,解析为TombstoneFile 对象保存,通知dropbox特定tombstones ⽂件删除特定tombstones ⽂件检索值得关注的是AMS对该服务的使⽤,也是Android11新增API:getHistoricalProcessExitReasons()软件架构如图:BootReceiver SystemServer 开机启动仅添加本地服务LocalServices.addService NativeTombstoneManager.class SystemService AppExitInfoTracker Nativ eTombs toneManager tombs toneServ ic e void clearHistoryProcessExitInfo()ActivityManagerService Nativ eTombs toneManager tombs toneServ ic e public getHistoricalProcessExitReasons()暴露接⼝给appNativeTombstoneManagerServiceNativ eTombs toneManager mManagerpublic v oid onStart()public v oid onBootPhas e()TombstoneFile by:qiuchengParcelFileDescriptorRetrieverNativeTombstoneManagerpriv ate v oid handleTombs tone()priv ate v oid handleProtoTombs tone()public v oid c ollec tTombs tones ()服务核⼼实现TombstoneWatcher 监听⽬录:/data/tombstones FileObserver 图:NativeTombstoneManager类图启动流程SystemServer SystemServer SystemServiceManager SystemServiceManager NativeTombstoneManagerService NativeTombstoneManagerService NativeTombstoneManager NativeTombstoneManager LocalServicesLocalServices 1startCoreServices23by:qiucheng4NativeTombstoneManager567addService(NativeTombstoneManager.class8startOtherServices9startBootPhasePHASE_ACTIVITY_MANAGER_READY10onBootPhase111213registerForPackageRemoval14handleTombstone图:NativeTombstoneManager服务启动时序图服务⽐较简单,和其他SystemServer启动的服务⼀样,frameworks/base/services/core/java/com/android/server/os/NativeTombstoneManagerService.java public class NativeTombstoneManagerService extends SystemService {private NativeTombstoneManager mManager;@Overridepublic void onStart() {mManager = new NativeTombstoneManager(getContext());//仅添加本地服务,没有binder服务LocalServices.addService(NativeTombstoneManager.class, mManager);}@Overridepublic void onBootPhase(int phase) {if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {mManager.onSystemReady();}}}本服务也是SystemService⼯具类的⼦类,通过重写onStart、onBootPhase获得代码流程在onStart中初始化真正的服务实现NativeTombstoneManager,实例化后添加到LocalServicesframeworks/base/services/core/java/com/android/server/os/NativeTombstoneManager.javapublic final class NativeTombstoneManager {NativeTombstoneManager(Context context) {//启动handler线程,⽤于后续处理本服务的业务final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",THREAD_PRIORITY_BACKGROUND, true /* allowIo */);thread.start();mHandler = thread.getThreadHandler();//启动⽂件监听/data/tombstonesmWatcher = new TombstoneWatcher();mWatcher.startWatching();}void onSystemReady() {registerForUserRemoval();registerForPackageRemoval();// 开机阶段先扫描⼀次/data/tombstones⽬录mHandler.post(() -> {final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {if (tombstoneFiles[i].isFile()) {handleTombstone(tombstoneFiles[i]);开机流程有三个动作启动handler线程,⽤于后续处理本服务的业务TombstoneWatcher启动⽂件监听/data/tombstones开机阶段先扫描⼀次/data/tombstones⽬录看⼀下handleTombstoneframeworks/base/services/core/java/com/android/server/os/NativeTombstoneManager.javaprivate void handleTombstone(File path) {final String filename = path.getName();if (!filename.startsWith("tombstone_")) {return;}if (filename.endsWith(".pb")) {handleProtoTombstone(path);BootReceiver.addTombstoneToDropBox(mContext, path, true);} else {BootReceiver.addTombstoneToDropBox(mContext, path, false);如果是以pb结尾的原型⽂件,则handleProtoTombstone⽅法中为该⽂件⽣成TombstoneFile对象,并添加到数据结构private final SparseArray<TombstoneFile> mTombstones;并且,每个新⽣成的tombstone⽂件都会同步给dropbox新⽂件的监听frameworks/base/services/core/java/com/android/server/os/NativeTombstoneManager.javaclass TombstoneWatcher extends FileObserver {TombstoneWatcher() {super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);}@Overridepublic void onEvent(int event, @Nullable String path) {mHandler.post(() -> {handleTombstone(new File(TOMBSTONE_DIR, path));});内部类TombstoneWatcher,当⽬录/data/tombstones有⽂件⽣成时,回调到onEvent,然后通过handleTombstone⽅法做处理AciivtyManager#getHistoricalProcessExitReasonsActivityManager ActivityManager ActivityManagerServiceActivityManagerServiceLocalServicesLocalServicesNativeTombstoneManagerNativeTombstoneManagergetHistoricalProcessExitReasonsenforceNotIsolatedCallerenforceDumpPermissionForPackageby:qiuchenggetServicecollectTombstonesnew ParceledListSlice<ApplicationExitInfo>(results)图:getHistoricalProcessExitReasons⽅法时序图需要注意返回的数据结构的处理ApplicationExitInfo。
performtraversals 源码解析
一、performTraversals 源码解析在Android开发中,performTraversals是一个非常重要的方法,它负责整个View树的遍历和绘制。
在这篇文章中,我们将对performTraversals方法的源码进行深入解析,探讨其实现原理和内部逻辑。
二、performTraversals方法的调用时机performTraversals方法是在View树需要进行遍历和绘制时被调用的。
这个时机通常包括Activity的创建、窗口尺寸改变、View树结构发生改变等情况。
在这些时机下,performTraversals方法的执行是为了保证View的正确展示和用户交互。
三、performTraversals的执行流程1. 初始化performTraversals方法首先对View树进行初始化,包括对整个View树的尺寸、布局参数等进行计算和赋值。
2. 测量performTraversals方法对每个View进行测量,计算出每个View的尺寸和位置。
这一步是确保View在屏幕中的正确显示的重要步骤。
3. 布局在测量完成后,performTraversals方法对每个View进行布局,确定它们的最终位置并更新布局参数。
4. 绘制performTraversals方法对每个View进行绘制,包括背景、内容等的绘制过程。
以上就是performTraversals方法的基本执行流程,它负责了整个View树的遍历和绘制过程,保证了View在屏幕中的正确展示。
四、performTraversals方法的内部逻辑performTraversals方法的内部逻辑非常复杂,涉及到了View的测量、布局和绘制等方面。
这里我们简单介绍一下其中一些重要的部分:1. measure 源码解析在performTraversals方法中,对每个View进行测量是一个非常重要的步骤。
在measure的过程中,View的测量模式、尺寸等都会被计算和设置,从而确定了View的最终大小和位置。
36个Android开发常用经典代码大全
36个Android开发常⽤经典代码⼤全本⽂汇集36个Android开发常⽤经典代码⽚段,包括拨打电话、发送短信、唤醒屏幕并解锁、是否有⽹络连接、动态显⽰或者是隐藏软键盘等,希望对您有所帮助。
//36个Android开发常⽤代码⽚段//拨打电话public static void call(Context context, String phoneNumber) {context.startActivity( new Intent(Intent.ACTION_CALL, Uri.parse( "tel:" + phoneNumber)));}//跳转⾄拨号界⾯public static void callDial(Context context, String phoneNumber) {context.startActivity( new Intent(Intent.ACTION_DIAL, Uri.parse( "tel:" + phoneNumber)));}//发送短信public static void sendSms(Context context, String phoneNumber,String content) {Uri uri = Uri.parse( "smsto:"+ (TextUtils.isEmpty(phoneNumber) ? "" : phoneNumber));Intent intent = new Intent(Intent.ACTION_SENDTO, uri);intent.putExtra( "sms_body" , TextUtils.isEmpty(content) ? "" : content);context.startActivity(intent);}//唤醒屏幕并解锁public static void wakeUpAndUnlock(Context context){KeyguardManager km= (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);KeyguardManager.KeyguardLock kl = km.newKeyguardLock( "unLock" );//解锁kl.disableKeyguard();//获取电源管理器对象PowerManager pm=(PowerManager) context.getSystemService(Context.POWER_SERVICE);//获取PowerManager.WakeLock对象,后⾯的参数|表⽰同时传⼊两个值,最后的是LogCat⾥⽤的TagPowerManager.WakeLock wl = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK, "bright" ); //点亮屏幕wl.acquire();//释放wl.release();}//需要添加权限<uses-permission android:name= "android.permission.WAKE_LOCK" /><uses-permission android:name= "android.permission.DISABLE_KEYGUARD" />//判断当前App处于前台还是后台状态public static boolean isApplicationBackground( final Context context) {ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);@SuppressWarnings ( "deprecation" )List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks( 1 ); if (!tasks.isEmpty()) {ComponentName topActivity = tasks.get( 0 ).topActivity;if (!topActivity.getPackageName().equals(context.getPackageName())) { return true ;}}return false ;}//需要添加权限<uses-permissionandroid:name= "android.permission.GET_TASKS" />//判断当前⼿机是否处于锁屏(睡眠)状态public static boolean isSleeping(Context context) {KeyguardManager kgMgr = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);boolean isSleeping = kgMgr.inKeyguardRestrictedInputMode();return isSleeping;}//判断当前是否有⽹络连接public static boolean isOnline(Context context) {ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Activity.CONNECTIVITY_SERVICE);NetworkInfo info = manager.getActiveNetworkInfo();if (info != null && info.isConnected()) {return true ;}return false ;}//判断当前是否是WIFI连接状态public static boolean isWifiConnected(Context context) {ConnectivityManager connectivityManager = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo wifiNetworkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);if (wifiNetworkInfo.isConnected()) {return true ;}return false ;}//安装APKpublic static void installApk(Context context, File file) {Intent intent = new Intent();intent.setAction( "android.intent.action.VIEW" );intent.addCategory( "android.intent.category.DEFAULT" );intent.setType( "application/vnd.android.package-archive" );intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive" );intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);}//判断当前设备是否为⼿机public static boolean isPhone(Context context) {TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);if (telephony.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) { return false ;} else {return true ;}}//获取当前设备宽⾼,单位px@SuppressWarnings ( "deprecation" )public static int getDeviceWidth(Context context) {WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);return manager.getDefaultDisplay().getWidth();}@SuppressWarnings ( "deprecation" )public static int getDeviceHeight(Context context) {WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);return manager.getDefaultDisplay().getHeight();}//获取当前设备的IMEI,需要与上⾯的isPhone()⼀起使⽤@TargetApi (Build.VERSION_CODES.CUPCAKE)public static String getDeviceIMEI(Context context) {String deviceId;if (isPhone(context)) {TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);deviceId = telephony.getDeviceId();} else {deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);}return deviceId;}//获取当前设备的MAC地址public static String getMacAddress(Context context) {String macAddress;WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);WifiInfo info = wifi.getConnectionInfo();macAddress = info.getMacAddress();if ( null == macAddress) {return "" ;}macAddress = macAddress.replace( ":" , "" );return macAddress;}//获取当前程序的版本号public static String getAppVersion(Context context) {String version = "0" ;try {version = context.getPackageManager().getPackageInfo(context.getPackageName(), 0 ).versionName;} catch (NotFoundException e) {e.printStackTrace();}return version;}//收集设备信息,⽤于信息统计分析public static Properties collectDeviceInfo(Context context) {Properties mDeviceCrashInfo = new Properties();try {PackageManager pm = context.getPackageManager();PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);if (pi != null ) {mDeviceCrashInfo.put(VERSION_NAME,pi.versionName == null ? "not set" : pi.versionName);mDeviceCrashInfo.put(VERSION_CODE, pi.versionCode);}} catch (NotFoundException e) {Log.e(TAG, "Error while collect package info" , e);}Field[] fields = Build. class .getDeclaredFields();for (Field field : fields) {try {field.setAccessible( true );mDeviceCrashInfo.put(field.getName(), field.get( null ));} catch (Exception e) {Log.e(TAG, "Error while collect crash info" , e);}}return mDeviceCrashInfo;}public static String collectDeviceInfoStr(Context context) {Properties prop = collectDeviceInfo(context);Set deviceInfos = prop.keySet();StringBuilder deviceInfoStr = new StringBuilder( "{\n" );for (Iterator iter = deviceInfos.iterator(); iter.hasNext();) {Object item = iter.next();deviceInfoStr.append( "\t\t\t" + item + ":" + prop.get(item)+ ", \n" );}deviceInfoStr.append( "}" );return deviceInfoStr.toString();}//是否有SD卡public static boolean haveSDCard() {return android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);}//动态隐藏软键盘@TargetApi (Build.VERSION_CODES.CUPCAKE)public static void hideSoftInput(Activity activity) {View view = activity.getWindow().peekDecorView();if (view != null ) {InputMethodManager inputmanger = (InputMethodManager) activity .getSystemService(Context.INPUT_METHOD_SERVICE);inputmanger.hideSoftInputFromWindow(view.getWindowToken(), 0 );}}@TargetApi (Build.VERSION_CODES.CUPCAKE)public static void hideSoftInput(Context context, EditText edit) {edit.clearFocus();InputMethodManager inputmanger = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);inputmanger.hideSoftInputFromWindow(edit.getWindowToken(), 0 );}//动态显⽰软键盘@TargetApi (Build.VERSION_CODES.CUPCAKE)public static void showSoftInput(Context context, EditText edit) {edit.setFocusable( true );edit.setFocusableInTouchMode( true );edit.requestFocus();InputMethodManager inputManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);inputManager.showSoftInput(edit, 0 );}//动态显⽰或者是隐藏软键盘@TargetApi (Build.VERSION_CODES.CUPCAKE)public static void toggleSoftInput(Context context, EditText edit) {edit.setFocusable( true );edit.setFocusableInTouchMode( true );edit.requestFocus();InputMethodManager inputManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0 ); }//主动回到Home,后台运⾏public static void goHome(Context context) {Intent mHomeIntent = new Intent(Intent.ACTION_MAIN);mHomeIntent.addCategory(Intent.CATEGORY_HOME);mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);context.startActivity(mHomeIntent);}//获取状态栏⾼度//注意,要在onWindowFocusChanged中调⽤,在onCreate中获取⾼度为0@TargetApi (Build.VERSION_CODES.CUPCAKE)public static int getStatusBarHeight(Activity activity) {Rect frame = new Rect();activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);return frame.top;}//获取状态栏⾼度+标题栏(ActionBar)⾼度//(注意,如果没有ActionBar,那么获取的⾼度将和上⾯的是⼀样的,只有状态栏的⾼度)public static int getTopBarHeight(Activity activity) {return activity.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();}//获取MCC+MNC代码 (SIM卡运营商国家代码和运营商⽹络代码)//仅当⽤户已在⽹络注册时有效, CDMA 可能会⽆效(中国移动:46000 //46002, 中国联通:46001,中国电信:46003)public static String getNetworkOperator(Context context) {TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);return telephonyManager.getNetworkOperator();}//返回移动⽹络运营商的名字//(例:中国联通、中国移动、中国电信) 仅当⽤户已在⽹络注册时有效, //CDMA 可能会⽆效)public static String getNetworkOperatorName(Context context) {TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);return telephonyManager.getNetworkOperatorName();}//返回移动终端类型PHONE_TYPE_NONE :0 ⼿机制式未知PHONE_TYPE_GSM :1 ⼿机制式为GSM,移动和联通PHONE_TYPE_CDMA :2 ⼿机制式为CDMA,电信PHONE_TYPE_SIP:3public static int getPhoneType(Context context) {TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);return telephonyManager.getPhoneType();}//判断⼿机连接的⽹络类型(2G,3G,4G)//联通的3G为UMTS或HSDPA,移动和联通的2G为GPRS或EGDE,电信的2G为CDMA,电信的3G为EVDOpublic class Constants {/*** Unknown network class*/public static final int NETWORK_CLASS_UNKNOWN = 0 ;/*** wifi net work*/public static final int NETWORK_WIFI = 1 ;/*** "2G" networks*/public static final int NETWORK_CLASS_2_G = 2 ;/*** "3G" networks*/public static final int NETWORK_CLASS_3_G = 3 ;/*** "4G" networks*/public static final int NETWORK_CLASS_4_G = 4 ;}public static int getNetWorkClass(Context context) {TelephonyManager telephonyManager = (TelephonyManager) context .getSystemService(Context.TELEPHONY_SERVICE);switch (telephonyManager.getNetworkType()) {case WORK_TYPE_GPRS:case WORK_TYPE_EDGE:case WORK_TYPE_CDMA:case WORK_TYPE_1xRTT:case WORK_TYPE_IDEN:return WORK_CLASS_2_G;case WORK_TYPE_UMTS:case WORK_TYPE_EVDO_0:case WORK_TYPE_EVDO_A:case WORK_TYPE_HSDPA:case WORK_TYPE_HSUPA:case WORK_TYPE_HSPA:case WORK_TYPE_EVDO_B:case WORK_TYPE_EHRPD:case WORK_TYPE_HSPAP:return WORK_CLASS_3_G;case WORK_TYPE_LTE:return WORK_CLASS_4_G;default :return WORK_CLASS_UNKNOWN;}}//判断当前⼿机的⽹络类型(WIFI还是2,3,4G)//需要⽤到上⾯的⽅法public static int getNetWorkStatus(Context context) {int netWorkType = WORK_CLASS_UNKNOWN;ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();if (networkInfo != null && networkInfo.isConnected()) {int type = networkInfo.getType();if (type == ConnectivityManager.TYPE_WIFI) {netWorkType = WORK_WIFI;} else if (type == ConnectivityManager.TYPE_MOBILE) {netWorkType = getNetWorkClass(context);}}return netWorkType;}//px-dp转换public static int dip2px(Context context, float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return ( int ) (dpValue * scale + 0 .5f);}public static int px2dip(Context context, float pxValue) {final float scale = context.getResources().getDisplayMetrics().density;return ( int ) (pxValue / scale + 0 .5f);}//px-sp转换public static int px2sp(Context context, float pxValue) {final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return ( int ) (pxValue / fontScale + 0 .5f);}public static int sp2px(Context context, float spValue) {final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return ( int ) (spValue * fontScale + 0 .5f);}//把⼀个毫秒数转化成时间字符串//格式为⼩时/分/秒/毫秒(如:24903600 –> 06⼩时55分03秒600毫秒)/*** @param millis* 要转化的毫秒数。
android输入法01:SoftKeyboard源码解析01
android输入法01:SoftKeyboard源码解析01本文主要介绍android自带输入法实例SoftKeyboard的源码,共分为两篇:第一篇为SoftKeyboard框架概述,第二篇为源码注释。
1、IMF简介一个IMF结构中包含三个主要的部分:•i nput method manager:管理各部分的交互。
它是一个客户端API,存在于各个应用程序的context中,用来沟通管理所有进程间交互的全局系统服务。
•i nput method(IME):实现一个允许用户生成文本的独立交互模块。
系统绑定一个当前的输入法。
使其创建和生成,决定输入法何时隐藏或者显示它的UI。
同一时间只能有一个IME运行。
•c lient application:通过输入法管理器控制输入焦点和IME的状态。
一次只能有一个客户端使用IME。
1.1 InputManager由UI控件(View,TextView,EditText等)调用,用来操作输入法。
比如,打开,关闭,切换输入法等。
它是整个输入法框架(IMF)结构的核心API,处理应用程序和当前输入法的交互。
可以通过Context.getSystemService()来获取一个InputMethodManager的实例。
在开发过程中,最基础最重要的就是养成阅读API的习惯。
优秀的程序员要养成把自己关在小黑屋里,断绝与外界的联网和联系,仅仅靠自己电脑中的开发环境和API文档,以及漂亮女仆送来的每天三顿饭,写出优秀的程序。
这个在武侠小说中叫闭关,在软件开发中叫Clean Room,哈哈。
Android的API文档在:%SDK_ROOM%/docs/reference/index.html,InputManager类的位置:%SDK_ROOM%/docs/reference/android/view/inputmethod/InputMethodManager.html 由于,该类跟本次要讲的Sample关系不大,这里就不详细分析,请各位自行阅读API doc吧。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Android 2.1 源码结构分析leeAndroid 2.1|-- Makefile|-- bionic (bionic C库)|-- bootable (启动引导相关代码)|-- build (存放系统编译规则及generic等基础开发包配置)|-- cts (Android兼容性测试套件标准)|-- dalvik (dalvik JAVA虚拟机)|-- development (应用程序开发相关)|-- external (android使用的一些开源的模组)|-- frameworks (核心框架——java及C++语言)|-- hardware (主要保护硬解适配层HAL代码)|-- out (编译完成后的代码输出与此目录)|-- packages (应用程序包)|-- prebuilt (x86和arm架构下预编译的一些资源)|-- sdk (sdk及模拟器)|-- system (文件系统库、应用及组件——C语言)`-- vendor (厂商定制代码)bionic 目录|-- libc (C库)| |-- arch-arm (ARM架构,包含系统调用汇编实现)| |-- arch-x86 (x86架构,包含系统调用汇编实现)| |-- bionic (由C实现的功能,架构无关)| |-- docs (文档)| |-- include (头文件)| |-- inet (?inet相关,具体作用不明)| |-- kernel (Linux内核中的一些头文件)| |-- netbsd (?nesbsd系统相关,具体作用不明)| |-- private (?一些私有的头文件)| |-- stdio (stdio实现)| |-- stdlib (stdlib实现)| |-- string (string函数实现)| |-- tools (几个工具)| |-- tzcode (时区相关代码)| |-- unistd (unistd实现)| `-- zoneinfo (时区信息)|-- libdl (libdl实现,dl是动态链接,提供访问动态链接库的功能)|-- libm (libm数学库的实现,)| |-- alpha (apaha架构)| |-- amd64 (amd64架构)| |-- arm (arm架构)| |-- bsdsrc (?bsd的源码)| |-- i386 (i386架构)| |-- i387 (i387架构?)| |-- ia64 (ia64架构)| |-- include (头文件)| |-- man (数学函数,后缀名为.3,一些为freeBSD的库文件)| |-- powerpc (powerpc架构)| |-- sparc64 (sparc64架构)| `-- src (源代码)|-- libstdc++ (libstdc++ C++实现库)| |-- include (头文件)| `-- src (源码)|-- libthread_db (多线程程序的调试器库)| `-- include (头文件)`-- linker (动态链接器)`-- arch (支持arm和x86两种架构)bootable 目录.|-- bootloader (适合各种bootloader的通用代码)| `-- legacy (估计不能直接使用,可以参考)| |-- arch_armv6 (V6架构,几个简单的汇编文件)| |-- arch_msm7k (高通7k处理器架构的几个基本驱动)| |-- include (通用头文件和高通7k架构头文件)| |-- libboot (启动库,都写得很简单)| |-- libc (一些常用的c函数)| |-- nandwrite (nandwirte函数实现)| `-- usbloader (usbloader实现)|-- diskinstaller (android镜像打包器,x86可生产iso)`-- recovery (系统恢复相关)|-- edify (升级脚本使用的edify脚本语言) |-- etc (init.rc恢复脚本)|-- minui (一个简单的UI)|-- minzip (一个简单的压缩工具)|-- mtdutils (mtd工具)|-- res (资源)| `-- images (一些图片)|-- tools (工具)| `-- ota (OTA Over The Air Updates升级工具) `-- updater (升级器)build目录.|-- core (核心编译规则)|-- history (历史记录)|-- libs| `-- host (主机端库,有android “cp”功能替换)|-- target (目标机编译对象)| |-- board (开发平台)| | |-- emulator (模拟器)| | |-- generic (通用)| | |-- idea6410 (自己添加的)| | `-- sim (最简单)| `-- product (开发平台对应的编译规则)| `-- security (密钥相关)`-- tools (编译中主机使用的工具及脚本)|-- acp (Android "acp" Command)|-- apicheck (api检查工具)|-- applypatch (补丁工具)|-- apriori (预链接工具)|-- atree (tree工具)|-- bin2asm (bin转换为asm工具)|-- check_prereq (检查编译时间戳工具)|-- dexpreopt (模拟器相关工具,具体功能不明)|-- droiddoc (?作用不明,java语言,网上有人说和JDK5有关)|-- fs_config (This program takes a list of files and directories)|-- fs_get_stats (获取文件系统状态)|-- iself (判断是否ELF格式)|-- isprelinked (判断是否prelinked)|-- kcm (按键相关)|-- lsd (List symbol dependencies)|-- releasetools (生成镜像的工具及脚本)|-- rgb2565 (rgb转换为565)|-- signapk (apk签名工具)|-- soslim (strip工具)`-- zipalign (zip archive alignment tool)dalvik目录 dalvik虚拟机.|-- dalvikvm (main.c的目录)|-- dexdump (dex反汇编)|-- dexlist (List all methods in all concrete classes in a DEX file.)|-- dexopt (预验证与优化)|-- docs (文档)|-- dvz (和zygote相关的一个命令)|-- dx (dx工具,将多个java转换为dex)|-- hit (?java语言写成)|-- libcore (核心库)|-- libcore-disabled (?禁用的库)|-- libdex (dex的库)|-- libnativehelper (Support functions for Android's class libraries)|-- tests (测试代码)|-- tools (工具)`-- vm (虚拟机实现)development 目录(开发者需要的一些例程及工具)|-- apps (一些核心应用程序)| |-- BluetoothDebug (蓝牙调试程序)| |-- CustomLocale (自定义区域设置)| |-- Development (开发)| |-- Fallback (和语言相关的一个程序)| |-- FontLab (字库)| |-- GestureBuilder (手势动作)| |-- NinePatchLab (?)| |-- OBJViewer (OBJ查看器)| |-- SdkSetup (SDK安装器)| |-- SpareParts (高级设置)| |-- Term (远程登录)| `-- launchperf (?)|-- build (编译脚本模板)|-- cmds (有个monkey工具)|-- data (配置数据)|-- docs (文档)|-- host (主机端USB驱动等)|-- ide (集成开发环境)|-- ndk (本地开发套件——c语言开发套件)|-- pdk (Plug Development Kit)|-- samples (例程)| |-- AliasActivity (?)| |-- ApiDemos (API演示程序)| |-- BluetoothChat (蓝牙聊天)| |-- BrowserPlugin (浏览器插件)| |-- BusinessCard (商业卡)| |-- Compass (指南针)| |-- ContactManager (联系人管理器)| |-- CubeLiveWallpaper (动态壁纸的一个简单例程)| |-- FixedGridLayout (像是布局)| |-- GlobalTime (全球时间)| |-- HelloActivity (Hello)| |-- Home (Home)| |-- JetBoy (jetBoy游戏)| |-- LunarLander (貌似又是一个游戏)| |-- MailSync (邮件同步)| |-- MultiResolution (多分辨率)| |-- MySampleRss (RSS)| |-- NotePad (记事本)| |-- RSSReader (RSS阅读器)| |-- SearchableDictionary (目录搜索)| |-- SimpleJNI (JNI例程)| |-- SkeletonApp (空壳APP)| |-- Snake (snake程序)| |-- SoftKeyboard (软键盘)| |-- Wiktionary (?维基)| `-- WiktionarySimple(?维基例程)|-- scripts (脚本)|-- sdk (sdk配置)|-- simulator (?模拟器)|-- testrunner (?测试用)`-- tools (一些工具)external 目录.|-- aes (AES加密)|-- apache-http (网页服务器)|-- astl (ASTL (Android STL) is a slimmed-down version of the regular C++ STL.)|-- bison (自动生成语法分析器,将无关文法转换成C、C++)|-- blktrace (blktrace is a block layer IO tracing mechanism)|-- bluetooth (蓝牙相关、协议栈)|-- bsdiff (diff工具)|-- bzip2 (压缩工具)|-- clearsilver (html模板系统)|-- dbus (低延时、低开销、高可用性的IPC机制)|-- dhcpcd (DHCP服务)|-- dosfstools (DOS文件系统工具)|-- dropbear (SSH2的server)|-- e2fsprogs (EXT2文件系统工具)|-- elfcopy (复制ELF的工具)|-- elfutils (ELF工具)|-- embunit (Embedded Unit Project)|-- emma (java代码覆盖率统计工具)|-- esd (Enlightened Sound Daemon,将多种音频流混合在一个设备上播放)|-- expat (Expat is a stream-oriented XML parser.)|-- fdlibm (FDLIBM (Freely Distributable LIBM))|-- freetype (字体)|-- fsck_msdos (dos文件系统检查工具)|-- gdata (google的无线数据相关)|-- genext2fs (genext2fs generates an ext2 filesystem as a normal (non-root) user)|-- giflib (gif库)|-- googleclient (google用户库)|-- grub (This is GNU GRUB, the GRand Unified Bootloader.)|-- gtest (Google C++ Testing Framework)|-- icu4c (ICU(International Component for Unicode)在C/C++下的版本)|-- ipsec-tools (This package provides a way to use the native IPsec functionality )|-- iptables (防火墙)|-- jdiff (generate a report describing the difference between two public Java APIs.)|-- jhead (jpeg头部信息工具)|-- jpeg (jpeg库)|-- junit (JUnit是一个Java语言的单元测试框架)|-- kernel-headers (内核的一些头文件)|-- libffi (libffi is a foreign function interface library.)|-- libpcap (网络数据包捕获函数)|-- libpng (png库)|-- libxml2 (xml解析库)|-- mtpd (一个命令)|-- netcat (simple Unix utility which reads and writes dataacross network connections)|-- netperf (网络性能测量工具)|-- neven (看代码和JNI相关)|-- opencore (多媒体框架)|-- openssl (SSL加密相关)|-- openvpn (VPN开源库)|-- oprofile (OProfile是Linux内核支持的一种性能分析机制。