0
点赞
收藏
分享

微信扫一扫

基于安卓5.0,添加硬件访问服务ledservice

目标:在安卓5.0里添加一个ledservice的硬件访问服务,让其他app可以通过我们的ledservice来控制led灯。

一、定义service提供给其他app的接口:.aidl文件
我们的LedService是要替其他app去控制led硬件的,所以肯定要有一个ILedService.java的文件里提供了一些接口,对于这个.java文件,我们可以提供一个aidl文件,然后用安卓的编译系统来生成。
代码如下,并将这个文件放在frameworks/base/core/java/android/hardware/目录下

package android.hardware;

/** @hide */
interface ILedService //这里接口的名字必须于文件名一致
{
    /**
     * 
     */
    int  leds_ctrl(int id, bool on);
}

修改frameworks/base/android.mk文件,将aidl文件添加到安卓的编译系统
在这里插入图片描述
在framework/base目录下使用mmm .命令编译,编译成功后,在out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/hardware/目录下就会有安卓编译系统帮我们生成的java接口文件ILedService.java
在这里插入图片描述
打开这个ILedService.java文件
在这里插入图片描述
可以看到,这个ILedService是一个interface接口,里面声明了led_ctrl方法
在这里插入图片描述
二、实现接口的实际方法
我们要写一个LedService.java文件,这个class LedService要extends ILedService.stub。覆写其中的led_ctrl方法

package com.android.hardware;
import android.hardware.ILedService;
/** @hide */
public class LedService extends ILedService.Stub
{
    public static native int native_leds_ctrl(int id, boolean on);
    public int leds_ctrl(int id, boolean on) throws android.os.RemoteException{
        return native_leds_ctrl(id, on);
    }
}

将这个文件放到frameworks/base/services/core/java/com/android/server/目录下
在这里插入图片描述
不需要修改Android.mk,因为android-5.0.2/frameworks/base/services/Android.mk文件里已经定义要把services目录下的所有java文件都编译进去了
在这里插入图片描述

在frameworks/base/services/java/com/android/server/目录下的SystemServer.java文件里的StartOtherServices()方法中添加我们的LedService!
在这里插入图片描述

在这里插入图片描述
编译:

book@book-virtual-machine:/work/android-5.0.2$ mmm frameworks/base/services/

在out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/classes/com/android/hardware目录下就会有LedService.class

三、jni文件编写
为了让LedService.java中的native 方法和jni文件里的函数直接建立映射,我们需要定义一个JNINativeMethod 结构体数组来描述java中方法名和c中函数名的对应关系,调用jniRegisterNativeMethods()函数注册,同时加载hal库,然后在jni函数里调用对应的hal层接口,控制led。
/frameworks/base/services/core/jni/com_android_server_LedService.cpp的代码如下:

#define LOG_TAG "LedService_jni"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <dlfcn.h>
#include <stdio.h>

#define HAL_PATH "/vendor/lib/libled.so"
namespace android {
int (*led_open)(void);
int (*led_ctl)(int id, int stat);
/*
 * Class:     com_android_hardware_LedService_bkp
 * Method:    native_leds_ctrl
 * Signature: (IZ)I
 */
JNIEXPORT jint JNICALL jni_leds_ctrl
  (JNIEnv *env, jclass cls, jint id, jboolean on){
	if (on)
		return led_ctl(id, 1); //调用hal层接口,控制led
	else
		return led_ctl(id, 0);
}
static JNINativeMethod method_table[] = {
    { "native_leds_ctrl", "(IZ)I", (void*)jni_leds_ctrl }
};
int register_android_server_LedService(JNIEnv *env)
{
	void *handle;
	/* 加载hal库*/	
	handle = dlopen(HAL_PATH, RTLD_LAZY);
    if(!handle){
        fprintf(stderr, "%s\n", dlerror());
        return -1;
    }

	//获得hal层接口
	led_open = (int (*)(void))dlsym(handle, "led_open");
	led_ctl = (int (*)(int id, int stat))dlsym(handle, "led_ctl");
	if (!led_open || !led_ctl) {
		ALOGI(LOG_TAG,"get hal interface failed\n");
	}
	led_open();
    return jniRegisterNativeMethods(env, "com/android/hardware/LedService",
            method_table, NELEM(method_table));
}
};

在/frameworks/base/services/core/jni/onload.cpp里注册LedService
在这里插入图片描述
修改Android.mk
在这里插入图片描述
在这里插入图片描述

编译

book@book-virtual-machine:/work/android-5.0.2/frameworks/base/services$ mmm .

四、hal层文件编写
led_hal.c代码如下:

