网站链接: element-ui dtcms
当前位置: 首页 > 技术博文  > 技术博文

安卓源码笔记四:硬件抽象层HAL之 开发硬件访问服务及测试

2021/6/28 21:38:49 人评论

本文是学习罗升阳大佬的《安卓系统源代码情景分析》的笔记和总结 这是第二章硬件抽象层2.4、2.5小节的总结 一、什么是硬件访问服务? 二、那么这里有两个技术问题:framework和应用层之间、framework和硬件抽象层之间是怎么沟通的? 三、开发硬…

       本文是学习罗升阳大佬的《安卓系统源代码情景分析》的笔记和总结

        这是第二章硬件抽象层2.4、2.5小节的总结

        一、什么是硬件访问服务?

        二、那么这里有两个技术问题:framework和应用层之间、framework和硬件抽象层之间是怎么沟通的?

        三、开发硬件访问服务的步骤

        四、理解硬件抽象层、framework层、应用层之间的关系

        一、什么是硬件访问服务?

        硬件访问服务就是framework层里面的一个类,在这个类里把应用层发过来的访问请求转化成访问硬件抽象层,不要把它想得太复杂,因为它能给应用层提供某种功能所以称为服务,习惯在这个类名后面加上Service,比如FregService、PowerService等

       

        二、那么这里有两个技术问题:framework和应用层之间、framework和硬件抽象层之间是怎么沟通的?

        framework和应用层之间:framework的硬件访问服务通常运行在系统进程,而应用程序通常运行在别的进程中,相当于两个人在两个地方,那么这个时候最好是有个收发器(Binder)给他们进行联系,那这个收发器的通信协议得给个说明呀,不然用户怎么用呢?可以用AIDL进行说明

        framework和硬件抽象层之间:framework层用java,硬件抽象层用c++,相当于两个语言不同的人,那他们怎么交流?需要翻译软件(JNI),关于JNI可参考NDK、JNI的使用方法

        三、开发硬件访问服务的步骤

        1、首先需要明确想给应用层提供什么样的服务接口

         然而,应用层和framework之间常常是在不同的进程中的,所以需要一种可以跨进程的接口,AIDL可以满足要求

          frameworks/base/core/java/android/os/IFregService.aidl          

package android.os ;

interface IFregService{
	void setVal(int val);
	int getVal();
}

        2、在framework层中实现第1步的接口

        前面第一点说了“在这个类里把应用层发过来的访问请求转化成访问硬件抽象层”,那么是怎么实现这个转化的呢?

        首先需要通过freg_init()得到这个硬件抽象层里面的device的地址,然后在给应用层提供的接口里面间接调用这个device里面的方法就可以了。比如下面的:应用层-->setValue()-->JNI的setVal_native(这步就是翻译的过程即根据java的请求调用相应的c)-->JNI里面又会调用硬件抽象层的相应方法,具体看JNI方法的代码

 frameworks/base/services/java/com/android/server/FregService.java

package com.android.server;
import android.os.IFregService;
import android.util.Slog;
public class FregService extends IFregService.Stub{
    private static final String TAG="IFregService";
    private int mPtr;

    //构造函数,后面在ServiceManager里面add这个服务的时候会调用new FregService(),会执行这里
    public FregService() {
        mPtr=init_native();//mPtr是硬件抽象层device的地址
        if(mPtr==0){
            Slog.e(TAG,"failed to initialize freg service");
        }
    }

    public void setVal(int val){
        if(mPtr==0){
            Slog.e(TAG,"failed to initialize freg service");
            return;
        }
        setVal_native(mPtr,val);//每次应用层调用setVal的时候其实调用的是JNI的setVal_native
    }

    public int getVal(){
        if(mPtr==0){
            Slog.e(TAG,"failed to initialize freg service");
            return 0;
        }
        return getVal_native(mPtr);
    }


    
    private static native int init_native();
    private static native void setVal_native(int ptr,int val);
    private static native int getVal_native(int ptr);
}

        记得编译:mmm ./frameworks/base/services/java/

        3、实现第2步中JNI的方法

       既然JNI会调用硬件抽象层的方法,是怎么做到的呢?

        首先你想调用哪个device肯定需要找到对应的module,所以通过hw_get_module()加载硬件抽象层模块,然后打开虚拟设备看是否存在,如果存在则把设备的指针传回,这样就可以根据这个地址找到对应的设备从而使用设备的相应接口

         frameworks/base/services/jni/com_android_server_FregService.cpp

#define LOG_TAG "FregServiceJNI"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/freg.h>

