In-depth introduction-Android system transplantation and platform development (10)-led Hal simple design case analysis

Source: Internet
Author: User

Through the analysis of the previous two Hal frameworks and the JNI overview, we have a more detailed understanding of the stub Hal provided by Android. Let's take a look at the LED instance and write the driver to light up the LED, just like writing a program and learning a language to print helloworld, if printing helloworld is the first speaker in a new language, lighting up the LED lights is a beacon for us to learn Hal, instruct us to find the correct direction in the complicated Hal code.

Ledhal instance Architecture

Describes the framework layers of our led instances:

L leddemo. Java: the Android Application we wrote.

L ledservice. Java: it is a Java framework layer API encapsulated by LED Hal. It is mainly used to provide the framework layer API to the application layer, which belongs to the android framework layer.

L libled_runtime.so: Because Java code cannot access the Hal layer, this library is the local code part corresponding to ledservice. java.

L led. Default. So: Hal code for LED hardware

Leddemo accesses the LED device through the framework layer API provided by ledservice. ledservice is the service provider of the LED Device for the leddemo application. The ledservice runs in Dalvik and cannot directly access the LED hardware device, it can only deliver specific led operations to local code for implementation, and call libled_runtime.so, The encapsulation library of LED hardware operations through JNI, as shown in the Hal stub framework, in libled_runtime.so, first find the module of the hardware device registered as an LED, and then save its operation interface pointer to wait for the framework layer ledservice to call in the local database. LED. default. so is the Hal-layer code, which is the actual implementer of upper-layer operations. It is not a dynamic library (that is, it is not loaded and linked by any process ), it only finds the module through ldopen in the local code to find the module, and returns the function pointer encapsulated in the device operation structure corresponding to the hardware module.

The call sequence is as follows:

Led Hal instance code analysis

Let's take a look at the directory structure of the LED instance:

The main file is as follows:

Com. Hello. ledservice. cpp: it is in the frameworks/services/JNI Directory, which is the led local service code.

Led. C: Hal code

Led. h: Hal header file

Leddemo. Java: application code

Ledservice. Java: led framework layer service code

In the android source code directory, the Framework-layer service code should be stored in the frameworks/services/Java/package name/directory. The Android compilation system will compile and generate the system/framework/services. JAR file. Because our test code is customized by the vendor, try not to put it in the source code tree of frameworks. I put it together with the leddemo application, although this method is not standard at the android framework level.

In addition, the name of the local service code must match the name of the corresponding framework-layer Java code (package name + class file name, and the package directory should be replaced ). There is a corresponding Android. mk file in the source code directory. It is the guidance file of the android compilation system and used to compile the target module.

1) Android. mk File Analysis

First, let's take a look at Android. mk in the led source code:

include $(call all-subdir-makefiles)

The code is simple. It indicates that all android. mk files in the current directory are contained.

Let's take a look at Android. mk (3) in the led_app directory:

# Call the macro my-Dir, which returns the current android. path of the MK file local_path: = $ (call my-DIR) # contains the MK file build/CORE/clear_vars.mk pointed to by the clear_vars variable, it is mainly used to clear the compilation variable include $ (clear_vars) that is dependent on the compilation process # specify the tag of the current target. For more information about its function, see the previous Android compilation system Chapter local_module_tags: = user # current MK file compilation target module local_package_name: = leddemo # source code that is dependent on the Compilation target. It calls a macro "all-Java-files-under, this macro is available in build/CORE/definitions. define # In mK to search for all java files in the current directory. Return local_src_files :=$ (callall-Java-files-under, Src) to the searched java files) # specify the API level when compiling the android application, that is, the platform version of the current program. # Here, the local_sdk_version of the current source code is used: = Current # The most important is the code, which contains a file build/CORE/package. MK: compile and generate the android package file based on the preceding compilation variables, that is, the APK file include $ (build_package)

Annotations are added to the above Code. Basically, each compilation target has a declaration similar to the preceding compilation variable:

Local_module_tags

Local_package_name

Local_src_files

Because all android. MK is eventually included in the compilation system. Therefore, when compiling each target module, you must specify the directory of the current target through local_path :=$ (call my-DIR, then, call include $ (clear_vars) to clear the important compilation variables on which the compilation system depends and generate new compilation variables.

Let's take a look at the source code corresponding to the leddemo target.

2) leddemo code analysis

