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