從內(nèi)核驅(qū)動到android app
該文不得作為商業(yè)用途,僅為學(xué)習(xí)積累所用,轉(zhuǎn)載請注明出處:http://blog.csdn.net/callon_h/article/details/51909169
了解android驅(qū)動框架:
1.方法1——jni調(diào)用底層驅(qū)動
在android框架中寫入c/c++直接調(diào)用底層linux驅(qū)動,并向上提供jni接口給應(yīng)用程序:
優(yōu)點(diǎn):簡單易行;
缺點(diǎn):主要在于驅(qū)動程序,由于在linux中需要遵循GPL協(xié)議,需要開源,而許多廠商的一些代碼不希望開源。
2.方法2——增加硬件抽象層
將驅(qū)動程序一分為二,一部分開源在內(nèi)核中,一部分不開源在android框架中:
led android驅(qū)動:
從這里我們將看到整個應(yīng)用框架層到底層驅(qū)動的走向。首先,無論是哪種方法,我們都需要實(shí)現(xiàn)一個linux驅(qū)動以供上層訪問led資源。
1.linux驅(qū)動:
linux的字符設(shè)備驅(qū)動編寫:
#include#include#include#include#include#include#include#includestatic?struct?cdev?dev;
static?dev_t?dev_num;
#define?GPM4CON?0x110002E0
#define?GPM4DAT?0X110002E4
#define?LED_ON?_IOW('G',0,int)
#define?LED_OFF?_IOW('G',1,int)
static?unsigned?int?*led_con?=?NULL;
static?unsigned?int?*led_dat?=?NULL;
static?struct?class?*led_class?=?NULL;
static?long?led_ioctl(struct?file?*file,?unsigned?int?cmd,?unsigned?long?arg)
{
switch(cmd)
{
case?LED_ON:
{
writel(readl(led_dat)&?~(0x1<<arg),?led_dat);
break;
}
case?LED_OFF:
{
writel(readl(led_dat)|?(0x1<<arg),?led_dat);
break;
}
default:
{
return?-EINVAL;
break;
}
}
return?0;
}
static?struct?file_operations?led_fops?=?{
.owner?=?THIS_MODULE,
.unlocked_ioctl?=?led_ioctl,
};
static?void?hw_init()//GPM4_0-3
{
//1.2.1?映射地址
led_con?=?ioremap(GPM4CON,4);
led_dat?=?ioremap(GPM4DAT,4);
//1.2.2?設(shè)置為輸出狀態(tài)
writel((readl(led_con)&?~0xffff)?|?0x1111,?led_con);
//1.2.3?設(shè)置為高電平
writel(readl(led_dat)|0xf,?led_dat);
}
static?int?led_init()
{
??//1.1?cdev字符設(shè)備初始化
//1.1.1?分配cdev結(jié)構(gòu)(靜態(tài)分配)?
//1.1.2?初始化cdev結(jié)構(gòu)
alloc_chrdev_region(&dev_num,0,1,"callon_led");
cdev_init(&dev,?&led_fops);
dev.owner?=?THIS_MODULE;
//1.1.3?注冊cdev結(jié)構(gòu)?
cdev_add(&dev,dev_num,1);
//1.2?硬件初始化
hw_init();
//1.3?創(chuàng)建設(shè)備文件
//1.3.1?創(chuàng)建類
led_class?=?class_create(THIS_MODULE,"callon_led");
//1.3.2?創(chuàng)建設(shè)備
device_create(led_class,NULL,dev_num,NULL,"%s","callon_led");
printk("init?led?device?is?OK!n");
return?0;
}
static?void?led_exit()
{
device_destroy(led_class,dev_num);
class_destroy(led_class);
iounmap(led_dat);
iounmap(led_con);
cdev_del(&dev);
unregister_chrdev_region(dev_num,1);
}
module_init(led_init);
module_exit(led_exit);2 實(shí)現(xiàn)第一種方法:
首先,說明一下為什么使用jni:
1.基于對代碼的保護(hù),java相對容易被反編譯,而c/c++庫反匯編難度較大;
2.可以方便地使用現(xiàn)存的開源庫;
3.提高執(zhí)行效率;
4.java在某些文件操作方面,找不到相關(guān)的API,如此處的ioctl.
其次,為什么使用ndk?
ndk提供了一系列的工具,能夠幫助開發(fā)者快速開發(fā)c/c++的動態(tài)庫,并能自動將.so動態(tài)庫和java一起打包成apk.
第一種方法的設(shè)計(jì)思路如下所示:
在第一步中想要使用javah命令自動產(chǎn)生頭文件需要先編寫app程序,但是此時的app程序并不需要很完善,只需要提出一個你想要的native接口就好(但是你的android程序必須要Rebuild正確才會正確產(chǎn)生頭文件):
package?com.led.ndk.example.callon.ndk_led;
import?android.app.Activity;
import?android.os.Bundle;
import?android.view.View;
import?android.widget.CheckBox;
public?class?MainActivity?extends?Activity?{
????private?CheckBox[]?Led?=?new?CheckBox[4];
????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(R.layout.activity_main);
????????Led[0]?=?(CheckBox)findViewById(R.id.checkbox_led1);
????????Led[1]?=?(CheckBox)findViewById(R.id.checkbox_led2);
????????Led[2]?=?(CheckBox)findViewById(R.id.checkbox_led3);
????????Led[3]?=?(CheckBox)findViewById(R.id.checkbox_led4);
????}
????private?void?SendCmd(View?view)
????{
????????for(int?i?=1;?i<5;?i++)
????????{
????????????if(Led[i].isChecked())
????????????{
????????????????cmdLeds(1,?i);
????????????}
????????????else
????????????{
????????????????cmdLeds(0,?i);
????????????}
????????}
????}
????public?native?void?cmdLeds(int?cmd,?int?arg);
????
}
根據(jù)如上簡單的app,使用命令:
javah -d jni -classpath 你的sdk目錄/platforms/你的平臺名/android.jar:你的應(yīng)用程序/app/build/intermediates/classes/debug/ 你的包名.主類名
如:
javah?-d?jni?-classpath?/opt/AndroidSDK/platforms/android-23/android.jar:/home/callon/Downloads/callon_ndk_led/ndk_led/app/build/intermediates/classes/debug/?com.led.ndk.example.callon.ndk_led.MainActivity
此時自動產(chǎn)生出jni文件夾,其中包含頭文件com_led_ndk_example_callon_ndk_led_MainActivity.h,頭文件比較重要的:
JNIEXPORT?void?JNICALL?Java_com_led_ndk_example_callon_ndk_1led_MainActivity_cmdLeds ??(JNIEnv?*,?jobject,?jint,?jint);
這也就是我們編寫源文件需要實(shí)現(xiàn)的方法名,下面直接編寫源文件:
#include?"com_led_ndk_example_callon_ndk_led_MainActivity.h"
#include#include#include#include#include#include#include#define?LED_ON?_IOW('G',0,int)
#define?LED_OFF?_IOW('G',1,int)
JNIEXPORT?void?JNICALL?Java_com_led_ndk_example_callon_ndk_1led_MainActivity_cmdLeds
??(JNIEnv?*?env,?jobject?thiz,?jint?cmd,?jint?arg)
{
int?fd;
int?temp_cmd;
fd?=?open("/dev/callon_led",O_WRONLY);
if(cmd?==?1)
temp_cmd?=?LED_ON;
else
temp_cmd?=?LED_OFF;
ioctl(fd,?temp_cmd,?arg);
close(fd);
??
}
就是一些對內(nèi)核驅(qū)動文件的操作,然后編寫Android.mk即makefile:
LOCAL_PATH?:=?$(call?my-dir) include?$(CLEAR_VARS) LOCAL_MODULE?:=?callon_ndk_led LOCAL_SRC_FILES?:=?ndk_led.c include?$(BUILD_SHARED_LIBRARY)
此時,在你的jni文件夾應(yīng)該有這三個文件了,退到j(luò)ni文件夾之外,使用命令ndk-build即可:
callon@ubuntu:~/Downloads/callon_ndk_led/jni$?ls Android.mk??com_led_ndk_example_callon_ndk_led_MainActivity.h??ndk_led.c callon@ubuntu:~/Downloads/callon_ndk_led/jni$?cd?.. callon@ubuntu:~/Downloads/callon_ndk_led$?ndk-build? [armeabi]?Compile?thumb??:?callon_ndk_led??libs/armeabi/libcallon_ndk_led.so callon@ubuntu:~/Downloads/callon_ndk_led$
最后一步,完善app:
首先,用Project形式來看我們的app,并在app->src->main下創(chuàng)建一個目錄"jniLibs";
然后,將我們.so所在的armeabi目錄拷貝到j(luò)niLibs目錄下;
最后在我們的app代碼的最后加上:
static
????{
????????System.loadLibrary("callon_ndk_led");
????}然后Rebuild Project即可!
3 實(shí)現(xiàn)第二種方法:
1.HAL程序編寫:
首先在 ?安卓源代碼根目錄/hardware/libhardware/modules/下創(chuàng)建自己的hal代碼存放路徑,如led。
最終編寫的文件為:安卓源代碼根目錄/hardware/libhardware/modules/led/led_hal.c和Android.mk,安卓源代碼根目錄/hardware/libhardware/include/hardware/led.h。
#include#include#include#include#include#include#include#include#include#include#define?LOG_TAG?"callon_led"
static?int?fd;
static?int?led_close(struct?hw_device_t?*device)
{
struct?led_device_t*?led?=?(struct?led_device_t*)device;
free(led);
close(fd);
return?0;
}
int?led_on(struct?led_device_t*?dev,int?arg)
{
ioctl(fd,LED_ON,arg);
return?0;
}
int?led_off(struct?led_device_t*?dev,int?arg)
{
ioctl(fd,LED_OFF,arg);
return?0;
}
static?struct?led_device_t?led_dev?=?{
.led_device?=?{
.tag?=?HARDWARE_DEVICE_TAG,
.close?=?led_close,
},
.set_on?=?led_on,
.set_off?=?led_off,
};
static?int?open_led(const?struct?hw_module_t*?module,?char?const*?name,
????????struct?hw_device_t**?device)
{
*device?=?&led_dev;
fd?=?open("/dev/callon_led",O_RDWR);
if(fd?<?0)
{
ALOGD(LOG_TAG,?"open?device?fail!");
return?-1;
}
return?0;
}
static?struct?hw_module_methods_t?led_methods?=?{
????.open?=??open_led,
};
struct?hw_module_t?HAL_MODULE_INFO_SYM?=?{
????.tag?=?HARDWARE_MODULE_TAG,
????.id?=?"led",
????.methods?=?&led_methods,
};led.h
#ifndef?_HARDWARE_LED_H
#define?_HARDWARE_LED_H
#include#define?LED_ON?_IOW('G',0,int)
#define?LED_OFF?_IOW('G',1,int)
struct?led_device_t?{
struct?hw_device_t?led_device;
int?(*set_on)(struct?led_device_t*?dev,int?arg);//means?led_number
int?(*set_off)(struct?led_device_t*?dev,int?arg);
};
#endif??//?_HARDWARE_LED_HAndroid.mk
LOCAL_PATH?:=?$(call?my-dir) include?$(CLEAR_VARS) LOCAL_MODULE?:=?led.default #?HAL?module?implementation?stored?in #?hw/.default.so LOCAL_MODULE_RELATIVE_PATH?:=?hw LOCAL_C_INCLUDES?:=?hardware/libhardware LOCAL_SRC_FILES?:=?led_hal.c LOCAL_SHARED_LIBRARIES?:=?liblog LOCAL_MODULE_TAGS?:=?eng include?$(BUILD_SHARED_LIBRARY)
編譯則在 ?安卓源代碼根目錄下使用
$?.?setenv $?lunch $?mmm?hardware/libhardware/modules/led/
看到
target?SharedLib:?led.default?(out/target/product/tiny4412/obj/SHARED_LIBRARIES/led.default_intermediates/LINKED/led.default.so) target?Symbolic:?led.default?(out/target/product/tiny4412/symbols/system/lib/hw/led.default.so) Export?includes?file:?hardware/libhardware/modules/led/Android.mk?--?out/target/product/tiny4412/obj/SHARED_LIBRARIES/led.default_intermediates/export_includes target?Strip:?led.default?(out/target/product/tiny4412/obj/lib/led.default.so) Install:?out/target/product/tiny4412/system/lib/hw/led.default.so make:?Leaving?directory?`/opt/Tiny4412/Android/android-5.0.2' ####?make?completed?successfully?(1?seconds)?####
即可,最后產(chǎn)生的就在out/target/product/tiny4412/system/lib/hw/led.default.so了。
我們將system.img重做,這里通過一個腳本./gen-img.sh完成。
2.硬件服務(wù)編寫:
首先,Service Manager為了解決訪問沖突而存在的,在有Service Manager的基礎(chǔ)之上,我們的底層需要先編寫硬件服務(wù),注冊到Service Manager,我們的app才能通過Service Manager獲取到服務(wù),操作底層。
由于涉及到許多目錄操作,細(xì)化操作后為:
1.創(chuàng)建 ILedService.aidl
仿照 frameworks/base/core/java/android/os/IVibratorService.aidl?
package?android.os;
/**?{@hide}?*/
interface?ILedService
{
????int?LedOpen();
????int?LedOn(int?arg);
????int?LedOff(int?arg);
}2.修改frameworks/base/Android.mk 增加
core/java/android/os/ILedService.aidl?
3.自動生成ILedService.java
mmm frameworks/base/編譯自動生成out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java
4.創(chuàng)建 LedService.java 實(shí)現(xiàn)接口函數(shù)
仿照frameworks/base/services/core/java/com/android/server/VibratorService.java?
package?com.android.server;
import?android.util.Slog;
import?android.os.ILedService;
public?class?LedService?extends?ILedService.Stub{
????private?static?final?String?TAG?=?"LedService";
????
public?LedService()?
{
Slog.d(TAG,"LedService");
}
???? public?int?LedOpen()?throws?android.os.RemoteException
{
return?native_LedOpen();
}
???? public?int?LedOn(int?arg)?throws?android.os.RemoteException
{
return?native_LedOn(arg);
}
public?int?LedOff(int?arg)?throws?android.os.RemoteException
{
return?native_LedOff(arg);
}??????
????????
public?static?native?int?native_LedOpen();
public?static?native?int?native_LedOn(int?arg);
public?static?native?int?native_LedOff(int?arg);
}5.將服務(wù)注冊到Service Manager當(dāng)中
修改frameworks/base/services/java/com/android/server/SystemServer.java
參考vibrator修改的地方
LedService?led?=?null;
Slog.i(TAG,?"Led?Service");
????????????led?=?new?LedService();
????????????ServiceManager.addService("led",?led);
6.實(shí)現(xiàn)com_android_server_LedService.cpp
為什么需要它?因?yàn)槲覀兊膆al代碼并沒有提供jni的接口(這里提供了一種新的方法來提供native方法,之前是自動產(chǎn)生頭文件然后來實(shí)現(xiàn)的)。
根據(jù)frameworks/base/services/core/jni/com_android_server_VibratorService.cpp
/*
?*?Copyright?(C)?2009?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.
?*/
#define?LOG_TAG?"LedService"
#include?"jni.h"
#include?"JNIHelp.h"
#include?"android_runtime/AndroidRuntime.h"
#include#include#include#includestruct?led_device_t?*led_dev;
namespace?android
{
static?jint?LedOpen(JNIEnv?*env,?jobject?clazz)
{
hw_module_t?*module;
hw_device_t?*device;
hw_get_module("led",(hw_module_t?const?**)&module);
module->methods->open(module,?NULL,?&device);
led_dev?=?(struct?led_device_t*)device;
return?0;
}
static?jint?LedOn(JNIEnv?*env,?jobject?clazz,?int?arg)
{
led_dev->set_on(led_dev,arg);
return?0;
}
static?jint?LedOff(JNIEnv?*env,?jobject?clazz,?int?arg)
{
???? led_dev->set_off(led_dev,arg);
return?0;
}
static?JNINativeMethod?method_table[]?=?{
????{?"native_LedOpen",?"()I",?(void*)LedOpen?},
????{?"native_LedOn",?"(I)I",?(void*)LedOn?},
????{?"native_LedOff",?"(I)I",?(void*)LedOff}
};
int?register_android_server_LedService(JNIEnv?*env)
{
????return?jniRegisterNativeMethods(env,?"com/android/server/LedService",
????????????method_table,?NELEM(method_table));
}
};7.注冊native接口
在frameworks/base/services/core/jni/onload.cpp中添加
int?register_android_server_LedService(JNIEnv*?env);
register_android_server_LedService(env);
8.修改Android.mk
在frameworks/base/services/core/jni/Android.mk中加入自己寫的com_android_server_LedService.cpp
$(LOCAL_REL_DIR)/com_android_server_LedService.cpp?
9.mmm frameworks/base/services/編譯
Copying:?out/target/common/obj/JAVA_LIBRARIES/services_intermediates/classes.dex target?Jar:?services?(out/target/common/obj/JAVA_LIBRARIES/services_intermediates/javalib.jar) Install:?out/target/product/tiny4412/system/framework/services.jar make:?Leaving?directory?`/opt/Tiny4412/Android/android-5.0.2' ####?make?completed?successfully?(50?seconds)?####
其中碰到一陣錯誤,非常無語:
make:?Entering?directory?`/opt/Tiny4412/Android/android-5.0.2' make:?***?No?rule?to?make?target?`frameworks/base/services/core/.java',?needed?by?`out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/classes-full-debug.jar'.??Stop. make:?Leaving?directory?`/opt/Tiny4412/Android/android-5.0.2' ####?make?failed?to?build?some?targets?(1?seconds)?####
直接不往下編譯,直接報錯,非常難找錯誤,最后發(fā)現(xiàn)由于LedService.java的文件名多了個空格。出現(xiàn)這種錯誤一定要耐心,一定是某個小地方出錯的。
最后繼續(xù)使用./gen-img.sh完成system.img的編譯,燒寫進(jìn)開發(fā)板,為寫應(yīng)用程序做準(zhǔn)備。
3.應(yīng)用程序設(shè)計(jì):
package?com.led.hal.example.callon.callonhalled;
import?android.os.RemoteException;
import?android.support.v7.app.AppCompatActivity;
import?android.os.Bundle;
import?android.view.View;
import?android.widget.CheckBox;
import?android.os.ILedService;
import?android.os.ServiceManager;
public?class?MainActivity?extends?AppCompatActivity?{
????private?CheckBox[]?Led?=?new?CheckBox[4];
????private?ILedService?iLedService?=?null;
????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(R.layout.activity_main);
????????Led[0]?=?(CheckBox)findViewById(R.id.checkbox_led1);
????????Led[1]?=?(CheckBox)findViewById(R.id.checkbox_led2);
????????Led[2]?=?(CheckBox)findViewById(R.id.checkbox_led3);
????????Led[3]?=?(CheckBox)findViewById(R.id.checkbox_led4);
????????iLedService?=?ILedService.Stub.asInterface(ServiceManager.getService("led"));
????????try?{
????????????iLedService.LedOpen();
????????}catch(RemoteException?e){
????????????e.printStackTrace();
????????}
????}
????private?void?SendCmd(View?view)
????{
????????for(int?i?=1;?i<5;?i++)
????????{
????????????if(Led[i].isChecked())?{
????????????????try?{
????????????????????iLedService.LedOn(i);
????????????????}catch?(RemoteException?e){
????????????????????e.printStackTrace();
????????????????}
????????????}
????????????else?{
????????????????try?{
????????????????????iLedService.LedOff(i);
????????????????}catch?(RemoteException?e){
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}
????}
}程序其實(shí)很簡單,但是涉及到android 中隱藏類的使用,我們的ILedService和ServiceManager其實(shí)都是隱藏類,你編譯不過去的,添加相應(yīng)的jar包才可以,參考
http://blog.csdn.net/wukunting/article/details/5788196
首先拷貝out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar到工程下
然后AndroidStudio中File->Project Structure點(diǎn)擊其中的'+'->選擇Import .JAR/.AAR Package->選擇classes.jar->Finish
繼續(xù)在Project Structure下選擇app->Dependencies->選擇'+'->Module Dependency->選擇'classes'即可。
這時候整個源碼都好了,但是下載速度會很慢,改善基于frameworks設(shè)計(jì)的app下載和運(yùn)行速度的方案:
1.修改build.gradle(Module:app)
apply?plugin:?'com.android.application'
android?{
????compileSdkVersion?23
????buildToolsVersion?"23.0.2"
????defaultConfig?{
????????applicationId?"com.led.hal.example.callon.callonhalled"
????????minSdkVersion?21
????????targetSdkVersion?23
????????versionCode?1
????????versionName?"1.0"
????????multiDexEnabled?true
????}
????dexOptions?{
????????javaMaxHeapSize?"4g"
????}
????buildTypes?{
????????release?{
????????????minifyEnabled?false
????????????proguardFiles?getDefaultProguardFile('proguard-android.txt'),?'proguard-rules.pro'
????????}
????}
}
dependencies?{
????compile?fileTree(dir:?'libs',?include:?['*.jar'])
????testCompile?'junit:junit:4.12'
????compile?'com.android.support:appcompat-v7:23.2.0'
????compile?project(':classes')
????compile?'com.android.support:multidex:1.0.0'
}2.修改AndroidManifest.xml
在application下增加
android:name="android.support.multidex.MultiDexApplication"
最后下載運(yùn)行吧!