Those who have learned about Android applications are familiar with the directory structure. The leddemo source code is under the src directory.

@ Led_app/src/COM/farsight/leddemo. Java:

package com.hello; import com.hello.LedService; import com.hello.R; importandroid.app.Activity; importandroid.os.Bundle; importandroid.util.Log; importandroid.view.View; import android.view.View.OnClickListener; importandroid.widget.Button;  public classLedDemo extends Activity {     privateLedService led_svc;     private Buttonbtn;     private booleaniflag = false;     private Stringtitle;      /** Calledwhen the activity is first created. */     @Override     public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        Log.i("Java App", "OnCreate");         led_svc =new LedService();         btn =(Button) this.findViewById(R.id.Button01);        this.btn.setOnClickListener(new OnClickListener() {            public void onClick(View v) {                Log.i("Java App", "btnOnClicked");                if (iflag) {                    title = led_svc.set_off();                    btn.setText("Turn On");                    setTitle(title);                    iflag = false;                } else {                    title = led_svc.set_on();                    btn.setText("Turn Off");                    setTitle(title);                    iflag = true;                }             }         });     } }

The code is very simple. There is a button on the activity. When the activity is initialized, The ledservice object is created. When the button is pressed, The ledservice object calls its methods set_on () and set_off ().

 

3) ledservice code analysis

Let's take a look at the ledservice code:

@ Led_app/src/COM/farsight/ledservice. Java:

