Android8.0 之从驱动层到应用层的通信

前段时间公司要做一个用旋钮调节音量的手机,简单粗暴的做法是应用层用一个死循环读驱动文件,然后驱动一个死循环读硬件ADC并写文件。但是这样太low了。相信很多小伙伴都知道UEvent机制,我们Android系统电池上报相关的信息就是用的这个机制。好了废话不多说了,直接上代码。

首先是驱动层加节点并读取硬件信息。

我加的文件路径:
kernel-3.18\drivers\input\keyboard\kpd.c

首先在kpd.c文件里面

#define     VOLUME_MAJOR 156
unsigned int g_volume_val=0;
static struct class *volume_class = NULL;
static struct device *volume_dev = NULL;
static struct work_struct vol_work;
//当上层读取file时会调用show_volume_level函数来更新及显示文件内容
static ssize_t show_volume_level(struct device *dev,struct device_attribute *attr, char *buf)
{
     return sprintf(buf, "%u\n", g_volume_val);;
}
//当上层写入file时会调用 store_volume_level函数来更新及写入文件内容
static ssize_t store_volume_level(struct device *dev,struct device_attribute *attr, const char *buf, size_t size)
{   
    char *pvalue = NULL;
    unsigned int test_item = 1;
    if(buf != NULL)
    {
        test_item = simple_strtoul(buf,&pvalue,10); 
        g_volume_val = test_item;
        printk("store_volume_level test_item=%d .\n",test_item);        
    }
    return size;;
}

void vol_work_func(struct work_struct *work)
{   
    g_volume_val++;
    kobject_uevent(&volume_dev->kobj, KOBJ_CHANGE); 
    mdelay(2000);
    schedule_work(&vol_work);   
}
static DEVICE_ATTR(volume_level, 0664, show_volume_level, store_volume_level); 
static int __init kpd_mod_init(void)
{   
    //在sys文件系统下建立一个类
    volume_class = class_create(THIS_MODULE, "volume");
    //在类里建立一个设备 
    volume_dev = device_create(volume_class, NULL, 
                                          VOLUME_MAJOR, 
                                           NULL, 
                                           "volume_device");    
    //在设备目录下建立一个属性文件                                                                           
    ret_device_file = device_create_file(volume_dev, &dev_attr_volume_level);
    //注意思在使用kobject_uevent函数发送uevent事件时需要建立一个工作队列来发送,否则会在发送过程中内核死掉。
    INIT_WORK(&vol_work,vol_work_func);
    schedule_work(&vol_work);
    return 0;
}
 static void __exit kpd_mod_exit(void)
{
    class_destroy(volume_class);
}
 module_init(kpd_mod_init);
 module_exit(kpd_mod_exit);

上面代码只是大概流程,不是具体实现细节,小伙伴可根据自己的业务具体实现。
DEVICE_ATTR(volume_level, 0664, show_volume_level, store_volume_level) ,还有上面
store_volume_level ,show_volume_level 这2个方法是里面包含的volume_level 和黑体字标注是固定写法,可根据自己的业务重新命名。最大权限只能给0664 否则编译会出错。

kernel-3.18\drivers\input\keyboard\Makefile这个文件
obj-$(CONFIG_KEYBOARD_MTK) := kpd.o

具体Makefile根据自己需求去写。

其次是JNI层根据驱动uevent事件通知服务主动去读取节点。

com_android_server_vm_VolumeControlService.cpp

static jint readFromFile(JNIEnv* env, jobject obj) {
const char *path = "/sys/class/volume/volume_device/volume_level";
const int SIZE = 128;
char buf[SIZE];
ssize_t count = 0;
int g_vol_val = -1;

if (access(path, R_OK) == 0){
int fd = open(path, O_RDONLY, 0);
if (fd == -1) {
    LOGI("Could not open '%s'\n",path);
    return -1;
}
count = TEMP_FAILURE_RETRY(read(fd, buf, SIZE));
LOGI("g_vol_val '%s'\n",buf);
g_vol_val = atoi(buf);
LOGI("g_vol_val '%d'\n",g_vol_val);
close(fd);
}else{
    LOGI("Could not access '%s'\n",path);
}
return g_vol_val;
}

jni 函数注册就不写了,自己不会百度一下。

然后framework 服务层接到驱动通知主动调用native函数读取。

public class VolumeControlService  extends IVolumeControlService.Stub { 

    private String TAG = "VolumeControlService"; 

    public VolumeControlService(Context context) {
        mUEventObserver.startObserving("SUBSYSTEM=volume");
      }

    @Override  
    public int readState() throws RemoteException {         
        return 0;
    } 

   private UEventObserver mUEventObserver = new UEventObserver() {

         @Override
         public void onUEvent(UEventObserver.UEvent event) { 
             int volumeLevel = readFromFile();
             //TODO 发送广播到应用层,应用层监听
         }

    };

    private static native int readFromFile();   

}      

需要用到的权限。

device.te里面定义
type volume_device, dev_type;
system_server.te里面添加
allow system_server volume_device:chr_file rw_file_perms;
allow system_server volume_device:dir r_dir_perms;
service.te里面添加
type volumecontrol_service, system_api_service, system_server_service, service_manager_type;
service_contexts里面添加
volumecontrol u:object_r:volumecontrol_service:s0

至此驱动到应用层通讯已经完成,如有疑问,请在下方留言。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Android 8.0 及以上版本,为了增强应用程序的安全性,Android 引入了后台限制,禁止未在前台运行的应用程序启动服务。如果您想在后台启动服务,需要使用 `startForegroundService()` 方法。这个方法会启动一个前台服务,然后你可以在服务启动后在通知栏显示一个通知,以此来告知用户服务正在运行。 以下是一个使用 `startForegroundService()` 的示例代码: ``` if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 创建一个 NotificationChannel NotificationChannel channel = new NotificationChannel("channel_id", "channel_name", NotificationManager.IMPORTANCE_DEFAULT); // 向系统注册 NotificationChannel NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(channel); } // 创建一个 Intent,启动你的服务 Intent serviceIntent = new Intent(this, YourService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 在 Android 8.0 及以上版本上,需要调用 startForegroundService() 方法启动服务。 startForegroundService(serviceIntent); } else { // 在 Android 8.0 以下版本上,可以直接调用 startService() 方法启动服务。 startService(serviceIntent); } ``` 注意:如果你使用的是 `startForeground()` 方法,会在 Android 8.0 及以上版本上抛出 `IllegalStateException` 异常,因为 Android 8.0 及以上版本禁止在后台启动服务。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BFP_BSP

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值