Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android應用程序訪問linux驅動第三步:實現並向系統注冊Service

android應用程序訪問linux驅動第三步:實現並向系統注冊Service

編輯:關於Android編程

前面兩篇博客記錄了實現Linux驅動和使用HAL層訪問Linux驅動的代碼,我們分別對這兩部分做了測試,他們都正常工作。有了前面的基礎,我們就可以實現service層了,我們想系統注冊我們自己的service,在service中訪問HAL層,在HAL層中訪問linux驅動…當然,我們要在應用程序中訪問service,這要留到下一節來實現。
應用程序訪問service設計到了進程間的通信,這要求我們使用(Android Interface definition language)來描述我們的service提供的接口,然後應用程序就可以通過binder來訪問service 了。所以,我們先從aidl開始,逐步搭建我們的service層。

一.aidl

首先在frameworks/base/core/java/android/os/目錄下新建HelloTestService.aidl文件,用來描述我們的service提供的接口:

package android.os;  

interface IHelloTestService {  
    int wirteString(String str);  
    String readString();  
}  

我們的service只提供兩個方法,一個寫字符串,另一個讀字符串。寫好aidl文件後,執行mmm framework/base進行編譯,編譯後會在out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/目錄下生成IHelloTestService.java文件。
IHelloTestService.java文件中實現了使用binder通信的一些方法,同時還包含了我們在接口中定義的兩個方法。接下來,我們需要實現這兩個方法。

二.HelloTestService.java

在framework/base/service/core/java/com/android/server/下新建HelloTestService.java文件,也就是創建HelloTestService類,這個類繼承了IHelloTestService中的Stub內部類,我們需要復寫其中的我們在aidl中定義的兩個方法。

package com.android.server;  
import android.content.Context;  
import android.os.IHelloTestService;  
import android.util.Slog;  
public class HelloTestService extends IHelloTestService.Stub {  
    private static final String TAG = "HelloTestService";  
    HelloTestService() {  
        init_native();  
    }    
    public int wirteString(java.lang.String str){
    return wirteString_native(str);
    }
    public java.lang.String readString() {  
       return readString_native();  
    }  

    private static native boolean init_native();  
    private static native int wirteString_native(String str);  
    private static native String readString_native();  
}; 

這個類中是java中的類,java是不能直接訪問C/C++代碼的,必須使用jni來實現。因此,我們在這裡簡單調用jni中對應的方法即可。

三.實現jni訪問HAL

在java對應native代碼中,就可以使用C/C++來訪問C/C++代碼了,回想下我們在上節測試hal層代碼時寫的測試代碼,這部分代碼將與之類似,只不過,因為這部分代碼需要供java層調用,所以它需要遵尋固定的格式。
jni層代碼如下:

#define LOG_TAG "HelloTestService"

#include "JNIHelp.h"
#include "jni.h"
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
namespace android  
{  
    //jstring to char*
    char* jstringTostring(JNIEnv* env, jstring jstr);
    //char* to jstring
    jstring stoJstring(JNIEnv* env, const char* pat);