Package COM. hello; import android. util. log; public class ledservice {/** loadnative service. */static {// static initialization language block, which is executed only once when the class is loaded. It is usually used to load the library log. I ("Java service", "load native Serivce lib"); system. loadlibrary ("led_runtime");} // constructor public ledservice () {int icount; log. I ("Java service", "Do init native call"); _ Init (); icount = _ get_count (); log. D ("Java service", "led COUNT =" + icount); log. D ("Java service", "init OK");}/** led nativemethods. */Public stringset_on () {log. I ("com. hello. ledservice "," led on "); _ set_on (); Return" led on ";} Public String set_off () {log. I ("com. hello. ledservice "," led off "); _ set_off (); Return" led off ";}/** declare all the native interface. */Private Static native Boolean _ Init (); Private Static native int _ set_on (); Private Static native int _ set_off (); Private Static native int _ get_count ();}

By analyzing the code above, we can see the work of ledservice:

L load the local service library code

L call the local code _ init In the constructor to initialize the LED and call get_count to obtain the number of LED lights.

L provides two APIs for the leddemo application: set_on and set_off. These two APIs are actually handed over to the local service code for operation.

Because Java code cannot directly operate on the underlying hardware, the specific operation is handed over to the local underlying code for implementation through the JNI method. It is only an API provider, that is, a service provider.

Let's go to the underlying local code and look at the Android. mk file of the underlying code first:

@ Frameworks/Android. mk:

Local_path: = $ (call my-DIR) include $ (clear_vars) local_module_tags: = englocal_module: = libled_runtime # compile the target module local_src_files: = \ Services/JNI/export local_shared_libraries: ==# dynamic library libandroid_runtime \ libnativehelper \ libcutils \ libutils \ libhardware local_c_includes + =\# header file directory $ (jni_h_include) local_prelink_module: = false # This target is not a pre-linked module include $ (build_shared_library) # compile and generate a shared dynamic library

Combining the Android. mk file analyzed earlier, it is not difficult to understand this MK file. The previous MK file is compiled into the android APK file, which is compiled into the so shared library. Therefore, local_module and include $ (build_shared_library) are different from the previous MK file. for more information about the variables in the MK file, see the android compilation system section.

All in all, the goal of local code compilation is to generate the libled_runtime.so file.

 

4) led local service code analysis

Let's take a look at the source code of the local service:

@ Frameworks/services/JNI/com_hello_ledservice.cpp:

#define LOG_TAG "LedService"#include "utils/Log.h"#include <stdlib.h>#include <string.h>#include <unistd.h>#include <assert.h>#include <jni.h>#include "../../../hardware/led.h"static led_control_device_t *sLedDevice = 0;static led_module_t* sLedModule=0; static jint get_count(void){    LOGI("%sE", __func__);   if(sLedDevice)        returnsLedDevice->get_led_count(sLedDevice);    else       LOGI("sLedDevice is null");    return 0;}static jint led_setOn(JNIEnv* env, jobject thiz) {    LOGI("%sE", __func__);    if(sLedDevice) {       sLedDevice->set_on(sLedDevice);    }else{       LOGI("sLedDevice is null");    }    return 0; }  static jint led_setOff(JNIEnv* env, jobject thiz) {    LOGI("%s E", __func__);     if(sLedDevice) {        sLedDevice->set_off(sLedDevice);     }else{         LOGI("sLedDevice is null");     }     return 0; }static inline int led_control_open(const structhw_module_t* module,     structled_control_device_t** device) {    LOGI("%s E ", __func__);     returnmodule->methods->open(module,        LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);}static jint led_init(JNIEnv *env, jclass clazz){    led_module_tconst * module;    LOGI("%s E ", __func__);     if(hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0){        LOGI("get Module OK");        sLedModule = (led_module_t *) module;        if(led_control_open(&module->common, &sLedDevice) != 0) {           LOGI("led_init error");           return-1;        }    }     LOGI("led_init success");    return 0;}  /*  *  * Array ofmethods.  * Each entryhas three fields: the name of the method, the method  * signature,and a pointer to the native implementation.  */static const JNINativeMethod gMethods[] = {    {"_init",    "()Z",(void*)led_init},    {"_set_on",   "()I",(void*)led_setOn },    {"_set_off", "()I",(void*)led_setOff },    {"_get_count", "()I",(void*)get_count },};  static int registerMethods(JNIEnv* env) {     static constchar* const kClassName = "com/hello/LedService";     jclass clazz;     /* look upthe class */     clazz =env->FindClass(kClassName);     if (clazz ==NULL) {        LOGE("Can't find class %s\n", kClassName);         return-1;     }      /* registerall the methods */     if(env->RegisterNatives(clazz, gMethods,            sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)     {        LOGE("Failed registering methods for %s\n", kClassName);         return -1;     }     /* fill outthe rest of the ID cache */     return 0; }  /*  * This iscalled by the VM when the shared library is first loaded.  */ jint JNI_OnLoad(JavaVM* vm, void* reserved) {     JNIEnv* env= NULL;     jint result= -1;    LOGI("JNI_OnLoad");     if(vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {        LOGE("ERROR: GetEnv failed\n");         gotofail;     }     assert(env!= NULL);     if(registerMethods(env) != 0) {         LOGE("ERROR: PlatformLibrary nativeregistration failed\n");         gotofail;     }     /* success-- return valid version number */     result =JNI_VERSION_1_4;  fail:     return result; }

The code here is not easy to read, because it contains the JNI type and JNI features of the Code. Look at the code first and find the entry. Ledservice. when the Java framework code is loaded, the system in the static initialization statement block is called. loadlibrary ("led_runtime"), load libled_runtime.so, the Library is just in front of Android. the target file of the MK file, that is, the library loaded by the ledservice is generated by the local code above. When a dynamic library is loaded by Dalvik, Dalvik calls back the jni_onload function in the code of the library. That is to say, jni_onload is the entry function of the local service code.

The jni_onload code is generally dead. You can copy it directly when using it. VM-> getenv will return the jnienv pointer, which is actually the environment variable of the Java Virtual Machine, we can use this pointer to call methods provided by JNI, such as findclass, call the registermethods method, and find reference of the ledservice class through jnienv findclass in the method, register the ing between the local method and the Java method in this class. The upper-layer Java code can call the ing to implement the local code. The registernatives method receives three parameters:

L first parameter jclass: The local method ing relationship in the class to be registered

L The second parameter jninativemethod *: this is a local method ing array to the Java method. jninativemethod is a struct, and each element is a ing from a Java method to a local method.

typedef struct {         constchar* name;         constchar* signature;         void*fnPtr;} JNINativeMethod;

Name: indicates the Java method name.

Signature: indicates the method signature.

Fnptr: local method pointer corresponding to the Java method

L size, the third parameter, is the number of ing relationships.

The Code shows that the ing between the Java method and the local method is as follows:

Java method

Local Method

Void _ Init ()

Jint led_init (jnienv * ENV, jclass clazz)

Int _ set_on ()

Jint led_seton (jnienv * ENV, jobject thiz)

Int _ set_off ()

Jint led_setoff (jnienv * ENV, jobject thiz)

Int _ get_count ()

Jint get_count (void)

The above table shows that the local method parameters have two default parameters: jnienv * env and jobject thiz, which indicate the JNI environment and the object reference that calls the current method respectively, of course, you can also not set these two parameters. In this case, you cannot access members in the Java environment. The signature of the local method and the Java method must be consistent, and the return value is inconsistent without causing errors.

Now let's review our call process:

L leddemo creates the ledservice object

L when the ledservice class is loaded, the corresponding local service library is loaded. In the local service library, Dalvik automatically calls the jni_onload function and registers the ing between the Java method and the local method.

According to the characteristics of the Java language, when the leddemo object is created, its constructor ledservice () is called ().

// Constructor public ledservice () {int icount; log. I ("Java service", "Do init native call"); _ Init (); icount = _ get_count (); log. D ("Java service", "led COUNT =" + icount); log. D ("Java service", "init OK ");}

In the ledservice constructor, the local methods _ init and _ get_count are directly called (with the native reserved word Declaration). That is to say, the jint led_init (jnienv * ENV, jclass clazz) in the local service code is called) and jintget_count (void ).

