因为最近项目Android要用到APP控制相关GPIO控制,因为网上大部分都是app直接通过JNI控制GPIO,这样做存在一个问题,GPIO被多个app打开会报错。同时也违背了Android设计初衷。这里demo也是从其他项目拷贝过来的。自己修改的。源码使用的是Android 5.1 的rk3288.使用控制led灯的方式来实现gpioservice。
通过JNI方式一般流程是
#app->jni->gpio驱动
我自己新编写流程是
#app->ledmanager->ledservice.java->ledservice.cpp->jni->led驱动
首先是led的相关gpio驱动
dts文件

      };        xgpio_beep {                compatible = "9tripod,beep";                gpio = <&gpio6 GPIO_B3 GPIO_ACTIVE_HIGH>;                status = "okay";        };

led驱动代码

struct xgpio_device_t {int gpio;struct device * dev;};static ssize_t xgpio_state_show(struct device * dev, struct device_attribute * attr, char * buf){struct xgpio_device_t * xdev = dev_get_drvdata(dev);if(!strcmp(attr->attr.name, "state")){if(gpio_direction_input(xdev->gpio) == 0)return strlcpy(buf, "0\n", 3);elsereturn strlcpy(buf, "1\n", 3);}return strlcpy(buf, "0\n", 3);}static ssize_t xgpio_state_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count){struct xgpio_device_t * xdev = dev_get_drvdata(dev);unsigned long on = simple_strtoul(buf, NULL, 10);if(!strcmp(attr->attr.name, "state")){if(on)gpio_direction_output(xdev->gpio, 1);elsegpio_direction_output(xdev->gpio, 0);}return count;}static DEVICE_ATTR(state, 0666, xgpio_state_show, xgpio_state_store);static struct attribute * xgpio_attrs[] = {&dev_attr_state.attr,NULL};static const struct attribute_group xgpio_group = {.attrs = xgpio_attrs,};static int xgpio_probe(struct platform_device * pdev){struct device_node * node = pdev->dev.of_node;struct xgpio_device_t * xdev;enum of_gpio_flags flags;int gpio;if(!node)return -ENODEV;gpio = of_get_named_gpio_flags(node, "gpio", 0, &flags);if(!gpio_is_valid(gpio)){printk("xgpio: invalid gpio %d\n", gpio);return -EINVAL;}if(devm_gpio_request(&pdev->dev, gpio, "xgpio-pin") != 0){printk("xgpio: can not request gpio %d\n", gpio);return -EINVAL;}xdev = devm_kzalloc(&pdev->dev, sizeof(struct xgpio_device_t), GFP_KERNEL);if(!xdev)return -ENOMEM;xdev->gpio = gpio;printk("xgpio: pptv gpiosis======== %d\n", gpio);xdev->dev = &pdev->dev;dev_set_drvdata(&pdev->dev, xdev);return sysfs_create_group(&pdev->dev.kobj, &xgpio_group);}static int xgpio_remove(struct platform_device *pdev){struct xgpio_device_t * xdev = dev_get_drvdata(&pdev->dev);devm_gpio_free(&pdev->dev, xdev->gpio);sysfs_remove_group(&pdev->dev.kobj, &xgpio_group);return 0;}#ifdef CONFIG_PMstatic int xgpio_suspend(struct device *dev){return 0;}static int xgpio_resume(struct device *dev){return 0;}#else#define xgpio_suspend NULL#define xgpio_resume NULL#endifstatic const struct dev_pm_ops xgpio_pm_ops = {.suspend = xgpio_suspend,.resume = xgpio_resume,};static struct of_device_id xgpio_of_match[] = {{ .compatible = "9tripod,beep" },{},};MODULE_DEVICE_TABLE(of, xgpio_of_match);static struct platform_driver xgpio_driver = {.driver= {.name= "xgpio",.owner= THIS_MODULE,.pm= &xgpio_pm_ops,.of_match_table= of_match_ptr(xgpio_of_match),},.probe= xgpio_probe,.remove= xgpio_remove,};module_platform_driver(xgpio_driver);MODULE_DESCRIPTION("9tripod xgpio driver");MODULE_AUTHOR("Jianjun Jiang, 8192542@qq.com");MODULE_LICENSE("GPL");

