路由这个词听上去很专业,其实它的目的很简单,就是为DSP选择数据出口,例如是从耳机、听筒还是扬声器传出。下面分析这样一个场景:
- 假设我们在用扬声器听歌,这时把耳机插上,会发生什么呢?
1. 耳机插拔事件处理
耳机插上后,系统会发一个广播,Java层的AudioService会接收这个广播,其中的内部类AudioServiceBroadcastReceiver会处理该事件,处理函数是onReceive。
这段代码在AudioSystem.java中。一起来看:
(1)耳机插拔事件接收
看这段代码,如下所示:
**AudioSystem.java::AudioServiceBroadcastReceiver的onReceive()**
~~~
private class AudioServiceBroadcastReceiverextends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
......
//如果该事件是耳机插拔事件
elseif (action.equals(Intent.ACTION_HEADSET_PLUG)) {
//取得耳机的状态
int state = intent.getIntExtra("state", 0);
int microphone =intent.getIntExtra("microphone", 0);
if (microphone != 0) {
//察看已连接设备是不是已经有了耳机,耳机的设备号为0x4,
//这个和AudioSystem.h定义的设备号是一致的
boolean isConnected =mConnectedDevices.containsKey(
AudioSystem.DEVICE_OUT_WIRED_HEADSET);
//如果之前有耳机而现在没有,则认为是耳机拔出事件
if (state == 0 &&isConnected) {
//设置Audio系统的设备连接状态,耳机为Unavailable
AudioSystem.setDeviceConnectionState(
AudioSystem.DEVICE_OUT_WIRED_HEADSET,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
"");
//从已连接设备中去掉耳机设备
mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
} //如果state为1,并且之前没有耳机连接,则处理这个耳机插入事件
else if (state == 1 && !isConnected){
//设置Audio系统的设备连接状态,耳机为Available
AudioSystem.setDeviceConnectionState(
AudioSystem.DEVICE_OUT_WIRED_HEADSET,
AudioSystem.DEVICE_STATE_AVAILABLE,
"");
//已连接设备中增加耳机
mConnectedDevices.put(
new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET),
"");
}
}
......
~~~
从上面的代码中可看出,不论耳机插入还是拔出,都会调用AudioSystem的setDeviceConnectionState函数。
(2)setDeviceConnectionState:设置设备连接状态
这个函数被定义为Native函数。下面是它的定义:
**AudioSystem.java**
~~~
publicstatic native int setDeviceConnectionState(int device, int state,
String device_address);
//注意我们传入的参数,device为0X4表示耳机,state为1,device_address为””
~~~
该函数的Native实现,在android_media_AudioSystem.cpp中,对应函数是:
**android_media_AudioSystem.cpp**
~~~
static int android_media_AudioSystem_setDeviceConnectionState(
JNIEnv*env, jobject thiz, jint
device,jint state, jstring device_address)
{
constchar *c_address = env->GetStringUTFChars(device_address, NULL);
intstatus = check_AudioSystem_Command(
//调用Native AudioSystem的setDeviceConnectionState
AudioSystem::setDeviceConnectionState(
static_cast<AudioSystem::audio_devices>(device),
static_cast<AudioSystem::device_connection_state>(state),
c_address));
env->ReleaseStringUTFChars(device_address, c_address);
returnstatus;
}
~~~
从AudioSystem.java转入到AudioSystem.cpp,现在来看Native的对应函数:
**AudioSystem.cpp**
~~~
status_tAudioSystem::setDeviceConnectionState(audio_devices device,
device_connection_state state,
const char *device_address)
{
constsp<IAudioPolicyService>& aps =
AudioSystem::get_audio_policy_service();
if(aps == 0) return PERMISSION_DENIED;
//转到AP去,最终由AMB处理
returnaps->setDeviceConnectionState(device, state, device_address);
}
~~~
Audio代码不厌其烦地把函数调用从这一类转移到另外一类,请直接看AMB的实现:
**AudioPolicyManagerBase.cpp**
~~~
status_tAudioPolicyManagerBase::setDeviceConnectionState(
AudioSystem::audio_devicesdevice,
AudioSystem::device_connection_statestate,
const char *device_address)
{
//一次只能设置一个设备
if(AudioSystem::popCount(device) != 1) return BAD_VALUE;
......
//根据设备号判断是不是输出设备,耳机肯定属于输出设备
if(AudioSystem::isOutputDevice(device)) {
switch (state)
{
case AudioSystem::DEVICE_STATE_AVAILABLE:
//处理耳机插入事件,mAvailableOutputDevices保存已连接的设备
//这个耳机是刚连上的,所以不走下面if分支
if (mAvailableOutputDevices & device) {
//启用过了,就不再启用了。
return INVALID_OPERATION;
}
//现在已连接设备中多了一个耳机
mAvailableOutputDevices |= device;
....
}
//① getNewDevice之前已分析过了,这次再看
uint32_t newDevice =getNewDevice(mHardwareOutput, false);
//②更新各种策略使用的设备
updateDeviceForStrategy();
//③设置新的输出设备
setOutputDevice(mHardwareOutput,newDevice);
......
}
~~~
这里面有三个比较重要的函数,前面也已提过,现将其再进行一次较深入的分析,旨在加深读者对它的理解。
(3)getNewDevice
来看代码,如下所示:
**AudioPolicyManagerBase.cpp**
~~~
uint32_tAudioPolicyManagerBase::getNewDevice(audio_io_handle_t output,
bool fromCache)
{ //注意我们传入的参数,output为mHardwardOutput,fromCache为false
uint32_tdevice = 0;
//根据output找到对应的AudioOutputDescriptor,这个对象保存了一些信息
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
if(mPhoneState == AudioSystem::MODE_IN_CALL ||
outputDesc->isUsedByStrategy(STRATEGY_PHONE))
{
device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
}
elseif (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION))
{
device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
}
elseif (outputDesc->isUsedByStrategy(STRATEGY_MEDIA))
{
//应用场景是正在听歌,所以会走这个分支
device= getDeviceForStrategy(STRATEGY_MEDIA, fromCache);
}
elseif (outputDesc->isUsedByStrategy(STRATEGY_DTMF))
{
device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
}
return device;
}
~~~
策略是怎么和设备联系起来的呢?秘密就在getDeviceForStrategy中,来看:
**AudioPolicyManagerBase.cpp**
~~~
uint32_tAudioPolicyManagerBase::getDeviceForStrategy(
routing_strategystrategy, bool fromCache)
{
uint32_t device = 0;
if (fromCache){//如果为true,则直接取之前的旧值
return mDeviceForStrategy[strategy];
}
//如果fromCache为false,则需要重新计算策略所对应的设备
switch(strategy) {
caseSTRATEGY_DTMF://先处理DTMF策略的情况
if(mPhoneState != AudioSystem::MODE_IN_CALL) {
//如果不处于电话状态,则DTMF的策略和MEDIA策略对应同一个设备
device = getDeviceForStrategy(STRATEGY_MEDIA, false);
break;
}
//如果处于电话状态,则DTMF策略和PHONE策略用同一个设备
caseSTRATEGY_PHONE:
//是PHONE策略的时候,先要考虑是不是用户强制使用了某个设备,例如强制使用扬声器
switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
......
case AudioSystem::FORCE_SPEAKER:
...... //如果没有蓝牙,则选择扬声器
device = mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_SPEAKER;
break;
}
break;
caseSTRATEGY_SONIFICATION://SONIFICATION策略
if(mPhoneState == AudioSystem::MODE_IN_CALL) {
/*
如果处于来电状态,则和PHONE策略用同一个设备。例如通话过程中我们强制使用
扬声器,那么这个时候按拨号键,则按键声也会从扬声器出来
*/
device = getDeviceForStrategy(STRATEGY_PHONE, false);
break;
}
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
//如果不处于电话状态,则SONIFICATION和MEDIA策略用同一个设备
case STRATEGY_MEDIA: {
//AUX_DIGITAL值为0x400,耳机不满足该条件
uint32_t device2 = mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_AUX_DIGITAL;
if(device2 == 0) {
//也不满足WIRED_HEADPHONE条件
device2 = mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
}
if(device2 == 0) {
//满足这个条件,所以device2为0x4,WIRED_HEADSET
device2 = mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
if(device2 == 0) {
device2 = mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_SPEAKER;
}
device |= device2; //最终device为0x4,WIRED_HEADSET
}break;
default:
break;
}
returndevice;
}
~~~
getDeviceForStrategy是一个比较复杂的函数。它的复杂,在于选取设备时,需考虑很多情况。简单的分析仅能和读者一起领略一下它的风采,在实际工作中反复琢磨,或许才能掌握其中的奥妙。
好,getNewDevice将返回耳机的设备号0x4。下一个函数是updateDeviceForStrategy。这个函数和getNewDevice没有什么关系,因为它没用到getNewDevice的返回值。
(4)updateDeviceForStrategy
同样是来看相应的代码,如下所示:
**AudioPolicyManagerBase.cpp**
~~~
voidAudioPolicyManagerBase::updateDeviceForStrategy()
{
for(int i = 0; i < NUM_STRATEGIES; i++) {
//重新计算每种策略使用的设备,并保存到mDeviceForStrategy中,起到了cache的作用
mDeviceForStrategy[i] =
getDeviceForStrategy((routing_strategy)i,false);
}
}
~~~
updateDeviceForStrategy会重新计算每种策略对应的设备。
另外,如果updateDeviceForStrategy和getNewDevice互换位置,就会节省很多不必要的调用。如:
~~~
updateDevicdForStrategy();//先更新策略
//使用cache中的设备,节省一次重新计算
uint32_t newDevice =getNewDevice(mHardwareOutput, true);
~~~
OK,不必讨论这位码农的功过了,现在看最后一个函数setOutputDevice。它会对新选出来的设备做如何处理呢?
(5)setOutputDevice
继续看setOutputDevice的代码,如下所示:
**AudioPolicyManagerBase.cpp**
~~~
void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_toutput,
uint32_t device,bool force, int delayMs)
{
......
//把这个请求要发送到output对应的AF工作线程中
AudioParameterparam = AudioParameter();
//参数是key/vlaue键值对的格式
param.addInt(String8(AudioParameter::keyRouting),(int)device);
//mpClientInterface是AP对象,由它处理
mpClientInterface->setParameters(mHardwareOutput,
param.toString(),delayMs);
//设置音量,不做讨论,读者可自行分析
applyStreamVolumes(output, device, delayMs);
}
~~~
setParameters最终会调用APS的setParameters,代码如下所示:
**AudioPolicyService.cpp**
~~~
voidAudioPolicyService::setParameters(audio_io_handle_t ioHandle,
constString8& keyValuePairs, int delayMs)
{
//把这个请求加入到AudioCommandThread处理
mAudioCommandThread->parametersCommand((int)ioHandle,
keyValuePairs, delayMs);
}
~~~
AudioPolicyService创建时会同时创建两个线程,其中一个用于处理各种请求。现在看看它是怎么做的。
2. AudioCommandThread
AudioCommandThread有一个请求处理队列,AP负责往该队列中提交请求,而AudioCommandThread在它的线程函数threadLoop中处理这些命令。请直接看命令是如何处理的。
* * * * *
**说明**:这种通过一个队列来协调两个线程的方法,在多线程编程中非常常见,它也属于生产者/消费者模型。
* * * * *
(1)AudioCommandThread中的处理
**AudioPolicyService.cpp**
~~~
boolAudioPolicyService::AudioCommandThread::threadLoop()
{
nsecs_twaitTime = INT64_MAX;
mLock.lock();
while(!exitPending())
{
while(!mAudioCommands.isEmpty()) {
nsecs_t curTime = systemTime();
if (mAudioCommands[0]->mTime <= curTime) {
AudioCommand *command = mAudioCommands[0];
mAudioCommands.removeAt(0);
mLastCommand = *command;
switch (command->mCommand) {
case START_TONE:
......
case STOP_TONE:
...... //TONE处理
mLock.lock();
}break;
case SET_VOLUME: {
//设置音量
delete data;
}break;
case SET_PARAMETERS: {
//处理路由设置请求
ParametersData *data =(ParametersData *)command->mParam;
//转到AudioSystem处理,mIO的值为mHardwareOutput
command->mStatus =AudioSystem::setParameters(
data->mIO,
data->mKeyValuePairs);
if(command->mWaitStatus) {
command->mCond.signal();
mWaitWorkCV.wait(mLock);
}
delete data;
}break;
......
default:
}
}
~~~
Audio系统真是非常绕!先看AudioSystem的setParameters。
(2)AudioSystem的setParameters
AudioSystem将设置请求转移给AudioFlinger处理,代码如下所示:
**AudioSystem.cpp**
~~~
status_tAudioSystem::setParameters(audio_io_handle_t ioHandle,
constString8& keyValuePairs)
{
constsp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
//果然是交给AF处理,ioHandle看来一定就是工作线程索引号了
returnaf->setParameters(ioHandle, keyValuePairs);
}
~~~
离真相越来越近了,接着看代码,如下所示:
**AudioFlinger.cpp**
~~~
status_t AudioFlinger::setParameters(intioHandle,
constString8& keyValuePairs)
{
status_t result;
// ioHandle == 0 表示和混音线程无关,需要直接设置到HAL对象中。
if(ioHandle == 0) {
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_SET_PARAMETER;
//调用AudioHardwareInterface的参数设置接口
result = mAudioHardware->setParameters(keyValuePairs);
mHardwareStatus = AUDIO_HW_IDLE;
return result;
}
sp<ThreadBase> thread;
{
Mutex::Autolock _l(mLock);
//根据索引号找到对应混音线程。
thread = checkPlaybackThread_l(ioHandle);
}
//我们只有一个MixerThread,交给它处理,这又是一个命令处理队列
result = thread->setParameters(keyValuePairs);
returnresult;
}
returnBAD_VALUE;
}
~~~
好了,最终的请求处理在MixerThread的线程函数中,来看:
(3)MixerThread最终处理
代码如下所示:
**AudioFlinger.cpp**
~~~
bool AudioFlinger::MixerThread::threadLoop()
{
....
while(!exitPending())
{
processConfigEvents();
mixerStatus = MIXER_IDLE;
{// scope for mLock
Mutex::Autolock _l(mLock);
// checkForNewParameters_l最有嫌疑
if (checkForNewParameters_l()) {
...
}
......//其他处理
}
~~~
**AudioFlinger.cpp**
~~~
boolAudioFlinger::MixerThread::checkForNewParameters_l()
{
boolreconfig = false;
while(!mNewParameters.isEmpty()) {
status_t status = NO_ERROR;
String8 keyValuePair = mNewParameters[0];
AudioParameter param = AudioParameter(keyValuePair);
int value;
......
//路由设置需要硬件参与,所以直接交给代表音频输出设备的HAL对象处理
status = mOutput->setParameters(keyValuePair);
return reconfig;
}
~~~
至此,路由设置所经历的一切轨迹,我们都已清晰地看到了,可总还有点意犹未尽的感觉,HAL的setParameters到底是怎么工作的呢?不妨再来看一个实际的HAL对象处理例子。
(4)真实设备的处理
这个实际的Hardware,位于hardware/msm7k/libaudio-qsd8k的Hardware.cpp中,它提供了一个实际的音频处理例子,这个Hardware针对的是高通公司的硬件。直接看它是怎么处理音频输出对象setParameters的,代码如下所示:
**AudioHardware.cppAudioStreamOutMSM72xx::setParameters()**
~~~
status_tAudioHardware::AudioStreamOutMSM72xx::setParameters(
const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 key = String8(AudioParameter::keyRouting);
status_tstatus = NO_ERROR;
intdevice;
if(param.getInt(key, device) == NO_ERROR) {
mDevices = device;
//调用doRouting,mHardware就是AudioHardware对象
status = mHardware->doRouting();
param.remove(key);
}
......
returnstatus;
}
~~~
**AudioHardware.cpp**
~~~
status_t AudioHardware::doRouting()
{
Mutex::Autolock lock(mLock);
uint32_t outputDevices = mOutput->devices();
status_t ret = NO_ERROR;
intsndDevice = -1;
......
//做一些判断,最终由doAudioRouteOrMute处理
if((vr_mode_change) || (sndDevice != -1 && sndDevice != mCurSndDevice)) {
ret = doAudioRouteOrMute(sndDevice);
mCurSndDevice = sndDevice;
}
returnret;
}
~~~
**AudioHardware.cpp**
~~~
status_t AudioHardware::doAudioRouteOrMute(uint32_tdevice)
{
uint32_t rx_acdb_id = 0;
uint32_t tx_acdb_id = 0;
//只看看就行,对应硬件相关的代码,咱们就是打打酱油
returndo_route_audio_dev_ctrl(device,
mMode== AudioSystem::MODE_IN_CALL, rx_acdb_id, tx_acdb_id);
}
~~~
**AudioHardware.cpp**
~~~
static status_t do_route_audio_dev_ctrl(uint32_tdevice, bool inCall,
uint32_t rx_acdb_id, uint32_t tx_acdb_id)
{
uint32_t out_device = 0, mic_device = 0;
uint32_t path[2];
int fd= 0;
//打开音频控制设备
fd =open("/dev/msm_audio_ctl", O_RDWR);
path[0]= out_device;
path[1]= rx_acdb_id;
//通过ioctl切换设备,一般系统调用都是返回-1表示出错,这里返回0表示出错
if(ioctl(fd, AUDIO_SWITCH_DEVICE, &path)) {
close(fd);
return -1;
}
......
}
~~~
- 前言
- 第1章 阅读前的准备工作
- 1.1 系统架构
- 1.1.1 Android系统架构
- 1.1.2 本书的架构
- 1.2 搭建开发环境
- 1.2.1 下载源码
- 1.2.2 编译源码
- 1.3 工具介绍
- 1.3.1 Source Insight介绍
- 1.3.2 Busybox的使用
- 1.4 本章小结
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 学习JNI的实例:MediaScanner
- 2.3 Java层的MediaScanner分析
- 2.3.1 加载JNI库
- 2.3.2 Java的native函数和总结
- 2.4 JNI层MediaScanner的分析
- 2.4.1 注册JNI函数
- 2.4.2 数据类型转换
- 2.4.3 JNIEnv介绍
- 2.4.4 通过JNIEnv操作jobject
- 2.4.5 jstring介绍
- 2.4.6 JNI类型签名介绍
- 2.4.7 垃圾回收
- 2.4.8 JNI中的异常处理
- 2.5 本章小结
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 属性服务
- 3.3 本章小结
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 关于zygote的总结
- 4.3 SystemServer分析
- 4.3.1 SystemServer的诞生
- 4.3.2 SystemServer的重要使命
- 4.3.3 关于 SystemServer的总结
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService发送请求
- 4.4.2 有求必应之响应请求
- 4.4.3 关于zygote分裂的总结
- 4.5 拓展思考
- 4.5.1 虚拟机heapsize的限制
- 4.5.2 开机速度优化
- 4.5.3 Watchdog分析
- 4.6 本章小结
- 第5章 深入理解常见类
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初识影子对象
- 5.2.2 第二板斧--由弱生强
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 轻量级的引用计数控制类LightRefBase
- 5.2.5 题外话-三板斧的来历
- 5.3 Thread类及常用同步类分析
- 5.3.1 一个变量引发的思考
- 5.3.2 常用同步类
- 5.4 Looper和Handler类分析
- 5.4.1 Looper类分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步关系
- 5.4.4 HandlerThread介绍
- 5.5 本章小结
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函数
- 6.2.2 独一无二的ProcessState
- 6.2.3 时空穿越魔术-defaultServiceManager
- 6.2.4 注册MediaPlayerService
- 6.2.5 秋风扫落叶-StartThread Pool和join Thread Pool分析
- 6.2.6 你彻底明白了吗
- 6.3 服务总管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服务的注册
- 6.3.3 ServiceManager存在的意义
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查询ServiceManager
- 6.4.2 子承父业
- 6.5 拓展思考
- 6.5.1 Binder和线程的关系
- 6.5.2 有人情味的讣告
- 6.5.3 匿名Service
- 6.6 学以致用
- 6.6.1 纯Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小结
- 第7章 深入理解Audio系统
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介绍
- 7.2.2 AudioTrack(Java空间)分析
- 7.2.3 AudioTrack(Native空间)分析
- 7.2.4 关于AudioTrack的总结
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的诞生
- 7.3.2 通过流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 关于AudioFlinger的总结
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的创建
- 7.4.2 重回AudioTrack
- 7.4.3 声音路由切换实例分析
- 7.4.4 关于AudioPolicy的总结
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 题外话
- 7.6 本章小结
- 第8章 深入理解Surface系统
- 8.1 概述
- 8.2 一个Activity的显示
- 8.2.1 Activity的创建
- 8.2.2 Activity的UI绘制
- 8.2.3 关于Activity的总结
- 8.3 初识Surface
- 8.3.1 和Surface有关的流程总结
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI层分析
- 8.3.4 Surface和画图
- 8.3.5 初识Surface小结
- 8.4 深入分析Surface
- 8.4.1 与Surface相关的基础知识介绍
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface对象的创建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介绍
- 8.4.7 深入分析Surface的总结
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的诞生
- 8.5.2 SF工作线程分析
- 8.5.3 Transaction分析
- 8.5.4 关于SurfaceFlinger的总结
- 8.6 拓展思考
- 8.6.1 Surface系统的CB对象分析
- 8.6.2 ViewRoot的你问我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小结
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理与机制分析
- 9.2.1 Netlink和Uevent介绍
- 9.2.2 初识Vold
- 9.2.3 NetlinkManager模块分析
- 9.2.4 VolumeManager模块分析
- 9.2.5 CommandListener模块分析
- 9.2.6 Vold实例分析
- 9.2.7 关于Vold的总结
- 9.3 Rild的原理与机制分析
- 9.3.1 初识Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 关于Rild main函数的总结
- 9.3.6 Rild实例分析
- 9.3.7 关于Rild的总结
- 9.4 拓展思考
- 9.4.1 嵌入式系统的存储知识介绍
- 9.4.2 Rild和Phone的改进探讨
- 9.5 本章小结
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模块分析
- 10.2.2 MSS模块分析
- 10.2.3 android.process.media媒体扫描工作的流程总结
- 10.3 MediaScanner分析
- 10.3.1 Java层分析
- 10.3.2 JNI层分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 关于MediaScanner的总结
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介绍
- 10.4.2 我问你答
- 10.5 本章小结