郭瑞祥, 李玉, 范葉平, 操李節(jié), 楊德勝
(安徽繼遠(yuǎn)軟件有限公司, 合肥 230088)
隨著移動(dòng)智能手機(jī)的迅速發(fā)展,移動(dòng)智能手機(jī)的功能已不僅僅是簡(jiǎn)單的通訊功能,用戶經(jīng)常使用智能手機(jī)處理日常生活中遇到的各種問(wèn)題,同時(shí)由于移動(dòng)智能手機(jī)便于攜帶及功能強(qiáng)大的優(yōu)勢(shì),移動(dòng)智能手機(jī)開(kāi)始進(jìn)入辦公領(lǐng)域,許多用戶為了使用方便,利用同一部智能手機(jī)來(lái)處理生活和工作內(nèi)容,這將造成生活與工作的混淆,且容易造成企業(yè)敏感信息的泄漏,為了區(qū)分工作和生活,同時(shí)也對(duì)企業(yè)敏感數(shù)據(jù)進(jìn)行保護(hù)[1-2],基于虛擬化技術(shù)和容器技術(shù)的雙系統(tǒng)智能手機(jī)成了一種選擇方案,在一臺(tái)智能移動(dòng)手機(jī)上運(yùn)行兩套系統(tǒng),分別稱為外域系統(tǒng)和內(nèi)域系統(tǒng),同一時(shí)刻只有一個(gè)系統(tǒng)占用智能移動(dòng)設(shè)備顯示、輸入、音頻等資源,用戶可通過(guò)系統(tǒng)界面按鈕一鍵切換內(nèi)外域系統(tǒng)的前后臺(tái)狀態(tài),實(shí)現(xiàn)了工作和生活的分離[3-4]。
利用虛擬化技術(shù)和容器技術(shù)實(shí)現(xiàn)雙系統(tǒng)時(shí)需要對(duì)系統(tǒng)模塊進(jìn)行重新設(shè)計(jì)研發(fā),本文提出了一種對(duì)雙系統(tǒng)中音頻模塊的實(shí)現(xiàn)方法與邏輯重新設(shè)計(jì)方案[5-6],實(shí)現(xiàn)了觸發(fā)系統(tǒng)前后臺(tái)切換時(shí),Audio子系統(tǒng)內(nèi)的處理邏輯,及后臺(tái)系統(tǒng)如有應(yīng)用想播放聲音時(shí)的處理邏輯等。
基于容器技術(shù)實(shí)現(xiàn)的雙Android系統(tǒng)擁有容器服務(wù),本文所述方法基于容器支撐平臺(tái)之上的ContainerService和ContainerControlService實(shí)現(xiàn),軟件架構(gòu)如圖1所示。
針對(duì)雙安卓系統(tǒng)的Audio Framework框架,關(guān)鍵的控制模塊為Java層的ContainerService和Native層的ContainerControlService。Audio Framework執(zhí)行的控制通路與數(shù)據(jù)通路的切換都是受ContainerService或ContainerControlService控制。
Audio Framework和驅(qū)動(dòng)的設(shè)計(jì),主要是針對(duì)容器切換時(shí),系統(tǒng)所處的前后臺(tái)狀態(tài)來(lái)決定不同的業(yè)務(wù)邏輯,內(nèi)域系統(tǒng)和外域系統(tǒng)的代碼處理邏輯類似,都是需要關(guān)注兩部分:容器切換時(shí)的處理邏輯和新建音頻播放流的處理邏輯。

圖1 軟件架構(gòu)圖
在用戶切換內(nèi)外域系統(tǒng)時(shí),也就是容器切換時(shí),最容易發(fā)生內(nèi)外域系統(tǒng)對(duì)音頻設(shè)備的訪問(wèn)沖突,為符合用戶使用的便捷性,原則上只有前臺(tái)系統(tǒng)才能訪問(wèn)音頻設(shè)備,后臺(tái)的系統(tǒng)禁止訪問(wèn)音頻設(shè)備。
前臺(tái)系統(tǒng)請(qǐng)求切換到后臺(tái)時(shí),ContainerService會(huì)調(diào)用AudioManager的接口setParameters()來(lái)設(shè)置系統(tǒng)的所處的前后臺(tái)狀態(tài)。setParameters()中的參數(shù)是個(gè)鍵值對(duì)字符串,如果系統(tǒng)是從前臺(tái)切到后臺(tái),其鍵值對(duì)為”container_state=back”;如果系統(tǒng)是從后臺(tái)切到前臺(tái),其鍵值對(duì)為“container_state=front”。此鍵值對(duì)經(jīng)過(guò)層層傳遞,最終在AudioALSAStreamManager中解析,并以布爾值的方式保存。當(dāng)系統(tǒng)切換至后臺(tái)時(shí),會(huì)調(diào)用standbyAllStreams()方法。暫停所有播放的聲音,并把音頻設(shè)備關(guān)閉。如圖2所示。具體代碼修改為修改AudioALSAHardware.cpp文件中兩段代碼,具體見(jiàn)下文。