正常启动后在led控制的路径
/sys/devices/xgpio_beep.21/state
通过echo 1 > /sys/devices/xgpio_beep.21/state
打开led灯
echo 0 > /sys/devices/xgpio_beep.21/state
关闭led灯
APP 应用相关代码

public class MainActivity extends Activity {        private static final String TAG = "LedServiceTest";        private LedManager mLedManager = null;        private Button mLed01OnBtn = null;        private Button mLed01OffBtn = null;        @Override        protected void onCreate(Bundle savedInstanceState) {                super.onCreate(savedInstanceState);                setContentView(R.layout.activity_main);                mLedManager = (LedManager) getSystemService(Context.LED_SERVICE);                if(mLedManager == null){                        Log.e("pptv", "mLedManager is null--- !");                }                mLed01OnBtn = (Button) findViewById(R.id.btnLed01On);                mLed01OffBtn = (Button) findViewById(R.id.btnLed01Off);                mLed01OnBtn.setOnClickListener(new Button.OnClickListener(){                        @Override                        public void onClick(View arg0) {                                // TODO Auto-generated method stub                                Log.e("pptv", "Call ledOn() start--- !");                                mLedManager.ledCtrl(1,1);                                Log.e("pptv", "Call ledOn() start !");                        }                });

首先获取LED_SERVICE,添加 LED_SERVICE在
frameworks/base/core/java/android/content/Context.java

 public static final String LED_SERVICE = "led";

如果把LED_SERVICE添加到systemserver成功的话,系统启动完成可以通过 adb shell service list 可以看到 led service

57      input_method: [com.android.internal.view.IInputMethodManager]58      bluetooth_manager: [android.bluetooth.IBluetoothManager]59      input: [android.hardware.input.IInputManager]60      window: [android.view.IWindowManager]61      alarm: [android.app.IAlarmManager]62      consumer_ir: [android.hardware.IConsumerIrService]63      led: [android.os.ILedService]64      vibrator: [android.os.IVibratorService]65      content: [android.content.IContentService]

下面介绍把LedManager添加到系统服务的步骤,
第一步,完成LedManager.java 路径在
frameworks/base/core/java/android/os/LedManager.java

package android.os;import android.os.RemoteException;import android.util.Log;import  android.os.ILedService;import android.content.Context;import android.content.pm.PackageManager;/** * Wrapper class for LedService; */public class LedManager{        private static final String TAG = "LedManager";        private final ILedService mLedService;         private Context mContext;        public LedManager(Context context, ILedService service) {                mContext = context;                mLedService = service;        }        public int ledCtrl(int which, int status) {                try{                        Log.e("pptv", "LedManager  start--- !");                        return mLedService.ledCtrl(which, status);                //      Log.e("pptv", "LedManager  ledCtrl--- !");                } catch (RemoteException e) {                        Log.e("pptv", "LedManager  error--- !");                        e.printStackTrace();                        return -1;                }        }}~   

frameworks/base/Android.mk

     core/java/android/os/IVibratorService.aidl \        core/java/android/os/ILedService.aidl \        core/java/android/os/LedManager.java  \

frameworks/base/core/java/android/app/ContextImpl.java

       import android.os.LedManager;       、、、、、、、、、、、、           registerService(LED_SERVICE, new ServiceFetcher() {                public Object createService(ContextImpl ctx) {                    IBinder b = ServiceManager.getService(LED_SERVICE);                    return new LedManager(ctx, ILedService.Stub.asInterface(b));                }});

这样就可以获取LedManager的服务了。LedManager只是对LedService做了一层封装。真正的工作都是由service 完成

frameworks/base/services/core/java/com/android/server/LedService.java
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 access hardware */        public int ledCtrl(int which, int status) throws android.os.RemoteException        {                return native_ledCtrl(which, status);        }        public LedService() {                native_ledOpen();        }        public static native int native_ledOpen();        public static native void native_ledClose();        public static native int native_ledCtrl(int which, int status);}

ILedServiceaidl 路径在
frameworks/base/core/java/android/os/ILedService.aidl

package android.os;/** {@hide} */interface ILedService{        int ledCtrl(int which, int status);}                                            

frameworks/base/Android.mk

 core/java/android/os/ILedService.aidl \

LedManager 之所以能通过 ServiceManager.getService(LED_SERVICE) 获取到LedService ,需要把LedService 添加到SystemServer系统服务中
frameworks/base/services/java/com/android/server/SystemServer.java

  Slog.i(TAG, "Led Service");            ServiceManager.addService("led", new LedService());

LedService 的相关函数具体是由jni实现的
frameworks/base/services/core/jni/com_android_server_LedService.cpp

#define LOG_TAG "pptv"#include "jni.h"#include "JNIHelp.h"#include "android_runtime/AndroidRuntime.h"#include #include #include #include #include #include #include #include #include namespace android{static led_device_t* led_device;jint ledOpen(JNIEnv *env, jobject cls){jint err;    hw_module_t* module;hw_device_t* device;ALOGE("pptv,native ledOpen ...");/* 1. hw_get_module */    err = hw_get_module("led", (hw_module_t const**)&module);    if (err == 0) {/* 2. get device : module->methods->open */    err = module->methods->open(module, NULL, &device);    if (err == 0) {/* 3. call led_open */        led_device = (led_device_t *)device;return led_device->led_open(led_device);    } else {ALOGE("pptv,open failed ...");        return -1;    }    }else{ALOGE("pptv,get_module failed ...");   }    return -1;}void ledClose(JNIEnv *env, jobject cls){//ALOGI("native ledClose ...");//close(fd);}jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status){ALOGE("pptv native ledCtrl %d, %d", which, status);return led_device->led_ctrl(led_device, which, status);}static const JNINativeMethod methods[] = {{"native_ledOpen", "()I", (void *)ledOpen},{"native_ledClose", "()V", (void *)ledClose},{"native_ledCtrl", "(II)I", (void *)ledCtrl},};int register_android_server_LedService(JNIEnv *env){    return jniRegisterNativeMethods(env, "com/android/server/LedService",            methods, NELEM(methods));}}

frameworks/base/core/jni/onload.cpp

int register_android_server_Watchdog(JNIEnv* env);int register_android_server_LedService(JNIEnv *env);'''''''    register_android_server_VibratorService(env);    register_android_server_LedService(env);

最后别忘了frameworks/base/services/core/jni/Android.mk
添加

   $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \    $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \    $(LOCAL_REL_DIR)/com_android_server_LedService.cpp \

hardware/libhardware/modules/led
有三个文件是led的hal代码
Android.mk

# Copyright (C) 2012 The Android Open Source Project## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at##      http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := led.default# HAL module implementation stored in# hw/.default.soLOCAL_MODULE_RELATIVE_PATH := hwLOCAL_C_INCLUDES := hardware/libhardwareLOCAL_SRC_FILES := led_hal.cLOCAL_SHARED_LIBRARIES := liblogLOCAL_MODULE_TAGS := enginclude $(BUILD_SHARED_LIBRARY)

led_hal.h

#ifndef ANDROID_LED_INTERFACE_H#define ANDROID_LED_INTERFACE_H#include #include #include #include __BEGIN_DECLSstruct led_device_t {    struct hw_device_t common;        int (*led_open)(struct led_device_t* dev);        int (*led_ctrl)(struct led_device_t* dev, int which, int status);};__END_DECLS#endif  // ANDROID_LED_INTERFACE_H

led_hal.c

#define LOG_TAG "pptv"/* 1. ʵΪHMI_module_t//* 2. ʵen, d_device_t//* 3. ʵd_device_t//*  hardware\libhardware\modules\vibrator\vibrator.c */#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //FILE *fd;int errNum = 0;static int fd;/** Close this device */static int led_close(struct hw_device_t* device){        close(fd);        return 0;}static int led_open(struct led_device_t* dev){        ALOGE(" UID\t= %d\n", getuid());        ALOGE(" EUID\t= %d\n", geteuid());        ALOGE(" GID\t= %d\n", getgid());        ALOGE(" EGID\t= %d\n", getegid());//      fd =  fopen("/sys/devices/xgpio_beep.29/state", "w");        fd = open("/sys/devices/xgpio_beep.29/state", O_RDWR);        //if(fd==NULL){        if(fd== -1){                errNum = errno;                ALOGE("open fail errno = %d reason = %s \n", errNum, strerror(errno));                ALOGI("led_hal led_open failed!!!----");                return 0;        }else{                ALOGI("led_hal led_open sucess!!!");                return -1;        }}static int led_ctrl(struct led_device_t* dev, int which, int status){        //int ret = fprintf(fd, "%s","1");        //int ret = ioctl(fd, status, which);        int ret = write(fd,"1",1);        ALOGE("led_hal led_ctrl : %d, %d, %d", which, status, ret);        return ret;}static struct led_device_t led_dev = {        .common = {                .tag   = HARDWARE_DEVICE_TAG,                .close = led_close,        },        .led_open  = led_open,        .led_ctrl  = led_ctrl,};static int led_device_open(const struct hw_module_t* module, const char* id,        struct hw_device_t** device){        *device = &led_dev;        return 0;}static struct hw_module_methods_t led_module_methods = {    .open = led_device_open,};struct hw_module_t HAL_MODULE_INFO_SYM = {        .tag = HARDWARE_MODULE_TAG,        .id = "led",        .methods = &led_module_methods,};

从Android 5.0 开始由于seLinux权限控制,直接控制sys或者dev设备驱动文件会报错E/SELinux ( 154): avc: denied { add } for service=led scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager
所以设置app访问led方法如下:

1、设置设备节点的上下文
device/rockchip/common/sepolicy/file_contexts:

/sys/devices/xgpio_beep.29/state  u:object_r:led_device:s0

2、声明设备类型
device/rockchip/common/sepolicy/file.te

type led_device, fs_type,sysfs_type;

3、申请system_server服务访问led设备节点权限
device/rockchip/common/sepolicy/system_server.te

添加allow system_server led_device:file rw_file_perms;

这里设置权限仅仅能让system_server能访问
/sys/devices/xgpio_beep.21/state
如果想让app不经过LedManager直接访问Ledservice,可以在文件
device/rockchip/common/sepolicy/system_app.te
添加

allow system_app led_device:file rw_file_perms;

4、在seLinux注册我们的led服务

led    u:object_r:system_server_service:s0

如果不注册,运行的时候会报

ServiceManager(  232): add_service('led_device,45) uid=1000 - PERMISSION DENIED  

做完这些你运行还会发现

open fail errno = 13 reason = Permission denied

这个我查了很久才解决,开始因为是selinux 权限赋值错误导致,后面经过仔细查找 是因为忘记修改/sys/devices/xgpio_beep.21/state权限导致
默认情况下 /sys/devices/xgpio_beep.21/state 是root 权限 并且是0755,
而我们ledservice 是system权限。没有正常写权限。
5、修改init.rc 添加

chmod 0770 /sys/devices/xgpio_beep.29chown system system /sys/devices/xgpio_beep.29

一定要是0770权限 如果是0660权限还是报Permission denied
相关代码
两篇很有用的博客
https://blog.csdn.net/u014767700/article/details/52996552
https://www.jianshu.com/p/9da8cdb4e684
https://blog.csdn.net/kongbaidepao/article/details/63254666
https://blog.csdn.net/LEAD_SOLO/article/details/53418117
https://blog.csdn.net/fengyuwuzu0519/article/details/73864567

更多相关文章

  1. Android实现截图分享qq 微信功能
  2. Android使用第三方SDK——百度地图
  3. Android(安卓)如何自定义共享库
  4. android音频系统音量控制探讨
  5. Android使用本地svg及不显示问题解决
  6. Android(安卓)绑定Service 实现android控制service的生命周期
  7. Android(安卓)小项目之--SQLite 使用法门
  8. Android(安卓)TextView 文字长度控制
  9. Android(安卓)调用相机拍照,适配到Android(安卓)10?

随机推荐

  1. Android(安卓)应用程序签名、发布
  2. Android键盘系统
  3. Android(安卓)ui基础——gravity 与 layo
  4. android EditText中的inputType
  5. Android(安卓)inputType ,软键盘输入类型
  6. Android中各种JAVA包的功能描述
  7. Android中Input型输入设备驱动原理分析
  8. Android,谁动了我的内存(转)
  9. Android安全机制
  10. Android中数据存储----SQLite数据库