以下例子均为未经测试的代码,也重点在整个的流程概况。目前仍有些不是太明白的地方。 分以下几点(忽略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) |