圖2 容器切換時(shí)序圖
AudioALSAHardware.cpp
status_t AudioALSAHardware::setParameters(const String8 & keyValuePairs)
{
String8 value_str;
AudioParameter param = AudioParameter(keyValuePairs);
if (param.get(String8("container_state ") , value_str) == NO_ERROR)
{
mStreamManager->setParameter(keyValuePairs, 0);
}
}
AudioALSAStreamManager.cpp
static String8 keyContainerState = String8(“container_state”);
status_t AudioALSAStreamManager::setParameters(const String8 & keyValuePairs , int IOport)
{
String8 value_str;
AudioParameter param = AudioParameter(keyValuePairs);
if (param.get(String8(keyContainerState) , value_str) ==NO_ERROR)
{
param.remove(String8(keyContainerState));
if(value_str == “back”)
{
standbyAllStreams(true);
container_state = false;
}
else
{
Container_state = true;
}
return 0;
}
}
雙安卓系統(tǒng)中,針對(duì)音頻的播放/錄制流程,只有是系統(tǒng)處在后臺(tái)時(shí)才需要進(jìn)行特殊處理;如果是前臺(tái)系統(tǒng),針對(duì)音頻的播放/錄制是與安卓原生系統(tǒng)是一致,不需要進(jìn)行特殊處理。
相對(duì)于原生的安卓系統(tǒng)中音頻驅(qū)動(dòng)適配層,最關(guān)鍵的就是在新建音頻流之前,雙安卓會(huì)判斷當(dāng)前應(yīng)用所屬系統(tǒng)的前后臺(tái)狀態(tài)。根據(jù)用戶體驗(yàn)需求,只有處在前臺(tái)系統(tǒng)的應(yīng)用才可以播放音頻,即只有處理前臺(tái)系統(tǒng)的應(yīng)用才能操作音頻設(shè)備。而標(biāo)記系統(tǒng)前后臺(tái)狀態(tài)的參數(shù)保存在音頻驅(qū)動(dòng)的適配層AudioALSAStreamManager中。如圖3所示。具體代碼修改為修改AudioALSAHardware.cpp文件中代碼,具體見(jiàn)下文。
1) 前臺(tái)應(yīng)用播放音頻
如果是前臺(tái)系統(tǒng)的應(yīng)用想播放音頻,雙安卓系統(tǒng)中的處理邏輯與原生安卓的處理邏輯一致,創(chuàng)建播放音頻所需的所有資源,包括對(duì)音頻設(shè)備的控制操作。
2) 后臺(tái)應(yīng)用播放音頻
如果是后臺(tái)系統(tǒng)的應(yīng)用想播放音頻,在雙安卓系統(tǒng)中會(huì)返回錯(cuò)誤碼,而不會(huì)創(chuàng)建播放音頻所需的資源,更不會(huì)對(duì)音頻設(shè)備進(jìn)行控制操作。
3) 音頻錄制
由于音頻的錄制跟播放的流程在AudioALSAHardware類里一樣,都需調(diào)用createAudioPatch()方法,所以其處理邏輯跟播放是一樣的,無(wú)需另外添加代碼。

圖3 AudioHal層新建播放/錄制音頻流的時(shí)序圖
AudioALSAHardware.cpp
int AudioALSAHardware::createAudioPatch(unsigned int num_sources , const struct audio_port_config *sources , unsigned int num_sinks , const struct audio_port_config *sinks , audio_patch_handle_t *handle)
{
if (handle == NULL || sources == NULL || sinks == NULL)
{
return BAD_VALUE;
}
if ( false == mStreamManager-> getContainerState())
{
return BAD_VALUE;
}
}
Audio Framework在雙安卓系統(tǒng)中的修改,主要是通過(guò)已有的setParameters()方法,傳遞了一個(gè)新增的用以表示系統(tǒng)前后臺(tái)狀態(tài)的鍵值對(duì),并最終在類AudioALSAStreamManager進(jìn)行解析,存儲(chǔ)及控制新建音頻流的業(yè)務(wù)邏輯。前后臺(tái)系統(tǒng)切換音頻控制的接口函數(shù)及成員變量如下所示:
AudioALSAStreamManager.cpp
Class AudioALSAStreamManager{
public:
//設(shè)置系統(tǒng)前后臺(tái)狀態(tài)的方法
void AudioALSAStreamManager :: setContainerState(bool state);
//獲取系統(tǒng)前后臺(tái)狀態(tài)的方法
bool AudioALSAStreamManager :: getContainerState();
private:
//系統(tǒng)前后臺(tái)狀態(tài)的標(biāo)識(shí):true: 前臺(tái);false:后臺(tái)
bool container_state;
}
移動(dòng)智能終端的發(fā)展方便了人們的生活,同時(shí)改變了企業(yè)辦公方式,基于虛擬化技術(shù)及容器技術(shù)的雙系統(tǒng)技術(shù)保障了用戶在同一臺(tái)設(shè)備上處理生活與工作時(shí)企業(yè)數(shù)據(jù)的安全。在實(shí)現(xiàn)雙系統(tǒng)時(shí)需要對(duì)系統(tǒng)模塊進(jìn)行重新設(shè)計(jì),以便解決系統(tǒng)切換時(shí)出現(xiàn)的資源訪問(wèn)沖突,本文提出了一種基于雙安卓系統(tǒng)的音頻控制方法,通過(guò)對(duì)系統(tǒng)AudioFramework及驅(qū)動(dòng)的重新設(shè)計(jì),實(shí)現(xiàn)了雙安卓系統(tǒng)內(nèi)外域系統(tǒng)切換時(shí)的音頻控制策略,保障了只有前臺(tái)系統(tǒng)才具備設(shè)備音頻設(shè)備的訪問(wèn)權(quán)限。