路由這個詞聽上去很專業,其實它的目的很簡單,就是為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 本章小結