#include <stdio.h>

#define FREG_MODULE_ID "freg_module"
#define FREG_DEVICE_ID "freg_device"

namespace android{

static int freg_device_open (const struct hw_module_t* module,
            struct freg_device_t ** device){
            return module->methods->open(module,FREG_DEVICE_ID,(struct hw_device_t ** )device);
            }

    static jint freg_init(JNIEnv * env ,jclass jclazz){
        freg_module_t * module;
        freg_device_t * device;
        LOGI("Initializing HAL stub freg ....");
        //加载硬件抽象层模块
        if(hw_get_module(FREG_MODULE_ID,(const struct hw_module_t **)&module)==0){
            LOGI("Device freg found");
            //打开虚拟硬件设备,在freg_device_open里面将设备的地址赋给&device
            if(freg_device_open(&(module->moduleBase),&device)==0)
            {
                LOGI("Device is open");
                //注意这里返回的是设备的地址而不是单纯的一个整数,返回地址给framework
                //后,framework就可以将这个地址转化成设备对象(见freg_setVal、freg_getVal)
                //然后就可以通过这个对象调用设备的方法了
                return (jint)device;
            }
            LOGI("Device is open failed");
            return 0;
        }

                LOGI("Failed to get HAL stub freg ....");
                return 0;
    }

    static void freg_setVal(JNIEnv * env ,jclass jclazz,jint ptr ,jint value){
        //将参数ptr转化成freg_device_t,注意ptr是freg_init中返回的device的地址
        freg_device_t * device =(freg_device_t *) ptr;
        int val =value;
        LOGI("set value %d",val);
        device->set_val(device,val);
    }


    static jint freg_getVal(JNIEnv * env ,jclass jclazz,jint ptr ){
        //将参数ptr转化成freg_device_t
                freg_device_t * device =(freg_device_t *) ptr;
                int val=0;
                device->get_val(device,&val);
                LOGI("get value %d",val);
                return val;
    }
    /*本地接口方法表
    *Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。
    *其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在
    *其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod
    *第一个参数是java中函数的名字
    *第二参数是用字符串是描述了函数的参数和返回值
    *第三个参数是函数指针,指向C函数
    */
    static const JNINativeMethod method_table[]={
        {"init_native","()I",(void *)freg_init},
        {"setVal_native","(II)V",(void *)freg_setVal},
        {"getVal_native","(I)I",(void *)freg_getVal},
    };

    /*
    *注册本地接口方法表
    */
    int register_android_server_FregService(JNIEnv * env){
        int ret=0;
        ret=jniRegisterNativeMethods(env,"com/android/server/FregService",method_table,NELEM(method_table));
        return ret;
    }
};

记得编译:mmm ./frameworks/base/services/jni/   

  注意编写好服务和jni后都是要编译一下的,它们的编译命令有区别

补充:hw_get_module()如下

    4、启动这个硬件访问服务

        

    5、写应用程序测试相应的硬件服务接口

        和老罗的基本一样

package com.example.helloandroid;

import android.app.Activity;
import android.os.Bundle;
import android.os.IFregService;
import android.os.ServiceManager;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.TextView;
import android.view.View.OnClickListener;
import android.os.RemoteException;

import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener{
	private final static String TAG="shy.luo.hello.helloAndroid";
	private TextView textView;
	private EditText editText;
	private Button readBtn;
	private Button writeBtn;
	private IFregService fregService=null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Log.i(TAG,"HelloAndroid Activity created");
		fregService=IFregService.Stub.asInterface(ServiceManager.getService("freg"));

		textView=(TextView)findViewById(R.id.text);
		editText=(EditText)findViewById(R.id.edit);
		readBtn=(Button)findViewById(R.id.read);
		writeBtn=(Button)findViewById(R.id.write);

		readBtn.setOnClickListener(this);
		writeBtn.setOnClickListener(this);
	}

	@Override
	public void onClick(View view){
		if (view.equals(readBtn)){
			try{
				int val=fregService.getVal();//调用framework的硬件服务接口
				String text=String.valueOf(val);
				textView.setText(text);
			}catch (RemoteException e){

			}

		}else if(view.equals(writeBtn)){
			try{
				String text=editText.getText().toString();
				int val=Integer.parseInt(text);//调用framework的硬件服务接口
				fregService.setVal(val);
			}catch (RemoteException e){

			}

		}
	}
}

        结果:

   

、理解硬件抽象层、framework层、应用层之间的关系

       
       

        

相关资讯

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?