The content in the led_init method is the usage rules of Hal framework code.

L use the hw_get_module method to find the module registered as led_hardware_module_id, that is, "led.

L open the LED device through the open function pointer associated with the led_module, return its device_t struct, and save it in the local code. Some may ask, isn't the local method unable to keep a reference? Because the device_t structure is allocated by malloc on the open device, the pointer remains available as long as the current process does not die. The local code here does not save the reference in Dalvik, the allocated space address of mallco is saved, but remember to free the address space when you disable the device. Otherwise, the memory will leak.

L after obtaining the device_t structure of the LED Device, call the set_on and set_off methods of the ledservice object when the buttons on the leddemo are pressed, the two ledservice methods directly call the corresponding ing method of the local service code. The local method directly calls the function directed to device_t to indirectly call the driver operation code.

Well, let's look at a detailed sequence diagram:

There is no need to explain more.

The android. mk file corresponding to Hal in the last file:

@ Hardware/Android. mk:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_C_INCLUDES += \         include/LOCAL_PRELINK_MODULE := falseLOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hwLOCAL_SHARED_LIBRARIES := liblogLOCAL_SRC_FILES := led.cLOCAL_MODULE := led.defaultinclude $(BUILD_SHARED_LIBRARY)

Note: local_prelink_module: = false must be added; otherwise, the compilation fails.

Specify the target name: LED. Default

The target input directory local_module_path is/system/lib/HW/. If this parameter is not specified, it is output to the/system/lib directory by default.

According to the preceding Hal framework analysis, the default load address of the Hal stub library is/vendor/lib/HW/or/system/lib/HW/, which can be found in these two directories: hardware ID. default. so, we have specified the Hal stub compilation target named led. default, compiled into a dynamic library, the output directory is: $ (target_out_shared_libraries)/HW, target_out_shared_libraries refers to the/system/lib/directory.

5) In-depth understanding

We analyze the code written above from the concept of process space.

In the previous sample code. java and ledservice. java is stored in an APK file, which means that after the application is compiled, it will run in a Dalvik Virtual Machine instance, that is, in a process, in the ledservice. java loads the libled_runtime.so library and calls the local code through JNI. Based on the running principle of the dynamic library, we know that, libled_runtime.so will be loaded to the memory and mapped to the process space of the referenced database during the first reference. We can simply understand that the program of the referenced database and the referenced database are in a process, in the libled_runtime.so library, the library file LED is opened through dlopen. default. so (the library is not loaded by the library loader, but is opened as a file), we can also understand it as LED. default. so and libled_runtime.so are in the same process.

It can be seen that the LED Hal code in the above example is all implemented in one process. The ledservice function in this example is redundant and basically cannot be regarded as a service. If leddemo runs in two processes, it means that the ledservice in the two processes cannot be reused. Generally, the so-called Service provides services to the client and can provide multiple client services (such as) at the same time ), therefore, the LED Hal code in our example is not a perfect Hal model, and we will implement a more perfect Hal architecture later.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.