Android硬件访问服务

上一章JNI直接访问硬件可能导致多个应用同时访问一个驱动,就可能导致驱动出现问题,所以我们可以只让一个应用程序来访问硬件,这个应用程序成为“SystemServer”,APP有应用请求统一发给它,由它统一管理所有的service。而我们这章的目的也是建立一个led service。
SystemServer使用java写的,访问硬件只能用C,所以中间也需要使用JNI。
SystemServer的源码在android目录下:
frameworks\base\services\java\com\android\server\SystemServer.java

这里先概述一下SystemServer的过程:

  • 通过LoadLibrary来加载C库,
  • C库的JNI_Onload函数里面注册本地方法,分别调用各个硬件的函数来注册本地方法,比如LED、振动器、串口等等
  • SystemServer:对每个硬件addService(Server里面有很多个Service,由Server提供Service),每个硬件都需构造Service(即注册本地方法)
  • APP使用:
    首先获得服务:get Service
    然后使用服务:执行Service方法
    在这里插入图片描述

SystemServer源码解析:
文件:SystemServer.java
SystemServer在源码中是一个类,里面实现了很多类方法,以Vibrator这个service作为参考例子,模仿写一个led service,我们从main这个方法开始解析

Main()
– SystemServer().run()
-----System.loadLibrary(“android_servers”)—>初始化native service,即加载C库,对应文件是 onload.cpp,里面有JNI_OnLoad,用于注册各种本地方法。
在这里插入图片描述
比如:
--------Register_android_server_VibratorService(env)—>对应com_ android_server_VibratorService(对应文件名),里面负责实现native方法
-----StartOtherServices()—>里面定义了各种service—>new VibratorService—>addService,在VibratorService中调用各种native方法。在native层中不会直接调用操作硬件的接口,而是通过一层hal层进行了封装,native层通过加载hal层的库,去调用hal层的接口,达到更好的封装效果。
想要APP能使用service需要注册添加各种service进service_manager.c,然后通过getService来获取接口,自己添加的驱动就需要在这里面addService,最上面那副图中的几个进程都会涉及到Binder driver,这个驱动程序并不是内核自带的,而是google公司对linux内核做的修改,添加的一个驱动程序,它可以实现更加高效的进程间通信。
接下来以代码的形式描述一下上面的整个过程,从APP—>server—>service—>JNI(其实应该还有个HAL,但这章主要描述service的建立过程,HAL放到下一章讲)。

整个过程文字描述如下:

1.实现一个aidl(Android接口定义语言)文件,写出这个文件后用Android系统里面的编译命令让它帮我们自动生成那个ILedService.java(接口)文件,怎么写参考源码目录framework里面的振动器代码(IVibratorService.aidl,位于frameworks目录),这个aidl文件位于源码目录下的frameworks/base/core/java/android/os,然后修改它的Android.mk (位于frameworks/base/),vi Android.mk,全局搜索关键字“Virbrator”可以找到相应的aidl文件定义,添加上我们自己的Led(core/ java/android/os /ILedService.aidl)文件即可,修改完后在目录(frameworks/base/)下执行mmm .(mmm <目录名>,这会根据目录名下的mk文件编译)命令,可能会遇到报错(mmm: command not found),在android源码目录下执行“. setenv”,然后选择对应的单板即可。执行mmm .后系统会帮我们自动生成ILedService.java文件。编译成功后在out目录下搜索“find –name “ILedService.java””可以找到相应生成的java接口文件。

aidl文件内容如下:

package android.os;

/** @hide */

interface ILedService
{
    int ledCtrl(int which, int status);
}

在这里插入图片描述
在这里插入图片描述
2.实现LedService.java
里面主要实现调用我们定义的本地方法然后修改SystemServer.java把这个service添加进去,之后APP就可以通过getService获得LedService从而操作LED了。

package com.android.server;

import android.os.ILedService;


public class LedService extends ILedService.Stub {
    private static final String TAG = "LedService";
  
    // call native c function to acess hardware
    public LedService() {
		native_ledOpen();
    }
    
    public int ledCtrl(int which, int status) throws android.os.RemoteException
    {
		return native_ledCtrl(which, status);
    }
	public static native int native_ledCtrl(int which, int status);
	public static native void native_ledOpen();
	public static native void native_ledClose();
 }

把写好的service上传到服务器编译,上传到目录:
frameworks/base/services/core/java/com/android/server/LedService.java
由于添加了新的service,所以我们需要去修改下SystemServer.java,参考vibrator的这个service写即可,添加如下代码到startOtherServices:

Slog.i(TAG, "Led Service");
ServiceManager.addService("led", new LedService());//The 'new' can call LedService Constr

