|
以下例子均為未經測試的代碼,也重點在整個的流程概況。目前仍有些不是太明白的地方。
分以下幾點(忽略HAL層):
驅動:lichee/linux-3.4/drivers/
主要是初始化相關芯片以及具體的交互功能,然后創建設備節點來與上層交互。(盡量不做邏輯控制)
調用 class_register() 接口時,會在 /sys/class/ 目錄下創建設備節點。
調用 misc_register() 接口時,會在 /dev/ 目錄下創建設備節點。
JNI:android/frameworks/base/services/jni/
該層一般是提供JAVA與 C\C++ 之間通信,這一層我主要用來做一些基本的邏輯控制處理,記得之前有
次參加面試,該公司主要是做類似安防的手機,他們就是主要在這層做邏輯控制。該層主要是通過
映射表來描述 JAVA 方法與 C\C++ 函數之間的對應關系。
static JNINativeMethod method_table[] ={
// JAVA 方法 返回值與參數描述 C\C++ 函數
{"Xxx_Init_native", "()I", (void *)HardwareConfig_XxxInit},
{"Xxx_Cmd_native", "(II)I", (void *)HardwareConfig_XxxCmd},
};
Server:android/frameworks/base/services/java/com/android/server/SystemServer.java
這個主要是供其他 APP 通過 aidl 接口調用到 Server 方法,Server 方法通過JNI映射表,
調用JNI的函數。進一步封裝接口,實現必要的邏輯處理。
aidl:frameworks/base/core/java/android/os
該文件用來生成 Stub 接口文件,用來聲明在 Server 提供的接口。
SystemServer:創建 Server 實例,加入服務表中,供 APP 獲取使用。
接口:
聲明一些數據結構、獲取 Server 根據需要決定是否再進一步封裝接口。
其主要的目的是為了APP能夠更加方便的使用接口。
APP:
通過接口文件,來調用相關的接口從而間接調用底層驅動功能。
調用流程:APP 通過獲取AIDL通信,獲取服務,通過服務調用服務的提供的類,調用JNI接口、JNI再去調用驅動。
具體例子:
/////////////////////////////////////////////////////////////////////////////////////////////
驅動:lichee/linux-3.4/drivers/my_drivers/pt22xx.c
會創建一個設備節點:/dev/MicAdjust 用于給上層交互
/////////////////////////////////////////////////////////////////////////////////////////////
long Pt22xx_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
unsigned char ret = 0;
if (_IOC_TYPE(cmd) != MAGIC)
return -EINVAL;
switch(cmd)
{
case CMD_SETMICVOL: dprintk("CMD_SETMICVOL....");
g_CurrentMicVolume = args;
// 與底層硬件進行交互
break;
case CMD_SETMICECHO: dprintk("CMD_SETMICECHO....");
g_CurrentMicEcho = args;
// 與底層硬件進行交互
break;
case CMD_GETMICVOL: dprintk("CMD_GETMICECHO....");
ret = g_CurrentMicVolume;
break;
case CMD_GETMICECHO: dprintk("CMD_GETMICECHO....");
ret = g_CurrentMicEcho;
break;
default: dprintk("no cmd....");
return -EINVAL;
}
return ret;
}
static struct file_operations Pt22xx_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = Pt22xx_ioctl,
};
static struct miscdevice Pt22xx_misc =
{
.minor = MISC_DYNAMIC_MINOR,
.name = "MicAdjust",
.fops = &Pt22xx_fops,
};
/*************************** 驅動入口 驅動出口 ***************************/
static int __init Pt22xx_init(void)
{
misc_register(&Pt22xx_misc);
return 0;
}
static void __exit Pt22xx_exit(void)
{
}
/*************************** 驅動入口 驅動出口 ***************************/
module_init(Pt22xx_init);
module_exit(Pt22xx_exit);
MODULE_LICENSE("GPL");
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
Jni:android/frameworks/base/services/jni/my_jni/Mic.cpp
會創建一個設備節點:/dev/MicAdjust 用于給上層交互
/////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------
對應驅動的JNI文件
//-------------------------------------------------------------------------------------
// 功放命令實現
jint Mic_Cmd(JNIEnv *env, jobject thiz, jint cmd, jint arg1)
{
int ret = 0;
// 這里則通過識別命令和參數 通過 ioctl 函數設置驅動
// 這里應用層 ioctl() 實際上回調的是驅動層 -> Pt22xx_ioctl() 函數
return ret;
}
// 初始化函數,需要識別并初始化硬件設備
jint Mic_Init(JNIEnv *env, jobject thiz)
{
// 初始化硬件
// 這里會 open /dev/MicAdjust 設備節點
return 0;
}
//-------------------------------------------------------------------------------------
管理和協調各個JNI程序,實現一些基本的邏輯功能,例如參數保存之類的。。
android/frameworks/base/services/jni/com_android_server_HardwareConfig.cpp
//-------------------------------------------------------------------------------------
namespace android {
/* 一些數據結構 */
///////////////////////////// AMP 邏輯實現 ////////////////////////////////////////
/* 處理獲取命令 */
jint HardwareConfig_GetAmp(enum enAmp_CMDTYPE cmd)
{
int ret;
switch(cmd)
{
default:
return eAMP_CMD_ERROR;
}
return ret;
}
// 功放命令實現
jint HardwareConfig_AmpCmd(JNIEnv *env, jobject thiz, jint cmd, jint arg1)
{
int ret = 0;
// 非法參數檢查
// 判斷是否為獲取命令
if( IsAmpGetCmdType(cmd) )
{
return HardwareConfig_GetAmp((enum enAmp_CMDTYPE)cmd);
}
// 參數檢查并糾正
// 調用相應的功能函數
ret = Amp_Cmd((enAmp_CMDTYPE)cmd, (enAmp_ARGSTYPE)arg1);
/* 保存相關參數 */
return ret;
}
///////////////////////////// MIC 邏輯實現 ////////////////////////////////////////
jint HardwareConfig_GetMic(enum enMic_CMDTYPE cmd)
{
int ret;
switch((int)cmd)
{
default:
return eMIC_CMD_ERROR;
}
return ret;
}
jint HardwareConfig_MicCmd(JNIEnv *env, jobject thiz, jint cmd, jint arg1)
{
int ret = 0;
// 非法參數檢查
// 判斷是否為獲取命令
if( IsAmpGetCmdType(cmd) )
{
return HardwareConfig_GetAmp((enum enAmp_CMDTYPE)cmd);
}
// 參數檢查并糾正
// 調用相應的功能函數
ret = Amp_Cmd((enAmp_CMDTYPE)cmd, (enAmp_ARGSTYPE)arg1);
/* 保存相關參數 */
return ret;
}
///////////////////////////// 以上為邏輯功能實現 ////////////////////////////////////////
// 初始化函數,需要識別并初始化硬件設備 這里對硬件初始化主要是調用各自的JNI接口初始化的
jint HardwareConfig_Init(JNIEnv *env, jobject thiz)
{
// 讀取配置文件
/* 功放初始化 */
Amp_Init();
Amp_Cmd(...);
Amp_Cmd(...);
Amp_Cmd(...);
/* 初始化麥克風 */
Mic_Init();
Mic_Cmd(...);
Mic_Cmd(...);
/* 初始化其他硬件 */
return 0;
}
///////////////////////////// JNI ////////////////////////////////////////
// 構建映射表
static JNINativeMethod method_table[] ={
{"HardwareConfig_Init_native", "()I", (void *)HardwareConfig_Init},
{"Amp_Cmd_native", "(II)I", (void *)HardwareConfig_AmpCmd},
{"Mic_Cmd_native", "(II)I", (void *)HardwareConfig_MicCmd},
//.... 其他硬件
};
int register_android_server_HardwareConfigService(JNIEnv* env)
{
return jniRegisterNativeMethods(env,"com/android/server/HardwareConfigService", method_table, NELEM(method_table));
}
}
//-------------------------------------------------------------------------------------
修改 onload.cpp 文件,該文件是用來集中加載所有Jni。
android\frameworks\base\services\jni\onload.cpp
//-------------------------------------------------------------------------------------
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
namespace android {
//... 省略
int register_android_server_HardwareConfigService(JNIEnv* env);
};
using namespace android;
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
//... 省略
register_android_server_HardwareConfigService(env);
return JNI_VERSION_1_4;
}
//-------------------------------------------------------------------------------------
修改 對應目錄下的 Android.mk 文件,該文件是用來編譯這些Jni文件的
android\frameworks\base\services\jni\Android.mk
//-------------------------------------------------------------------------------------
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
// ... 省略
zhc_jni/Mic.cpp \
com_android_server_HardwareConfig.cpp \
onload.cpp
LOCAL_C_INCLUDES += \
// ... 省略
my_jni
ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endif
LOCAL_MODULE:= libandroid_servers
include $(BUILD_SHARED_LIBRARY)
/*************** 完成以上步驟就可以在當前目錄下通過 mm 命令編譯出 JNI SO庫文件了 ***********/
/////////////////////////////////////////////////////////////////////////////////////////////
Server:
在 android\frameworks\base\services\java\com\android\server 建立一個文件 HardwareConfigService.java
/////////////////////////////////////////////////////////////////////////////////////////////
package com.android.server;
import android.content.Context;
import android.util.Slog;
import android.content.Context;
public class HardwareConfigService extends IHardwareConfigService.Stub {
private static final String TAG = "HardwareConfigService";
private static native int HardwareConfig_Init_native();
private static native int Amp_Cmd_native(int cmd, int arg1);
private static native int Mic_Cmd_native(int cmd, int arg1);
private static Context mContext;
///////////////////////////////////// 初始化處理部分 ////////////////////////////////
// 當這個類被創建時,以下代碼將會被執行
public HardwareConfigService(Context context)
{
mContext = context;
HardwareConfig_Init_native(); // <---- 這里調用 JNI 部分接口 進行初始化
}
///////////////////////////////////// 接口定義部分 ////////////////////////////////
public int Amp_Cmd(int Cmd, int arg1) // <<---- 對外提供的調用的接口
{
int ret = 0;
// 在這里可以對接口做進一步封裝
ret = Amp_Cmd_native(Cmd, arg1); // <---- 這里調用 JNI 部分接口
return ret;
}
public int Mic_Cmd(int Cmd, int arg1) // <<---- 對外提供的調用的接口
{
int ret = 0;
// 在這里可以對接口做進一步封裝
ret = Mic_Cmd_native(Cmd, arg1); // <---- 這里調用 JNI 部分接口
return ret;
}
};
//-------------------------------------------------------------------------------------
修改同目錄下的SystemServer.java文件,在ServerThread::run方法里加入
//-------------------------------------------------------------------------------------
class ServerThread extends Thread {
private static final String TAG = "SystemServer";
private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
// ... 省略代碼
@Override
public void run() {
try{ // 在這里 new 了一個對象并添加到 ServiceManager 中。
Slog.i(TAG, "add HardwareConfigService");
ServiceManager.addService("HardwareConfigService", new HardwareConfigService(context));
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting HardwareConfigService", e);
}
// ... 省略代碼
}
}
public class SystemServer {
private static final String TAG = "SystemServer";
// ... 省略代碼
public static final void init2() {
Slog.i(TAG, "Entered the Android system server!");
Thread thr = new ServerThread();
thr.setName("android.server.ServerThread");
thr.start();
}
}
/*************** 完成以上步驟就可以在 android\frameworks\base\services\java 下通過 mm 命令編譯了 ***********/
/////////////////////////////////////////////////////////////////////////////////////////////
AIAL:在 frameworks/base/core/java/android/os/ 新建立一個文件 IHardwareConfigService.aidl
/////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------
加入以下內容,以下內容為 服務里面的public接口,即提供給APP使用的接口
//-------------------------------------------------------------------------------------
package android.os;
interface IHardwareConfigService
{
int Amp_Cmd(int Cmd, int arg1);
int Mic_Cmd(int Cmd, int arg1);
}
//-------------------------------------------------------------------------------------
返回到 frameworks/base 修改 Android.mk 文件
//-------------------------------------------------------------------------------------
LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
## 省略
core/java/android/os/IHardwareConfigService.aidl \ ## 增加這一行代碼
# Include subdirectory makefiles
/////////////////////////////////////////////////////////////////////////////////////////////
接口文件:這個接口文件主要是進一步封裝接口,把一些命令標識什么的都放到問個文件里面
在 android\frameworks\base\policy\src\com\android\internal\policy\impl
新建立一個文件 HardwareConfigServiceInterface.java
/////////////////////////////////////////////////////////////////////////////////////////////
package com.android.internal.policy.impl;
import android.os.IHardwareConfigService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
public class HardwareConfigServiceInterface {
private IHardwareConfigService HardwareConfigService = null;
static final String TAG = "HardwareConfigServiceInterface";
///////////////////////// 功放命令與參數定義 ////////////////////////////
public enum enAmp_CMDTYPE
{
// 設置命令
eAMP_CMD_OPEN, // 打開
eAMP_CMD_CLOSE, // 關閉
eAMP_CMD_SETMUTE, // 靜音
eAMP_CMD_SETVOLUME, // 設置音量
eAMP_CMD_SETEQ, // 設置音效
eAMP_CMD_SETSIGLEEND, // 設置立體聲
eAMP_CMD_SETSURROUND, // 設置環繞
eAMP_CMD_SETTREBLEBASS_CTRL, // 高低音控制
eAMP_CMD_SETTREBLE, // 設置高音
eAMP_CMD_SETBASS, // 設置低音
// .........
eAMP_CMD_ERROR, // 命令執行錯誤或失敗,同時也是命令的個數
};
///////////////////////// 功放接口 ////////////////////////////
/* 獲取某些命令是已經使能了還是關閉了 設計此接口是為了應用層更加方便的判斷 出錯返回 eAMP_ARGS_NOT_USER*/
public enAmp_ARGSTYPE Amp_GetCmd(enAmp_CMDTYPE Cmd)
{
int index = 0;
// 省略轉換代碼
index = Amp_Cmd(Cmd, enAmp_ARGSTYPE.eAMP_ARGS_NOT_USER.ordinal());
// 省略轉換代碼
return enAmp_ARGSTYPE.eAMP_ARGS_NOT_USER;
}
/* 獲取具體的值 例如 高低音的值 出錯返回 -1 */
public int Amp_GetData(enAmp_CMDTYPE Cmd)
{
int ret = Amp_Cmd(Cmd, enAmp_ARGSTYPE.eAMP_ARGS_NOT_USER.ordinal());
// 省略轉換代碼
return ret;
}
public enAmp_CMDTYPE Amp_SetCmd(enAmp_CMDTYPE Cmd, enAmp_ARGSTYPE Args)
{
int index = 0;
// 省略轉換代碼
index = Amp_Cmd(Cmd, Args.ordinal());
// 省略轉換代碼
return enAmp_CMDTYPE.eAMP_CMD_ERROR;
}
public int Amp_SetData(enAmp_CMDTYPE Cmd, int Args)
{
Log.d(TAG, "Amp_SetData() Cmd = " + Cmd + "Args = " + Args );
return Amp_Cmd(Cmd, Args);
}
/* 最終的實現是由此命令完成的 設置命令時出錯返回 eAMP_CMD_ERROR 的索引*/
private int Amp_Cmd(enAmp_CMDTYPE Cmd, int Args)
{
Log.d(TAG, "Amp_SetCmd() Cmd = " + Cmd + " ordinal = " + Cmd.ordinal());
Log.d(TAG, "Amp_SetCmd() Args = " + Args);
try
{
if(null == HardwareConfigService)
{
HardwareConfigService = IHardwareConfigService.Stub.asInterface(ServiceManager.getService("HardwareConfigService"));
}
return HardwareConfigService.Amp_Cmd(Cmd.ordinal(), Args);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return (int)(enAmp_CMDTYPE.eAMP_CMD_ERROR.ordinal());
}
}
///////////////////////// 麥克風接口與定義 ////////////////////////////
/* 功放命令集,具體命令需由具體的功放芯片驅動實現 */
public enum enMic_CMDTYPE
{
// 設置命令
eMIC_CMD_START , // 起始
eMIC_CMD_SETVOLUME, // 設置MIC音量
eMIC_CMD_SETECHO, // 設置MIC回響
eMIC_CMD_SETMUTE, // 麥克風靜音
// 獲取命令
eMIC_CMD_GETTYPE_START, // 獲取命令類型 用于做標識 當大于此值時表示是獲取命令類型
eMIC_CMD_GETVOLUME, // 獲取MIC音量值
eMIC_CMD_GETECHO, // 獲取MIC回響值
eMIC_CMD_GETMUTE,
eMIC_CMD_GETTYPE_STOP, // eMic_CMD_GETTYPE_START 與 eMic_CMD_GETTYPE_STOP 之間是獲取命令的類型
eMIC_CMD_ERROR, // 命令執行錯誤或失敗,同時也是命令的個數
};
public enum enMic_ARGSTYPE // 參數
{
eMIC_ARGS_DISABLE ,
eMIC_ARGS_ENABLE,
eMIC_ARGS_NOT_USER, // 不使用
};
///////////////////////// 麥克風接口實現 ////////////////////////////
/* 獲取某些命令是已經使能了還是關閉了 設計此接口是為了應用層更加方便的判斷 出錯返回 eMIC_ARGS_NOT_USER*/
public enMic_ARGSTYPE Mic_GetCmd(enMic_CMDTYPE Cmd)
{
int index = 0;
// 省略轉換代碼
index = Mic_Cmd(Cmd, enMic_ARGSTYPE.eMIC_ARGS_NOT_USER.ordinal());
// 省略轉換代碼
return enMic_ARGSTYPE.eMIC_ARGS_NOT_USER;
}
/* 獲取具體的值 出錯返回 -1 */
public int Mic_GetData(enMic_CMDTYPE Cmd)
{
int ret = Mic_Cmd(Cmd, enMic_ARGSTYPE.eMIC_ARGS_NOT_USER.ordinal());
// 省略轉換代碼
return ret;
}
public enMic_CMDTYPE Mic_SetCmd(enMic_CMDTYPE Cmd, enMic_ARGSTYPE Args)
{
int index = 0;
// 省略轉換代碼
index = Mic_Cmd(Cmd, Args.ordinal());
// 省略轉換代碼
return enMic_CMDTYPE.eMIC_CMD_ERROR;
}
public int Mic_SetData(enMic_CMDTYPE Cmd, int Args)
{
// 省略轉換代碼
return Mic_Cmd(Cmd, Args);
}
/* 最終的實現是由此命令完成的 設置命令時出錯返回 eMIC_CMD_ERROR 的索引*/
private int Mic_Cmd(enMic_CMDTYPE Cmd, int Args)
{
Log.d(TAG, "Mic_SetCmd() Cmd = " + Cmd + " ordinal = " + Cmd.ordinal());
Log.d(TAG, "Mic_SetCmd() Args = " + Args);
try
{
if(null == HardwareConfigService)
{
HardwareConfigService = IHardwareConfigService.Stub.asInterface(ServiceManager.getService("HardwareConfigService"));
}
return HardwareConfigService.Mic_Cmd(Cmd.ordinal(), Args);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return (int)(enMic_CMDTYPE.eMIC_CMD_ERROR.ordinal());
}
}
}
/*****************************************************************************
昨晚以上的操作之后需要 make updata-api 建議全編一次
*****************************************************************************/
/////////////////////////////////////////////////////////////////////////////////////////////
應用層使用方法:
/////////////////////////////////////////////////////////////////////////////////////////////
1、需要在 Android 工程中加上 android\out\target\common\obj\JAVA_LIBRARIES\android.policy_intermediates\classes.jar
2、導入需要的數據結構
import com.android.internal.policy.impl.HardwareConfigServiceInterface;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enAmp_CMDTYPE;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enAmp_ARGSTYPE;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enMic_CMDTYPE;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enMic_ARGSTYPE;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enDevices_Status;
3、定義全局變量
static private HardwareConfigServiceInterface HardwareConfig = null;
4、在初始化函數中new 對象
HardwareConfig = new HardwareConfigServiceInterface();
5、在需要的地方直接調用接口
HardwareConfig.Amp_GetCmd(enAmp_CMDTYPE.eAMP_CMD_GETSDMUTE)
|
|