在添加一个串口触摸屏读写功能的时候,对我这android 新手来说,可谓历尽万难,经历了尝试利用系统自带的SerialService 改写,到自己写一个system service,再转到native service。简直一把泪啊
先来说说添加system service
1. 定义接口frameworks\base\core\java\android\hardware\ITouch.aidl
interface ITouch {
void touchsetup();
int writetouch(byte[] buf);
}
2. \frameworks\base\services\core\java\com\android\server\Touchservice.java
public class Touchservice extends ITouch.Stub {
private final String TAG = "Touchservice ";
private final Context mContext;
public Touchservice (Context context) {
mContext = context;
}
public void TouchSetup()
{
native_touchsetup();
}
public int writeTouch(byte[] buf)
{
return native_writeTouch(buf, buf.length);
}
private native void native_touchsetup();
private native int native_writeTouch(byte[] buf, int len);
}
3. 添加JNI 文件 frameworks\base\services\core\jni\com_android_server_touchservice.cpp
void register_android_server_TouchService_Setup(JNIEnv *env, jobject thiz) {
fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == -1) {
ALOGE("Cannot open port %s",SERIO_DEVICE);
return -1;
}
/* Configure device */
int ret;
int ldisc;
unsigned long type;
struct termios tio;
tcgetattr(fd, &tio);
tio.c_iflag = IGNPAR | IGNBRK;
tio.c_cflag = HUPCL | CS8 | CREAD | CLOCAL | B115200;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
tio.c_oflag = 0;
tio.c_lflag = 0;
cfsetispeed(&tio, B115200);
cfsetospeed(&tio, B115200);
ret = tcsetattr(fd, TCSANOW, &tio);
if (ret < 0) {
ALOGE("TCSANOW error");
close(fd);
return -1;
}
ldisc = N_MOUSE;
ret = ioctl(fd, TIOCSETD, &ldisc);
if (ret < 0) {
ALOGE("TIOCSETD error");
close(fd);
return -1;
}
type = SERIO_GENERAL_IRTSC | (SERIO_ANY << 8) | (SERIO_ANY << 16);
ret = ioctl(fd, SPIOCSTYPE, &type);
if (ret < 0) {
ALOGE("SPIOCSTYPE error");
close(fd);
return -1;
}
read(fd, NULL, 0);
return 0;
}
static jint register_android_server_TouchService_Write(JNIEnv *env, jobject thiz, jbyteArray data, jint len)
{
jbyte *bytes;
unsigned char *buf;
int i;
bytes = env->GetByteArrayElements(data,NULL);
if(bytes == NULL) {
return false;
}
buf =(unsigned char *)calloc(len,sizeof(char));
if(buf == NULL)
{
return false;
}
for(i=0;i<len;i++)
{
*(buf+i)=(unsigned char)(*(bytes+i));
}
i = write(fd, buf, len);
env->ReleaseByteArrayElements(data,bytes,0);
free(buf);
return i;
}
static JNINativeMethod method_table[] = {
{ "native_Touchsetup", "()V",(void*)register_android_server_TouchService_Setup},
{ "native_writetouch", "([BI)I",(void*)register_android_server_TouchService_Write},
};
int register_android_server_touchservice(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/touchservice",
method_table, NELEM(method_table));
}
frameworks\base\services\core\jni\Android.mk.添加一行,把JNI 文件编译进去
$(LOCAL_REL_DIR)/com_android_server_touchservice.cpp
frameworks\base\services\core\jni\onload.cpp 添加如下内容
..............
int register_android_server_touchservice(JNIEnv* env);
..............
register_android_server_touchservice(env);
4. frameworks\base\core\java\android\hardware\TouchManager.java
private ITouch mTouchService;
public TouchManager (Context context, ITouch service) {
mContext = context;
mTouchService= service;
}
public void WriteTouchManager(byte[] data)
{
try{
mTouchService.writetouch(data);
} catch (RemoteException e) {
e.printStackTrace();
}
}
5.Context.java 添加下面一行service名称
public static final String TOUCH_SERVICE = "touchservice";
6. SystemServer.java 照葫芦画瓢addservice
try {
Slog.i(TAG, "touchService");
touch= new TouchService(context);
ServiceManager.addService(Context.TOUCH_SERVICE, touch);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting touch service", e);
}
7. SystemServiceRegistry.java 注册service
registerService(Context.TOUCH_SERVICE, TouchManager.class,
new CachedServiceFetcher<TouchManager>() {
@Override
public TouchManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.TOUCH_SERVICE);
return new TouchManager(ctx, ITouch.Stub.asInterface(b));
}});
全部文件添加,修改完毕,要make update-api, 再make才能把xxx.aidl 的接口更新到两个txt文件,如果直接make, 后面报错会让你先make update-api 一下
编译进系统后,在串口执行命令,就可以看到添加的service 了
touchservice: [android.hardware.ITouch]
重点来了,这样添加的system service ,即使在device/....../sepolicy/目录下的 ***.te 文件添加了该service权限,最大也只能到system, 如果JNI 里open 一个设备,再调用内核里的函数有ioctl 操作就会失败,内核里面有下面这段代码检查权限,要有root 权限才能执行
if (!capable(CAP_SYS_ADMIN))
{
printk(" no CAP_SYS_ADMIN\n");
return -EPERM;
}
其他诸如open,read/write 操作,对应内核里的函数没有上面代码,则能操作成功
所以,折腾了几天功夫,有ioctl 操作,在android 的system service无法达成要求,只能改为用native service。因为native service 最后编译出来的执行bin 文件,是放在init.rc 里启动,可以设置权限到root,应用只调用了service 的接口,实际ioctl 操作在native service,所以没有问题。
这里就不叙述native service的东西了,可以参考上一篇博文