修改完后重新上传至:
frameworks/base/services/java/com/android/server/SystemServer.java

**注意:**虽然新增了LedService.java文件,这里我们不需要去修改 frameworks/base/services/core/Android.mk
因为它的内容里已经把该目录下所有JAVA文件自动包含进去了,后面编译会自动重新加载这个LedService.java文件:

LOCAL_SRC_FILES += \
    $(call all-java-files-under,java) //包含当前目录及子目录下所有的java文件

3.JNI 实现com_android_server_LedService.cpp
上面LedService.java的native方法都是由这个文件来实现的,即JNI的相关代码,主要将数组里面的方法注册到LedService这个方法里面,并修改Onload.cpp将这个注册函数添加进去,代码如下:

#define LOG_TAG "LedService"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include <utils/misc.h>
#include <utils/Log.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>


namespace android
{
	static jint fd;

	jint ledOpen(JNIEnv *env, jobject cls)
	{
		fd = open("/dev/leds_node", O_RDWR);//打开设备LED节点
		ALOGI("native ledOpen : %d", fd);
		if (fd >= 0)
			return 0;
		else
			return -1;
	}
	
	   
	void ledClose(JNIEnv *env, jobject cls)
	{
	  ALOGI("native ledClose ...");
	  close(fd);
	}

	jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
	{
		int ret = ioctl(fd, which, status);//给设备节点传输控制信息
		ALOGI("native ledCtrl : %d, %d, %d", which, status, ret);
		return ret;
	}

	/* 定义JNI字段描述符 */
	static const JNINativeMethod methods[] = {//定义一个映射数组
		{"native_ledOpen", "()I", (void *)ledOpen},//对应c语言的ledOpen,这个函数没有参数,返回值是int类型
		{"native_ledClose", "()V", (void *)ledClose},//对应c语言的ledClose,这个函数没有参数,返回值是void类型
		{"native_ledCtrl", "(II)I", (void *)ledCtrl},//对应c语言的ledCtrl,这个函数有两个int参数,返回值是int类型
	};
		
	int register_android_server_LedService(JNIEnv *env)
	{
		//注册方法
		return jniRegisterNativeMethods(env, "com/android/server/LedService",
				methods, NELEM(methods));
	}
}

onload.cpp添加如下代码:

namespace android {
	........
	int register_android_server_LedService(JNIEnv* env);
}
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
	........
	register_android_server_LedService(env);
}

把新文件上传到服务器, 所在目录:
frameworks/base/services/core/jni/onload.cpp
frameworks/base/services/core/jni/com_android_server_LedService.cpp

由于增加了com_android_server_LedService.cpp文件,需要修改 frameworks/base/services/core/jni/Android.mk :

$(LOCAL_REL_DIR)/com_android_server_LedService.cpp

4.编译
要将上面的cpp文件和java文件一起编译,怎么编译呢?
发现在frameworks/base/services/Android.mk中包含了所涉及的所有的Android.mk文件

include $(wildcard $(LOCAL_PATH)/*/jni/Android.mk)
....
include $(patsubst %,$(LOCAL_PATH)/%/Android.mk,$(services))

所以编译命令为:
mmm frameworks/base/services //生成libandroid_servers库以及class文件
make snod //生成system.img镜像
或者使用./gen-img.sh //生成所有镜像,宝库
5.APP使用
ILedService iLedService;
iLedService = ILedService.Stub.asInterface(ServiceManager.getService(“led”));
iLedService.ledCtrl(0, 1);

工程中需要包含什么:
在linux系统下Android源码目录使用指令:mmm frameworks/base show commands > log.txt 2>&1可以看到aidl文件修改后会涉及哪些文件,从这个文件里面我们可以看出最后会生成一个framework.jar文件,由于我们的ledservice是一个隐藏类(使用/** {@hide} */关键字描述的部分),所以我们是不是需要导入这个framework.jar呢?经搜索后发现我们应该导入classes.jar文件,因为framework.jar文件是dex格式文件,我们的android运行的程序并不是原原本本的java程序,它是把这些java目标文件转换为了dex格式的文件,dex文件是对java文件做了一些优化
jar文件目录:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
将classes.jar复制到D:\project\android studio\APP_0001_LEDDemo\app\libs\armeabi目录下。
Android studio软件打开File->Project Structure
在这里插入图片描述
在这里插入图片描述
在build.gradle(Module.app)里面添加如下几项:

defaultConfig {
       ...
        multiDexEnabled true
    }
    
dexOptions{
        javaMaxHeapSize "4g"
    }

并将implementation files(‘libs\armeabi\classes.jar’)改为

compileOnly files(‘libs\armeabi\classes.jar’),如图所示:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值