    /*在硬件抽象層中定義的硬件訪問結構體,參考*/  
        struct hellotest_device_t* device = NULL;  
    /*通過硬件抽象層定義的硬件訪問接口設置硬件寄存器val的值*/  
        static jstring hellotest_readString(JNIEnv* env, jobject clazz) {  
        if(!device) {  
            ALOGI("HelloTest JNI: device is not open.");  
            return NULL;  
        }  
        char read_str[10];
        device->read_string(device, (char **)&read_str);  
      ALOGI("HelloTest JNI: read string %s from hellotest device.", read_str); 
      return stoJstring(env,read_str);
    }  
        /*通過硬件抽象層定義的硬件訪問接口讀取硬件寄存器val的值*/  
    static jint hellotest_writeString(JNIEnv* env, jobject clazz,jstring str) {  
        if(!device) {  
            ALOGI("HelloTest JNI: device is not open.");  
            return -1;  
        }  
     char * local_str = jstringTostring(env,str);
        device->write_string(device, local_str);  

        ALOGI("HelloTest JNI: write string %s to hellotest device.", local_str); 
     return sizeof(local_str);
    }  
        /*通過硬件抽象層定義的硬件模塊打開接口打開硬件設備*/  
    static inline int hellotest_device_open(const hw_module_t* module, struct hellotest_device_t** device) {  
        return module->methods->open(module, HELLOTEST_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
    }  
        /*通過硬件模塊ID來加載指定的硬件抽象層模塊並打開硬件*/  
    static jboolean hellotest_init(JNIEnv* env, jclass clazz) {  
        hellotest_module_t* module;  

        ALOGI("HelloTest JNI: initializing......");  
        if(hw_get_module(HELLOTEST_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {  
            ALOGI("HelloTest JNI: hello Stub found.");  
            if(hellotest_device_open(&(module->common), &device) == 0) {  
                ALOGI("HelloTest JNI: hello device is open.");  
                return 0;  
            }  
            ALOGI("HelloTest JNI: failed to open hello device.");  
            return -1;  
        }  
        ALOGI("HelloTest JNI: failed to get hello stub module.");  
        return -1;        
    }  
        /*JNI方法表*/  
    static const JNINativeMethod method_table[] = {  
        {"init_native", "()Z", (void*)hellotest_init},  
        {"readString_native", "()Ljava/lang/String;", (void*)hellotest_readString},  
        {"wirteString_native", "(Ljava/lang/String;)I", (void*)hellotest_writeString},  
    };  
        /*注冊JNI方法*/  
    int register_android_server_HelloTestService(JNIEnv *env) {
             ALOGI("SystemServer :register_android_server_HelloTestService.");  
         ALOGI("SystemServer :register_android_server_HelloTestService.");  
         ALOGI("SystemServer :register_android_server_HelloTestService.");  
         ALOGI("SystemServer :register_android_server_HelloTestService.");  
         ALOGI("SystemServer :register_android_server_HelloTestService.");  
            return jniRegisterNativeMethods(env, "com/android/server/HelloTestService", method_table, NELEM(method_table));  
    }  

    //jstring to char*
char* jstringTostring(JNIEnv* env, jstring jstr)
{
       char* rtn = NULL;
       jclass clsstring = env->FindClass("java/lang/String");
       jstring strencode = env->NewStringUTF("utf-8");
       jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
       jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
       jsize alen = env->GetArrayLength(barr);
       jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
       if (alen > 0)
       {
                 rtn = (char*)malloc(alen + 1);
                 memcpy(rtn, ba, alen);
                 rtn[alen] = 0;
       }
       env->ReleaseByteArrayElements(barr, ba, 0);
       return rtn;
}

//char* to jstring
jstring stoJstring(JNIEnv* env, const char* pat)
{
       jclass strClass = env->FindClass("java/lang/String");
       jmethodID ctorID = env->GetMethodID(strClass, "", "([BLjava/lang/String;)V");
       jbyteArray bytes = env->NewByteArray(strlen(pat));
       env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
       jstring encoding = env->NewStringUTF("utf-8");
       return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
}

};  

這部分代碼中,我們把java層傳下來的字符串轉換為c++的char *字符數組,然後通過HAL層把它寫入linux驅動。然後把從linux驅動中讀出來的字符串轉換為java層的字符串並返回給java層。

3.1編譯

修改當前目錄下的Android.mk:
添加一行:

$(LOCAL_REL_DIR)/com_android_server_HelloTestService.cpp \

這樣就可以編譯我們的native代碼了,此時可以嘗試編譯並修改錯誤。

3.2向HelloTestService中注冊本地方法

我們先要在onload.cpp中添加聲明:

int register_android_server_tv_TvInputHal(JNIEnv* env);
int register_android_server_PersistentDataBlockService(JNIEnv* env);
int register_android_server_Watchdog(JNIEnv* env);
int register_android_server_HelloTestService(JNIEnv* env);

然後再onload.cpp中調用register_android_server_HelloTestService方法向java層的HelloTestService類注冊我們的native方法:

    register_android_server_tv_TvInputHal(env);
    register_android_server_PersistentDataBlockService(env);
    register_android_server_Watchdog(env);
    register_android_server_HelloTestService(env);

至此,本地的代碼會在onload.cpp中完成向java層對應類的注冊,同時我們可以編譯我們的service,使用mmm framework/base/service即可。我們已經完成了service層代碼的編寫,但系統還不會使用我們的service,所以,接下來我們要向系統注冊我們的服務。

四.向系統注冊我們的java服務

系統在啟動的時候,會使用SystemServer類啟動或者注冊系統服務,所以我們要在其中注冊我們的服務。

frameworks/base/services/java/com/android/server/SystemServer.java中的main函數中有:

    public static void main(String[] args) {
        new SystemServer().run();
    }

可見它調用了run方法,run方法中又調用了如下啟動注冊系統服務:

        // Start services.
        try {
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        }

我們在startOtherServices方法的最後注冊我們的服務就好了,使用ServiceManager.addService即可:

   try {

        Slog.i("jinwei", "Hello Test Service");
        ServiceManager.addService("hellotest", new HelloTestService());

   } catch (Throwable e) {
         Slog.e(TAG, "Failure starting Hello Test Service", e);
   }

然後再執行mmm frameworks/base/services/ 進行編譯。編譯完成後,使用make snod命令重新打包system.img,然後重寫燒寫system.img,這樣,系統中就有了我們的服務了。然而事情往往沒有想想中順利,我們需要解決一些問題。

五.解決問題

前面貼出來的代碼已經是經過測試的代碼,可以正常使用,這裡還是把我在實現service的過程中遇到的問題總結一下。
注意:我們在前面的文章中測試linux驅動和HAL層代碼的時候都是動態安裝linux驅動的,這裡因為我們在android啟動的時候就注冊我們的service,所以,驅動必須編譯進linux kernel。而且,我們在android應用程序訪問Linux驅動第二步-實現並測試hardware層
中編譯出來的hellotest.default.so文件必須拷貝到/system/lib/hw目錄下。

5.1系統無法成功啟動

重新燒寫完系統後發現系統無法成功啟動,Log如下:

pid: 6273, tid: 6273, name: system_server  >>> system_server <<<
12-31 18:01:02.461  1142  1142 F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
12-31 18:01:02.500  1142  1142 F DEBUG   : Abort message: 'art/runtime/jni_internal.cc:497] JNI FatalError called: RegisterNatives failed for 'com/android/server/HelloTestService'; aborting...'

這個問題是由於之前代碼"(Ljava/lang/String;)I" 中遺忘了”;”造成的,加上“;”即可解決。

5.2解決devie無法打開

從開機Log可以看到,/dev/hello無法打開:

09-17 17:27:22.416  2193  2193 I HelloTestService: SystemServer :register_android_server_HelloTestService.
09-17 17:27:22.444  2193  2193 D HelloTestService: init_native
09-17 17:27:22.444  2193  2193 I HelloTestService: HelloTest JNI: initializing......
09-17 17:27:22.447  2193  2193 I HelloTestService: HelloTest JNI: hello Stub found.
09-17 17:27:22.447  2193  2193 I HelloTestService: HelloTest JNI: failed to open hello device.

這是因為我們沒有訪問/dev/hello權限問題導致的解決方法:打開Android源代碼工程目錄下,進入到system/core/rootdir目錄,裡面有一個名為ueventd.rc文件,往裡面添加一行:
/dev/hello 0666 root root

最後,我們可以在開機Log中看到

HelloTest JNI: initializing......
HelloTest JNI: hello Stub found.
HelloTest JNI: hello device is open.

等LOG,說明我們的服務已經成功完成注冊。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved