編輯:關於Android編程
高通新的camera驅動架構設計發生了一些變化,借用互聯網上常用的一種結構,大致的原理如此:將camera的所有功能劃分為不同的模塊,讓模塊自己來決定自己的事情(高內聚,低耦合),模塊需要有統一的接口和格式。模塊中有端口,通過端口把模塊連接起來,又把模塊掛在總線上。每一個端口的連接就是一個流,把這些流用pipeline來管理。每次啟動一個camera就創建一個會話,由這個會話來管理此camera的一切事物。對於每一個會話,模塊是共享的,它可以是camera的硬件資源也可以是其它資源(如一些軟件算法等資源)。
那麼如何來定義這個模塊的結構呢?
1.端口——端口屬於模塊,如果這個模塊只有source端口,那麼它就是一個src模塊;如果只有sink端口就是sink模塊,如果都有就是中間模塊。沒有端口的模塊是不能連接到流中的,但他可以完成一些其他的功能,比如接收引擎的設置,報告事件到bus等。連接到流中的端口,也就是說流事件(set/get)主要通過端口來處理。而來自於引擎的(set/get)通過模塊來處理,當然端口也可以把事件交給模塊來處理。模塊內部的端口可以通過模塊來建立關系,也可以建立內部的連接,端口有關get/set process。
2.模塊線程——每個模塊可以有一個線程來處理模塊的事情。一個線程對應一個隊列,線程就是從隊列中取出數據處理,然後應答回去。
3.總線回調——擋一個模塊向總線注冊時,總線向其提供一個回調函數,當模塊有事件發生時,調用這個函數向bus發消息,然後總線把這個消息提交給管道,管道把這個消息順著流發下去。
4.模塊的get、set以及process。
管道、引擎與會話
管道有兩端,一端用於讀,一端用於寫。camera引擎負責對管道的監控,而會話管理camera引擎。
從代碼結構上來看這種新的驅動架構
高通的camera deamon代碼放置在vendor\qcom\proprietary\mm-camera目錄下,而此目錄下的mm-camera2就是新的camera架構位置,進入裡面可以看到media-controller、server-imaging、server-tuning及其它幾個目錄,我們這裡需要關注的就是media-controller目錄。
media-controller
|- mct——應該就是camera的引擎?裡面包含了引擎、pipiline、bus、module、stream及event等定義及封裝。
|- modules——這裡面就是劃分好的一些模塊代碼,各模塊大致功能如下
|- sensor —— sensor 的驅動模塊? ——src模塊
|- iface —— ISP interface模塊 ——inter模塊
|- isp —— 主要是ISP的處理,其內部又包含了眾多的模塊 ——inter模塊
|- stats —— 一些統計算法模塊,如3A,ASD,AFD,IS,GRRO等數據統計的處理 ——sink模塊
|- pproc —— post process處理 ——inter模塊
|- imglib —— 主要是圖片的一些後端處理,如HDR等 ——sink模塊
以上各模塊內部又包含了眾多的模塊,具體需要看代碼分析。
高通camera daemon進程
1.概述
高通在Android的camera架構中,依舊沿用了其傳統的方式,將其自身的一些處理放在一個daemon進程中。這部分內容出於應用於driver之間,是為了保護自身及硬件廠商的利益而專門弄出來的一個東東。其它各家平台也采用類似的方式將這部分的處理放在HAL層處理。
2.進程的入口
做為一個單獨的進程,那肯定是有其main函數入口的。在vendor\qcom\proprietary\mm-camera\mm-camera2\server-imaging\server.c文件中可以看到這個main函數。在這個函數中主要做了以下幾件事情。
1.找到服務節點的名字並打開此節點
get_server_node_name(serv_hal_node_name)
......
hal_fd->fd[0] = open(dev_name, O_RDWR | O_NONBLOCK); //這裡dev_name為節點名如"/dev/serv_hal_node_name"
2.初始化模塊。目前有sensor、iface、isp、stats、pproc及imglib六個模塊(見筆記一)
server_process_module_init();
3.進入主循環來處理來自HAL及MCT的事件及消息,處理完之後的結果反饋給kernel(msm.c)
RD_FD_HAL
----> server_process_hal_event(&event)。此函數如果返回真,表示事件消息已經傳給了MCT,這時不需要發送CMD ACK給kernel,因為MCT處理結束後會發出通知。如果返回假,表示沒有傳到MCT,此時需要立即發送CMD ACK到kernel,以便HAL發送此消息的線程阻塞住。
RD_DS_FD_HAL —— 通過domain socket傳自HAL的消息
----> server_process_hal_ds_packet(fd_info->fd
RD_PIPE_FD_MCT —— 來自media controller的消息
media controller線程
1.概述
MCT線程是camera新架構的引擎部分,負責對管道的監控,由此來完成一個camera設備的控制運轉。它運行在daemon進程空間,由MSM_CAMERA_NEW_SESSION事件來開啟,具體開啟函數為mct_controller_new()。
2.mct_controller_new()函數
此函數創建一個新的MCT引擎,這將對應一個事務的pipeline。我們知道上層可以創建多個事務,每個對應一個camera,也對應自己的MCT及pipeline等。因此這個函數的主要完成以下幾件事情:
1.mct_pipeline_new()
---->創建一個Pipeline及其bus,並完成pipeline函數的映射。
2.mct_pipeline_start_session()
---->開啟camera的所有模塊並查詢其能力
3.pthread_create(..., mct_controller_thread_run, ...)
---->創建mct線程並開始執行
4.pthread_create(..., mct_bus_handler_thread_run, ...)
---->創建bus處理線程
3.mct_list_traverse()函數
此函數在整個mct線程中大量使用,主要用來遍歷所有模塊並執行一些處理工作。結合前面所講,camera各模塊都具有統一的接口,通過流來連接,模塊中又包含模塊,根據這種特性高通使用鏈表結構來保存這些模塊並設計了此函數用來完成遍歷操作。
1.先來看看此鏈表的節點結構。鏈表的節點其實也是一個鏈表,整個鏈表就好像是一串串同級的節點搭建而成,整個數據結構組成一顆樹結構。
struct _mct_list {
void *data; // 節點數據
mct_list_t *prev; // 上一個節點地址
mct_list_t **next;// 下一個節點節點元素數組首地址
uint32_t next_num; // 下一個節點節點元素數,大部分情況下為1
}mct_list_t;
2.通過遞歸的深度優先算法來遍歷整棵樹。
4.MCT線程運行
MCT整個引擎部分主要處理server及bus兩類事情,對應前面提到的MCT及bus兩個線程。MCT線程主要用來處理來自image server的消息,先pop MCT queue,查看是否有消息,如果有則執行mct_controller_proc_serv_msg_internal()函數來處理。
mct_controller_proc_serv_msg_internal函數用來處理來自image server的消息,並返回類型MCT_PROCESS_RET_SERVER_MSG。這裡處理的消息類型主要有SERV_MSG_DS與SERV_MSG_HAL兩種,分別在pipline中給出了相應的處理函數,具體查看源碼可知。
5.bus線程運行
bus線程跟MCT線程流程一樣。從代碼上我們看到兩個線程都是從同一個queue上面pop消息,他們是通過各自的線程條件變量來進行區分,完成線程的阻塞及運行工作。MCT的條件變量mctl_cond可以看到是在server_process.c文件中標記的,而bus的條件變量mctl_bus_handle_cond未在源碼中找到標志的位置?
sensor模塊
1.概述
sensor模塊是眾多模塊中的一個,主要是由模組的各個硬件模塊組成,包括sensor、Flash、Af、EEprom、OIS、CSI等。這個模塊主要描述了模組硬件的一些工作原理及部分驅動相關部分。
2.module_sensor_init()函數
在前面講到的server process中提到,服務進程開始後會初始化各個模塊,其中就包括sensor模塊,sensor初始化入口函數即為module_sensor_init(...)。這個函數將創建sensor模塊並返回其指針,另外將創建它的端口,填充一些功能函數等。它的主要執行流程如下:
1.創建sensor的MCT module。 —— mct_module_create(name)
創建完之後填充set mode、query mode、start session、stop session及set session data五個接口函數。
2.創建module_sensro_ctrl_t結構體,此結構體包含bundle信息,用來構建前面提到的模塊樹(方便添加、遍歷等操作)。
3.sensor模塊是source模塊,所以其numsinkports應該設置為0。
4.eebin相關的操作
5.sensor的探測操作,用來探測有效的sensor。
6.填入所有已探測到sensor的信息。
7.填入所以sensor的其它信息(Actuator,Flash,CSID,OIS等)。
8.初始化sensor模塊。
9.創建基於CID info的端口
10.初始化eeprom
1.概述
actuator驅動信息文件是指vendor目錄下的$ActuatorName_actuator.h文件(如gigaset_actuator.h)。此信息文件就是一個actuator_driver_ctrl_t結構體。包括actuator_params與actuator_tuned_params兩部分,即driver與tunning兩部分。文檔以目前最主流的VCM為例進行參數的介紹。
2.driver部分信息——actuator_params
act_type: actuator類型,目前就VCM、PIEZO及HVCM三種
data_size:數據長度(VCM指傳輸lens位置的數據長度?)
init_settings:actuator的初始化參數
reg_tbl:寄存器列表,用來下actuator移動參數,這些參數又包括如下一些參數選項
reg_write_type:寫入數據的格式,VCM一般使用DAC,10位表示0~1023
reg_addr:寫入數據寄存器地址,如果不需要具體地址,則配置為0xFFFF
data_shift:數據對應寄存器的移位
hw_mask:其它bit位的控制設置,比如VCM的ring_ctrl
hw_shift:控制設置對應寄存器的移位
reg_tbl部分舉例AD5823如下:
.reg_tbl =
{
.reg_tbl_size = 1,
.reg_params =
{
{
.reg_write_type = MSM_ACTUATOR_WRITE_DAC,
.hw_mask = 0x00000400, ////除data外的其它b it位的mask
.reg_addr = 0x04, //寄存器寫地址為reg04
.hw_shift = 0, //hw_mask已經制定了明確的bit位,所以不再需要移位
.data_shift = 0, //reg05[7:0]為數據,所以不需要移位
},
},
},
查看驅動代碼大致如下:
value = (next_lens_position <<
write_arr[i].data_shift) |
((hw_dword & write_arr[i].hw_mask) >>
write_arr[i].hw_shift);
最終獲得I2C數據代碼如下:
i2c_byte1 = (value & 0xFF00) >> 8; //對應reg04寄存器值
i2c_byte2 = value & 0xFF; //對應reg05寄存器值
這裡獲得的value及需要寫入寄存器的值,上面的hw_dword參數來自於tunning參數表中的hw_params (此驅動的hw_params值為0x00000400)。下面再來看看AD5823的寄存器介紹:
上圖中reg04的bit[3]設置是否開啟RING_CTRL模式,bit[7:4]為零,由前面講到的hw_dword及hw_mask來設置。
再舉例DW9714如下:
.reg_params =
{
{
.reg_write_type = MSM_ACTUATOR_WRITE_DAC,
.hw_mask = 0x0000000F, //除data外的其它b it位的mask
.reg_addr = 0xFFFF, //沒有寄存器地址
.hw_shift = 0, //hw_mask已經制定了明確的bit位,不再需要移位
.data_shift = 4, //10bit的數據移位
},
},
再看DW9714規格書寄存器介紹如下:
與代碼對比可以看出,byte2的S[3:0]對應給出了mask位,需要根據tunning參數的hw_dword來確定具體的模式,此驅動給出的tunning參數為0x00000004,即S[3:0]-0100;而PD及FLAG被設置為0。
3. tunning部分——actuator_tuned_params
tunning參數分為向近景方向移動與向遠景方向移動兩部分(MOVE_NEAR&MOVE_FAR),每個部分又可設定多個移動場景,各個場景參數可以單獨設定,以便實現更細化的控制。
高通在這裡實現了一個lens位置數組,數組中的具體值對應著實際的lens位置,所以在驅動中可以看到step_pos與lens_pos兩個參數,分別對應詞數組表的下標index及當前index對應的值(此值為lens的實際位置)。代碼體現如下:
curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos];
具體一些參數介紹如下:
initial_code:初始lens的位置,對應VCM的0~1023的范圍
scenario_size:向近景與遠景方向移動部分的場景數
ringing_scenario: 各對應場景向近景與遠景方向的移動步數最大值
region_size:對lens的移動進行多個區段劃分,可實現更細化的設置(針對非線性?)
region_params:對應每個區段的在移動步數表中的index范圍,即bound
step_bound:對應具體的macro及infinity的bound邊界值,所有區段合起來就是總 長度(對應上面提到的表的長度)
code_per_step:在當前分區中每個step對應移動的實際lens長度
上面這些參數一起最終聲場移動步數表數組,具體在msm_actuator_init_step_table函數中實現,具體可查看代碼(msm_actuator.c文件),關鍵語句:
cur_code = set_info->af_tuning_params.initial_code; //初始值
code_per_step =a_ctrl->region_params[region_index].code_per_step; //當前分區的步長
cur_code += code_per_step; //每步疊加一個步長
damping:這部分參數用來做剎車消除磁滯,與前面的場景參數相呼應。
ringing_params:各個具體場景對應的參數設置。下面三個參數為當前場景各區段的參數設置。
damping_step: // 對應場景lens每次移動的最大步長
damping_delay: // 對應場景lens每次移動I2C命令間的延時
hw_params: // 對應場景actuator硬件寄存器的控制設置
上面寫了那麼多,總結起來也就下面幾點:
1.有一個數組lens_pos[max_bound] -----> 對應前面多次提到的移動步數數組
2.為了更好的消除磁滯,設定了多種移動的規則,根據每次移動的最大step來確定使用哪個規則---->規則即前面講的場景,最大step就是前面的ringing_scenario數組中對應每個場景的值,這個值表示數組的下標。
3.將整個移動范圍分成多個區間,細分每個區間lens步進,針對線性化問題改善。
4.針對不同場景的不同區間設定磁滯消除參數。
另外要注意的一點是高通AF算法發出的參數為step數量,而不是具體的lens位置,具體lens位置需要查表獲得。其實從這部分參數設定來看可以發現這裡主要對驅動芯片的驅動方式進行設定,算法上前進步長的判斷還是在其它地方實現,只要控制了每次的step長度,也就相當於進行了磁滯控制,所以參數damping_step設置為最大值是可以理解的。
下面舉例來說明一下:
.actuator_tuned_params =
{
.scenario_size =
{
1, /* MOVE_NEAR */ //場景數量,這裡就定義了一個場景
1, /* MOVE_FAR */
},
.ringing_scenario =
{
/* MOVE_NEAR */
{
400, //對應場景最大步子,移動步數小於此值表示適用此場景參數。這裡只定義了一個場景,所以將此值設置為最大步數(即數組容量)。此值表示每次移動步數的步子大小判斷,不是具體的數組下標。
},
/* MOVE_FAR */
{
400,
},
},
.initial_code = 70, //初始lens的位置
.region_size = 1, //對lens的移動進行多個區段劃分,用於不同區間磁滯的參數設定
.region_params =
{
{
.step_bound = //對應每個區段的step bound,這裡只定義了一個區間,即整個VCM移動過程使用同樣的磁滯處理。
{
400, /* Macro step boundary*/ //當前區段的結束step
0, /* Infinity step boundary*/ //當前區段的起始step
},
.code_per_step = 1, // step表中lens的步進長度
},
},
.damping =
{
/* damping[MOVE_NEAR] */
{
/* Scenario 0 */ //當前場景的磁滯參數
{
.ringing_params =
{
/* Region 0 */ //當前區間的磁滯參數
{
.damping_step = 0x3FF, //這裡設置為最大值1023,相當於不做磁滯處理,直接一次設定到需要的值。
.damping_delay = 6000,
.hw_params = 0x00000000,
},
},
},
},
/* damping[MOVE_NEAR] */
{
/* Scenario 0 */
{
.ringing_params =
{
/* Region 0 */
{
.damping_step = 0x3FF,
.damping_delay = 6000,
.hw_params = 0x00000000,
},
},
},
},
},
}, /* actuator_tuned_params */
主要記錄一下啟動的過程,順便看看是否能把握到數據流的走向,這次以高通810平台Android5.1為藍本。本篇主要講述Framework及Hardware部分,涉及到JAVA及driver部分的如有必要也會捎帶提及一下。
一、啟動Camera在JAVA層開始,主要是啟動了一個OpenCameraThread的線程(PhotoModule.java),代碼如下:
1 if (mOpenCameraThread == null && !mActivity.mIsModuleSwitchInProgress) { 2 mOpenCameraThread = new OpenCameraThread(); 3 mOpenCameraThread.start(); 4 }
在這個線程中只做了兩件事情,整個Camera的啟動過程也是圍繞著他們展開的:
1 private class OpenCameraThread extends Thread { 2 @Override 3 public void run() { 4 openCamera(); // 第一步開啟camera 5 startPreview(); // 第二部開始preview 6 } 7 }
二、開啟第一步openCamera
1. 還是在Java層,openCamera主要干了下面這些事情:
1.1CameraUtility.openCamera ------->重點部分
1.2 getParameters ------->一些參數獲得
1.3 initializeFocusManager -------->Focus管理
1.4 initializeCapabilities -------->Camera選擇的一些特性
1.5 sendMSG--CAMERA_OPEN_DONE -------->Open完成之後的處理
2. 上面那麼多,我們只需要關注第一點就可以了,現在繼續往下查看。關於這部分網上也有不少資料,可自行查看,直接查看源代碼也比較清晰。總之在Camera開啟成功後Java層會獲得一個mCameraDevice,這個其實就是Camera的一個代理--AndroidCameraProxyImpl,當然我們要關注的是前面開啟的過程。
1 public CameraManager.CameraProxy cameraOpen( 2 Handler handler, int cameraId, CameraOpenErrorCallback callback) { 3 mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0, 4 CameraOpenErrorCallbackForward.getNewInstance( 5 handler, callback)).sendToTarget(); 6 mCameraHandler.waitDone(); 7 if (mCamera != null) { 8 return new AndroidCameraProxyImpl(); // ----->這個就是丟給前台的mCameraDevice 9 } else { 10 return null; 11 } 12 }
3. OPEN_CAMERA的處理先使用傳統的方式開啟,如果不行再嘗試新的open方式,這裡開始就轉到frameworks。frameworks基本沒做什麼東西,就是一個傳話筒而已,很快就到了JNI。
1 public void handleMessage(final Message msg) { 2 try { 3 switch (msg.what) { 4 case OPEN_CAMERA: 5 try { 6 mCamera = android.hardware.Camera.openLegacy(msg.arg1, // 這裡的android.hardware.Camera就指向frameworks\base\core\java\android\hardware\camera.java 7 android.hardware.Camera.CAMERA_HAL_API_VERSION_1_0); // 先使用傳統的接口打開 8 } catch (RuntimeException e) { 9 /* Retry with open if openLegacy fails */ 10 Log.v(TAG, "openLegacy failed. Using open instead"); 11 mCamera = android.hardware.Camera.open(msg.arg1); //若出現異常再使用新的接口打開,這裡提供兩種方式也是方便硬件廠商做兼容及區別處理,到現在的AndroidM版本,Camera HAL已經有了3個版本了,但高通810目前還是使用HAL1 12 }
4. openLegacy的JNI調用為native_setup。JNI調用將轉到android_hardware_Camera.cpp文件,關於這部分網上也有很多資料。其實這裡也沒干什麼事情,跟frameworks一樣,都只是一個傳遞的作用,整個Camera框架真正有內涵的還是HAL以及driver部分,當然APP部分的各種呈現也很有內涵。
1returnnative_setup(newWeakReference
這裡通過JNI到了C++層
1 // connect to camera service 2 static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, 3 jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName) 4 { 。。。。。。10 //ALOGV("native setup: halversion %d", halVersion);//native setup: halversion 256 11 spcamera; 12 if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) { //CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -2 13 // Default path: hal version is don't care, do normal camera connect. 14 camera = Camera::connect(cameraId, clientName, 15 Camera::USE_CALLING_UID); 16 } else { 17 jint status = Camera::connectLegacy(cameraId, halVersion, clientName, 18 Camera::USE_CALLING_UID, camera); 19 if (status != NO_ERROR) { 20 return status; 21 }
調用到Camera的connectLegacy
1 status_t Camera::connectLegacy(int cameraId, int halVersion, 2 const String16& clientPackageName, 3 int clientUid, 4 sp& camera) // 需返回的Camera設備 5 { 6 ALOGV("connectLegacy: connect legacy camera device,halVersion %d", halVersion); 7 sp c = new Camera(cameraId); 8 sp cl = c; // 這個示例很重要,CameraService將包含此示例,用來執行回調調用到Client這邊。 9 status_t status = NO_ERROR; 10 const sp & cs = CameraBaseT::getCameraService(); // 獲取CameraService 11 12 if (cs != 0) { 13 status = cs.get()->connectLegacy(cl, cameraId, halVersion, clientPackageName, // 調用CameraService的connectLegacy 14 clientUid, /*out*/c->mCamera); 15 } 16 if (status == OK && c->mCamera != 0) { 17 c->mCamera->asBinder()->linkToDeath(c); 18 c->mStatus = NO_ERROR; 19 camera = c; 20 } else { 21 ALOGW("An error occurred while connecting to camera: %d", cameraId); 22 c.clear(); 23 } 24 return status; 25 }
這部分有太多的資料闡述,畢竟還是Android通用代碼,未涉及到硬件芯片廠商的差異性。簡約一些,connect會獲得一個CameraService,這個Service將進行HAL層設備的管理及交互。我們跳轉到CameraService::connectLegacy可以看到在這個Service中包含了一個CameraClient,而上面說到的返回的Camera設備就是這個Client(源碼:device = client;)。
1 status_t CameraService::connectLegacy( 2 const sp& cameraClient, 3 int cameraId, int halVersion, 4 const String16& clientPackageName, 5 int clientUid, 6 /*out*/ 7 sp & device) {// go here 8 9 。。。。。。 10 11 status_t status = validateConnect(cameraId, /*inout*/clientUid); 12 if (status != OK) { 13 return status; 14 } 15 16 sp client; 17 { 18 sp clientTmp; 19 Mutex::Autolock lock(mServiceLock); 20 if (!canConnectUnsafe(cameraId, clientPackageName, 21 cameraClient->asBinder(), 22 /*out*/clientTmp)) { 23 return -EBUSY; 24 } else if (client.get() != NULL) { 25 device = static_cast (clientTmp.get()); 26 return OK; 27 } 28 29 status = connectHelperLocked(/*out*/client, // 這個client就是上面調用中要返回的Camera設備device 30 cameraClient, // 傳下來的ICameraClient實例 31 cameraId, 32 clientPackageName, 33 clientUid, 34 callingPid, 35 halVersion, 36 /*legacyMode*/true); 37 if (status != OK) { 38 return status; 39 } 40 41 } 42 // important: release the mutex here so the client can call back 43 // into the service from its destructor (can be at the end of the call) 44 45 device = client; // 返回的Client設備 46 return OK; 47 }
在connectHelperLocked中,將根據不同HAL版本實現不同的示例,目前有CameraClient和Camera2Client兩種
1 case CAMERA_DEVICE_API_VERSION_1_0: 2 ALOGV("create a camera client.line %d",__LINE__); 3 client = new CameraClient(this, cameraClient, // 前面那個CameraClient是要實現的Client,第二個cameraClient就是上面傳下來的ICameraClient實例 4 clientPackageName, cameraId, 5 facing, callingPid, clientUid, getpid(), legacyMode); 6 break; 7 case CAMERA_DEVICE_API_VERSION_2_0: 8 case CAMERA_DEVICE_API_VERSION_2_1: 9 case CAMERA_DEVICE_API_VERSION_3_0: 10 case CAMERA_DEVICE_API_VERSION_3_1: 11 case CAMERA_DEVICE_API_VERSION_3_2: 12 ALOGI("create a camera2 client"); 13 client = new Camera2Client(this, cameraClient, 14 clientPackageName, cameraId, 15 facing, callingPid, clientUid, getpid(), legacyMode); 16 break;
上面獲得Client後會調用connectFinishUnsafe(client,client->getRemote()),這個函數將會執行到HAL的interface,所以廠家通用。
1 status_t CameraService::connectFinishUnsafe(const sp& client, 2 const sp & remoteCallback) { 3 status_t status = client->initialize(mModule); //這個mModule很關鍵,是在CameraService.cpp中onFirstRef中獲取的。其實就是根據類名等規則加載一個動態庫,一般在/system/lib/hw/目錄下,如camera.msm8994.so 4 if (status != OK) {
5 ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__); 6 return status; 7 } 8 if (remoteCallback != NULL) { 9 remoteCallback->linkToDeath(this); 10 } 11 12 return OK; 13 }
上面client中的initialize會調用到hardware interface中的initialize並設置callback (CameraHardwareInterface.h),而在這裡就會調用到具體廠家HAL層的函數了。
1 status_t initialize(hw_module_t *module) 2 { 3 ALOGI("Opening camera %s", mName.string()); 4 camera_module_t *cameraModule = reinterpret_cast(module); 5 camera_info info; 6 status_t res = cameraModule->get_camera_info(atoi(mName.string()), &info); 7 if (res != OK) return res; 8 ALOGI("init hal-ver:%d,dev-ver %d",module->module_api_version,info.device_version);// 515=v2.3,770=v3.2 9 int rc = OK; 10 if (module->module_api_version >= CAMERA_MODULE_API_VERSION_2_3 && 11 info.device_version > CAMERA_DEVICE_API_VERSION_1_0) {ALOGI("open legacy"); 12 // Open higher version camera device as HAL1.0 device. 13 rc = cameraModule->open_legacy(module, mName.string(), // 這個cameraModule就上具體廠家代碼編譯出來的so庫,這裡對應QCamera2Factory.cpp 14 CAMERA_DEVICE_API_VERSION_1_0, 15 (hw_device_t **)&mDevice); 16 } else { 17 rc = CameraService::filterOpenErrorCode(module->methods->open( 18 module, mName.string(), (hw_device_t **)&mDevice)); 19 } 20 if (rc != OK) { 21 ALOGE("Could not open camera %s: %d", mName.string(), rc); 22 return rc; 23 } 24 initHalPreviewWindow(); 25 return rc; 26 }
5.到這裡就正式進入HAL層,每個廠家的實現代碼也變得不一樣,但大致模式還是差不多,都是通過V4L2的命令接口來控制具體的Camera設備。 HAL及driver將在另一篇繼續討論。
經過前面幾篇文章的分析,我們了解到了Google原生是如何拆分apk的,並且我們自己可以通過解析manifest文件,通過創建插件ClassLoader,Resource
離線緩存是指在有網絡的狀態下將從服務器獲取的網絡數據,如Json 數據緩存到本地,在斷網的狀態下啟動APP時讀取本地緩存數據顯示在界面上,常用的APP(網易新聞、知乎等等
在web頁面中,有a標簽的超鏈接實現跳轉,同樣在Android當中,用TextView控件來顯示文字,實現它的事件來跳轉。用過微博Android手機端的朋友的都知道微博正
RecyclerView出來很長時間了,相信大家都已經比較了解了,這裡我把知識梳理一下,其實你把他看成一個升級版的ListView也是可以的,為什麼這樣說呢?我們一起來