//android系统中用于打印log的接口__android_log_print(ANDROID_LOG_INFO,...),可用logcat捕获
//需要包含android/log.h,依赖于liblog.so
#include <android/log.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define TAG "led_hal"
#define PATH "/sys/class/my_leds_cls/my_leds"

int fd;
char buf[4] = "000";
unsigned char buf_for_read[20];
int led_ctl(int id, int on) {
  int ret;
  
  int count = sizeof(buf);
  if (on)
	buf[id-1] = '1';
  else
    buf[id-1] = '0'; 
  ret = write(fd, buf, count);
  if (ret == count)
	__android_log_print(ANDROID_LOG_INFO, TAG, "led_1ctl:%d,%s\n", id, (on?"on":"off"));
  else
	__android_log_print(ANDROID_LOG_INFO, TAG, "led_1ctl:%d,%s failed\n", id, (on?"on":"off"));

	return 0;
}

int led_open(void) {

  int ret;
  fd = open(PATH, O_RDWR);
  if (fd <= 0){
	__android_log_print(ANDROID_LOG_INFO, TAG, "led_1open failed\n");
	return -1;
	}
	__android_log_print(ANDROID_LOG_INFO, TAG, "led_1open succeed, fd:%d\n", fd);
	ret = read(fd,buf_for_read,20);
	__android_log_print(ANDROID_LOG_INFO, TAG, "read str:%s,ret=%d\n", buf_for_read,ret);
  return 0;
  }


void led_close(void) {
  close(fd);
  __android_log_print(ANDROID_LOG_INFO, TAG, "led_1close succeed\n");
  }


Makefile文件如下:

C_SRC :=$(shell find . -name *.c)
OBJ :=$(patsubst %.c,%.o,$(C_SRC))
MAKE = arm-linux-gcc
TARGET = libled.so
INC =-I /work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include/
INC +=-I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
LIBS =/work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so 
LIBS +=/work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/liblog.so

all:$(OBJ) $(LIBS)
	$(MAKE) -shared -nostdlib -o $(TARGET) $^
%.o:%.c
	$(MAKE) $(INC) -fPIC -c -o $@ $<
clean:
	rm libled.so *.o
	

将编译出来的libled.so push 到开发板的/vendor/lib目录下
在这里插入图片描述
五、驱动文件编写

/*
 * 基于tiny4412开发板
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/device.h>

#define DEVICE_NAME "[my_leds]"
#define LED_NUM 3
static int led_gpios[LED_NUM] = {
	EXYNOS4212_GPM4(0),
	EXYNOS4212_GPM4(1),
	EXYNOS4212_GPM4(2)
};


static struct class* leds_cls;
char *show_str = "leds state\n";
static ssize_t my_leds_show(struct class *class, struct class_attribute *attr, char *buf)
{	int ret;
	printk("my_leds_show new stat!\n");

	ret = sprintf(buf, "%s",show_str);
	printk("copy_to_user ret:%d\n",ret);
	return ret;

}
 
static ssize_t my_leds_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count)
{
	int i;
	printk("my_leds_store:%s\n", buf);
	for(i = 0; i < LED_NUM; i++){
		if (buf[i] -'0' == 0){
			printk("my_leds_store:57\n");
			gpio_set_value(led_gpios[i], 1);
			}
		else {
			printk("my_leds_store:61\n");
			gpio_set_value(led_gpios[i], 0);
			}
		}
	return count;	
}
static CLASS_ATTR(my_leds, 0666, my_leds_show, my_leds_store);
static int __init leds_init(void) {
	int ret;
	int i;

	for (i = 0; i < LED_NUM; i++) {
		ret = gpio_request(led_gpios[i], "LED");
		if (ret) {
			printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
					led_gpios[i], ret);
			goto error;
		}

		s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
		gpio_set_value(led_gpios[i], 1);//先保持熄灭
	}
	leds_cls = class_create(THIS_MODULE, "my_leds_cls");
	ret = class_create_file(leds_cls, &class_attr_my_leds);
	if (ret) {
		printk("device_create_file:my_leds failed\n");
		goto error;
		}
		
	printk(DEVICE_NAME"\tinitialized\n");

	return ret;
error:
	if (i >= LED_NUM)
		i = 2;
	for (; i >= 0; i--) {
		gpio_free(led_gpios[i]);
	}
	return ret;
}

static void __exit leds_exit(void) {
	int i;
	printk("leds_exit!\n");
	for (i = 0; i < LED_NUM; i++) {
		gpio_free(led_gpios[i]);
	}

	class_remove_file(leds_cls, &class_attr_my_leds);
	class_destroy(leds_cls);
}
module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("SuZhenGe");




Makefile编写

KERN_DIR = /work/linux-3.0.86

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= my_leds.o


确认驱动文件可以正常编译后,修改driver/char/Makefile,将驱动编译进kernel。
重新编译出zImage

六、编写app程序来测试
用android studio创建工程,主要代码如下:
MainActivity.java

package com.example.mypc.app01_leddemo;

import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Toast;
import android.hardware.ILedService; //引入在安卓系统中添加的硬件服务的接口
import android.os.ServiceManager;

public class MainActivity extends AppCompatActivity {
    private Button button = null;
    private CheckBox cb1,cb2,cb3;
    private ILedService iLedService;
    boolean ledsAllOn = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button =(Button) findViewById(R.id.BUTTON);
        cb1 = (CheckBox)findViewById(R.id.LED1);
        cb2 = (CheckBox)findViewById(R.id.LED2);
        cb3 = (CheckBox)findViewById(R.id.LED3);
        //获得led硬件服务
        iLedService = ILedService.Stub.asInterface(ServiceManager.getService("led"));
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Perform action on click
                if (ledsAllOn) {
                    ledsAllOn = false;
                    cb1.setChecked(false);
                    cb2.setChecked(false);
                    cb3.setChecked(false);
                    //turn off all leds
                    try {
                        iLedService.leds_ctrl(1, false);
                        iLedService.leds_ctrl(2, false);
                        iLedService.leds_ctrl(3, false);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    button.setText("click to turn on all leds");
                } else {
                    ledsAllOn = true;
                    cb1.setChecked(true);
                    cb2.setChecked(true);
                    cb3.setChecked(true);
                    //turn on all leds
                    try {
                        iLedService.leds_ctrl(1, true);
                        iLedService.leds_ctrl(2, true);
                        iLedService.leds_ctrl(3, true);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    button.setText("click to  turn off all leds");
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
    protected void finalize(){

    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
    public void checkbox_click(View view) {
        CheckBox c = (CheckBox) findViewById(view.getId());
        int which_led = view.getId() - R.id.LED1 + 1;
        if (c.isChecked()) {
            try {
                iLedService.leds_ctrl(which_led, true);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            Toast.makeText(getApplicationContext(), "LED" + which_led + " on!", Toast.LENGTH_SHORT).show();
        }
        else {
            try {
                iLedService.leds_ctrl(which_led, false);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            Toast.makeText(getApplicationContext(), "LED" + which_led + " off!", Toast.LENGTH_SHORT).show();
        }
    }
}

由于ILedService是我们新添加的安卓的隐藏接口,所以需要将包含有这个接口的class.jar包放到app工程中,这样app才能够编译通过。
当我们编译ILedService…aidl成功后,会在out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/目录下生成class.jar
在这里插入图片描述
将这个class.jar文件拷贝到与app工程相关的目录下,在android studio中添加对这个文件的依赖。
在这里插入图片描述
选择class.jar所在的路径
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
参考文档:
a. 包含什么
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar

How do I build the Android SDK with hidden and internal APIs available?
http://stackoverflow.com/questions/7888191/how-do-i-build-the-android-sdk-with-hidden-and-internal-apis-available

b. 怎么包含
Creating a module library and adding it to module dependencies
https://www.jetbrains.com/idea/help/configuring-module-dependencies-and-libraries.html

此时,点击运行还是会有错误,需要修改gradle文件
在这里插入图片描述
添加以下内容,并点击sync

dexOptions {
        javaMaxHeapSize "4g"
    }

sync之后,再运行,出现新的错误
AGPBI: {“kind”:“simple”,“text”:“trouble writing output: Too many method references to fit in one dex file: 82082; max is 65536.”,“sources”:[{}]}
AGPBI: {“kind”:“simple”,“text”:“You may try using multi-dex. If multi-dex is enabled then the list of classes for the main dex list is too large.”,“sources”:[{}]}
AGPBI: {“kind”:“simple”,“text”:“References by package:”,“sources”:[{}]}

参考以下链接 https://blog.csdn.net/yanzhenjie1003/article/details/51818269?_t_t_t=0.42422462231479585&utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-0.pc_relevant_antiscanv2&spm=1001.2101.3001.4242.1&utm_relevant_index=3进行解决,主要是修改build.gradle,添加以下两行

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.3"
    dexOptions {
        javaMaxHeapSize "4g"
    }
    defaultConfig {
        applicationId "com.example.mypc.app01_leddemo"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        /// Enabling multidex support.
        multiDexEnabled true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile'com.android.support:multidex:1.0.1' //还有这一行
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
    compile project(':classes')
}

在这里插入图片描述
在这里插入图片描述
这样app就可以在板子上运行了。
补充:
在这里插入图片描述
为了避免编译出来的apk太大,可以在给app添加依赖的class时,选择provided
在这里插入图片描述

重新编译后,apk就变小了
在这里插入图片描述

举报

相关推荐

0 条评论