Android8. 1 Hal layer development

Posted by Ty44ler on Mon, 17 Jan 2022 03:46:02 +0100

This article is based on Android 8 1 source code.
Here is a simple writing method and call of HAL.
I will write an app interface that directly calls HAL, and the HAL layer code will directly read and write the driven node.

brief introduction

A new element of Android O is Project Treble. This is a major change in the architecture of the Android operating system framework, which aims to make it easier and faster for manufacturers to update their devices to the new Android system at a lower cost.
Before Android O, HAL was one by one The so library is opened through dlopen. The library and the framework are in the same process.
Under the new architecture, framework and HAL run in different processes, and all HAL are completed by new HIDL technology. As part of this change, devices running Android 8.0 must support bound or straight through HAL:

  • Bound HAL. HAL in HAL interface definition language (HIDL). These HALS replace the traditional HAL and old HAL used in earlier Android versions. In bound HAL, the Android framework communicates with HAL through Binder interprocess communication (IPC) calls. All devices equipped with Android 8.0 or later must only support bound HAL.

  • Straight through HAL. Traditional HAL or legacy HAL encapsulated in HIDL. These HALS encapsulate existing HALS and can be used in binding mode and
    Used in the same process mode. Devices upgraded to Android 8.0 can use straight through HAL.

You can see that bound HAL is the main push method. The following HAL will also be written in the binding HAL mode.

to write. hal file

There is a fingerprint identification in the source code. The path is / android/hardware/interfaces/biometrics/fingerprint.
A simple version of fingerprint HAL will be written here. There are only set, subscribe, unsubscribe and callback callback methods. Read and write data directly to the driver node. There are three main methods to operate the driver node: open, write and read.

The hal directory structure we write is as follows:

We need to write three Hal file, types hal,IFingerprint.hal and ifingerprintcallback hal.
types.hal defines some data structures, ifingerprint HAL defines the interface called from the Framework to HAL, and ifingerprintcallback HAL is the interface for HAL callback to the Framework.

First, create a fingerprint folder in the / hardware/interfaces directory, then create a folder named 1.0 in fingerprint, and add hal files in the 1.0 folder.

//types.hal
package android.hardware.fingerprint@1.0;

struct FingerprintValue {
    /** Property identifier */
    int32_t propId;
    
    vec<int32_t> values;
   
};

Only a simple structure is defined here to store fingerprint data.

//IFingerprint.hal
package android.hardware.fingerprint@1.0;

import IFingerprintCallback;

interface IFingerprint {
    
    subscribe(IFingerprintCallback callback) generates (bool retval);    

    unsubscribe() generates (bool retval);

    set(FingerprintValue fingerprintValue) generates (bool retval);

};

Three methods are defined here: subscribe to IFingerprintCallback callback, unsubscribe, and set data to the driver.

//IFingerprintCallback.hal
package android.hardware.fingerprint@1.0;


interface IFingerprintCallback {

    oneway onFingerprintEvent(FingerprintValue fingerprintValue);

};

Here is the callback method just subscribed, which is used to callback data to the application.

After writing the three hal files, import the environment variables in the root directory of the source code, and then enter the / hardware/interfaces directory to execute the following commands:

./update-makefiles.sh

This will generate some bp files, mk files and some necessary things.

Write main logic

Create a new default folder in the 1.0 folder.
Write fingerprint H and fingerprint CPP, we will be in fingerprint CPP implements the method we just defined in hal file to read and write driver node data. The code is as follows:

//Fingerprint.h
#ifndef ANDROID_HARDWARE_FINGERPRINT_V1_0_FINGERPRINT_H
#define ANDROID_HARDWARE_FINGERPRINT_V1_0_FINGERPRINT_H

#include <android/hardware/fingerprint/1.0/IFingerprint.h>

namespace android {
namespace hardware {
namespace fingerprint {
namespace V1_0 {
namespace implementation {

using ::android::hardware::fingerprint::V1_0::IFingerprint;
using ::android::hardware::fingerprint::V1_0::IFingerprintCallback;
using ::android::hardware::fingerprint::V1_0::FingerprintValue;


struct Fingerprint : public IFingerprint {
    Fingerprint(); 
    ~Fingerprint();
    Return<bool> subscribe(const sp<IFingerprintCallback>& callback)  override;
    Return<bool> unsubscribe()  override;
    Return<bool> set(const FingerprintValue& fingerprintValue)  override;

  private:
    void fingerprint_message_recv_thread_init();
    void fingerprint_message_recv_thread_destroy();
    void fingerprint_message_recv_thread_subscribe();
    void fingerprint_message_recv_thread_unsubscribe();
};

extern "C" IFingerprint* HIDL_FETCH_IFingerprint(const char* name);

}  // namespace implementation
}  // namespace V1_0
}  // namespace fingerprint
}  // namespace hardware
}  // namespace android

#endif  // ANDROID_HARDWARE_FINGERPRINT_V1_0_FINGERPRINT_H
//Fingerprint.cpp
#define LOG_TAG "android.hardware.fingerprint@1.0-impl"

#include <stdbool.h>
#include <stdlib.h>
#include <pthread.h>
#include <fcntl.h>
#include <signal.h>
#include <semaphore.h>
#include <log/log.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include "Fingerprint.h"

#define REMOTE_MESSAGE_BUFFER_SIZE			36
#define REMOTE_MESSAGE_PRINT_BUFFER_SIZE	256
#define REMOTE_MESSAGE_PRINT_MAX_COUNT		64

namespace android {
namespace hardware {
namespace fingerprint {
namespace V1_0 {
namespace implementation {

struct hw_fingerprint_device_impl {
	int	         fd;
};

struct hw_fingerprint_recv_thread
{
	pthread_t				t_id;
	pthread_attr_t			t_attr;
	bool					t_exitflag;
};

static sp<IFingerprintCallback>	mCallback = 0;

static struct hw_fingerprint_device_impl	g_fingerprint_impl =
{
	.fd = -1,
};

static struct hw_fingerprint_recv_thread g_fingerprint_recv_thread =
{
	.t_id					= 0,
	.t_exitflag				= false,
};

static void* fingerprint_message_recv_thread_proc(void *arg)
{
	ALOGD("%s", __FUNCTION__);

	unsigned char data[REMOTE_MESSAGE_BUFFER_SIZE];
    FingerprintValue fingerprintValue;

	while (g_fingerprint_recv_thread.t_exitflag == false)
	{
		if(g_fingerprint_impl.fd != -1)
		{
			memset(data, 0, REMOTE_MESSAGE_BUFFER_SIZE);
			int len = read(g_fingerprint_impl.fd, data, REMOTE_MESSAGE_BUFFER_SIZE); //Read data from drive node
			if (len > 0){
				if( data[0] != 0 ){
					//report message
					fingerprintValue.values.resize(len);
					for(int i = 0; i < len ; i++){
						fingerprintValue.values[i] = data[i];
					}
				
					if(mCallback != 0)
					{
						mCallback->onFingerprintEvent(fingerprintValue);//Callback data to application
					}else{
						ALOGE("Try to mCallback->onFingerprintEvent but mCallback==0!!!");
					}
				}else{
					usleep(100000);
				}
			}
		}
		else
		{
			ALOGE("Device has not been initialized!");
		}
	}

	ALOGD("%s END!", __FUNCTION__);

	return NULL;
}

Fingerprint::Fingerprint()
{
	
	ALOGD("%s", __FUNCTION__);

	if(-1 == g_fingerprint_impl.fd)
	{
		g_fingerprint_impl.fd = open("/dev/i2c-6", O_RDWR | O_NOCTTY | O_NDELAY);//Open drive node
		if(-1 == g_fingerprint_impl.fd)
		{
			ALOGE("Open device \"/dev/i2c-6\" failed!");
		}
	}

}

Fingerprint::~Fingerprint() {
	if(-1 != g_fingerprint_impl.fd)
	{
		close(g_fingerprint_impl.fd);
		g_fingerprint_impl.fd = -1;
	}

}

void Fingerprint::fingerprint_message_recv_thread_subscribe()
{
	ALOGD("%s", __FUNCTION__);

	g_fingerprint_recv_thread.t_exitflag = false;

	if (pthread_create(&g_fingerprint_recv_thread.t_id,
						NULL,
						fingerprint_message_recv_thread_proc,
						NULL))
	{
		g_fingerprint_recv_thread.t_id = 0;
	}
}

void Fingerprint::fingerprint_message_recv_thread_unsubscribe()
{
	ALOGD("%s", __FUNCTION__);

	if(g_fingerprint_recv_thread.t_id != 0)
	{
		g_fingerprint_recv_thread.t_exitflag = true;
		void* lpv = NULL;
		pthread_join(g_fingerprint_recv_thread.t_id, &lpv);
		g_fingerprint_recv_thread.t_id = 0;
	}
}

//When subscribing, start a thread to read data from the driver node all the time
Return<bool> Fingerprint::subscribe(const sp<IFingerprintCallback>& callback)  {
	ALOGD("%s", __FUNCTION__);

	mCallback = callback;

	if(-1 != g_fingerprint_impl.fd)
	{
		fingerprint_message_recv_thread_subscribe();//Create thread read data
		return true;
	}
	return false;
}

Return<bool> Fingerprint::unsubscribe()  {
	ALOGD("%s", __FUNCTION__);
	
	//signal(SIGIO, SIG_IGN);
	if(-1 != g_fingerprint_impl.fd)
	{
		fingerprint_message_recv_thread_unsubscribe();
	}

	return true;
}

Return<bool> Fingerprint::set(const FingerprintValue& fingerprintValue) {
	ALOGD("%s", __FUNCTION__);
    std::vector<uint8_t> vecData;
	unsigned int write_size = 0;
	for (auto value : fingerprintValue.values) {
		vecData.push_back((uint8_t)value);
		ALOGD("%s , value : %d", __FUNCTION__,value);
	}
	int msg_length = vecData.size();

	if(-1 != g_fingerprint_impl.fd){
		write_size = write(g_fingerprint_impl.fd, (char *)vecData.data(), msg_length);//Write data to drive node
		if(msg_length != write_size){
			ALOGE("Fingerprint Send message failed! write_size: %d != msg_length: %d !", write_size, msg_length);
			return false;
		}
	}else{
		ALOGE("Fingerprint Device has not been initialized!");
		return false;
	}
    return true;
}

IFingerprint* HIDL_FETCH_IFingerprint(const char* /* name */) {    

	return new Fingerprint();
}

} // namespace implementation
}  // namespace V1_0
}  // namespace fingerprint
}  // namespace hardware
}  // namespace android

Write the above two files and execute them again/ update-makefiles.sh update.
Then write Android hardware. fingerprint@1.0 -service. RC startup script, service CPP portal, Android BP is used for compilation.

//android.hardware.fingerprint@1.0-service.rc
service fingerprint-hal-1-0 /vendor/bin/hw/android.hardware.fingerprint@1.0-service
    class hal
    user system
    group system
//service.cpp
#define LOG_TAG "android.hardware.fingerprint@1.0-service"

#include <android-base/logging.h>
#include <hidl/HidlTransportSupport.h>
#include <android/hardware/fingerprint/1.0/IFingerprint.h>
#include <hidl/LegacySupport.h>

#include "Fingerprint.h"

using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::fingerprint::V1_0::implementation::Fingerprint;

int main() {

    configureRpcThreadpool(4, true);

    Fingerprint fingerprint;
    auto status = fingerprint.registerAsService();
    CHECK_EQ(status, android::OK) << "Failed to register fingerprint HAL implementation";

    joinRpcThreadpool();
    return 0;  // joinRpcThreadpool shouldn't exit
}

This is the service of bound HAL CPP writing method.

//Android.bp
cc_binary {
    vendor: true,
    relative_install_path: "hw",
    name: "android.hardware.fingerprint@1.0-service",
    init_rc: ["android.hardware.fingerprint@1.0-service.rc"],
    srcs: [
        "Fingerprint.cpp",
        "service.cpp",
    ],

    cflags: [
        "-Wall",
    ],

    shared_libs: [
        "liblog",
        "libutils",
        "libhidltransport",
        "android.hardware.fingerprint@1.0",
        "libdl",
        "libhardware",
        "libhidlbase",
	"libbase",
    ],

}

Well, the main code is finished.
After that, we need to modify the configuration file so that the hal we write can be used.

Modify system configuration

After writing the above file, if you call the interface directly, the following error prompt will appear:

 3004.17> 05-09 16:47:40.519  2909  2909 I example.com.fingerprinttest: Looking for service android.hardware.fingerprint@1.0::IFingerprint/default
 3004.17> 05-09 16:47:40.521   473   473 W /system/bin/hwservicemanager: getTransport: Cannot find entry android.hardware.fingerprint@1.0::IFingerprint/default in either framework or device manifest.
 3004.17> 05-09 16:47:40.521   701  1474 I AudioPolicyManagerCustom: FLAG None hence request for a primary output
 3004.17> 05-09 16:47:40.522  2909  2909 E example.com.fingerprinttest: service android.hardware.fingerprint@1.0::IFingerprint declares transport method EMPTY but framework expects hwbinder.
 3004.17> 05-09 16:47:40.522  2909  2909 D AndroidRuntime: Shutting down VM
 3004.17> 05-09 16:47:40.523   701  1474 I AudioPolicyManagerCustom: FLAG None hence request for a primary output
 3004.17> 05-09 16:47:40.523  2909  2909 E AndroidRuntime: FATAL EXCEPTION: main
 3004.17> 05-09 16:47:40.523  2909  2909 E AndroidRuntime: Process: example.com.fingerprinttest, PID: 2909
 3004.17> 05-09 16:47:40.523  2909  2909 E AndroidRuntime: java.util.NoSuchElementException
 3004.18> 05-09 16:47:40.523  2909  2909 E AndroidRuntime: 	at android.os.HwBinder.getService(Native Method)
 3004.18> 05-09 16:47:40.523  2909  2909 E AndroidRuntime: 	at android.hardware.fingerprint.V1_0.IFingerprint.getService(IFingerprint.java:44)
 3004.18> 05-09 16:47:40.523  2909  2909 E AndroidRuntime: 	at example.com.fingerprinttest.MainActivity.onClick(MainActivity.java:40)
 3004.18> 05-09 16:47:40.523  2909  2909 E AndroidRuntime: 	at android.view.View.performClick(View.java:6294)
 3004.18> 05-09 16:47:40.523  2909  2909 E AndroidRuntime: 	at android.view.View$PerformClick.run(View.java:24774)

This is a reminder that we need to modify the manifest XML file, add the following code so that the application will not make an error when calling the hal.

 android/device/qcom/msm8996/manifest.xml
<hal format="hidl">
    <name>android.hardware.fingerprint</name>
    <transport>hwbinder</transport>
    <version>1.0</version>
    <interface>
        <name>IFingerprint</name>
        <instance>default</instance>
    </interface>
</hal>

In addition, it needs to be in Android / device / qcom / msm8996 / msm8996 MK file
Add Android hardware. fingerprint@1.0 -Service will start Android hardware. fingerprint@1.0 -service. rc

msm8996 is the file name modified by Qualcomm itself. The names of different manufacturers will be different, but the modification is still the same.

In addition to the above errors, we may also report the following errors:

Line 254: [   42.716694] init: computing context for service 'fingerprint_hal_service'
Line 255: [   42.717950] init: service fingerprint_hal_service does not have a SELinux domain defined

This is because fingerprint_ hal_ The service was not started because the HWService was not started due to SELinux protection mechanism.
Because when Google requires init to start the service, it must switch SELinux domain, that is, switch from init domain to another domain. This is from the perspective of security. By default, the SELinux permission of init domain is large and can do many dangerous behaviors, such as mount image, kill process, etc. If an ordinary service uses init domain, it increases the security surface that can be attacked.

Google checks this item in CTS, CTS fail of Android security. cts. SELinuxDomainTest # testInitDomain

Generally, if init starts a oneshot service that can be executed quickly, that is, a non deamon program, and "flashes by", domain switching can not be performed Such CTS cannot be detected If init starts a memory resident daemon service, you must switch domains (version L0/L1)

However, in the M version, Google enhanced this constraint by using neverallow init {file_type fs_type}: File execute_ no_ trans; It is strictly limited that init must switch the domain when starting the service, otherwise the service cannot be started!!!

The solution is as follows:
Modify android/device/qcom/sepolicy/msm8996/file_contexts, add the following code:

//(vendor|system/vendor)/bin/hw/android.hardware.fingerprint@1.0-service    u:object_r:hal_xiaoheifingerprint_default_exec:s0

Hal above_ xiaoheifingerprint_ Default is defined by ourselves. Why add xiaohei? Because if you don't add it, it will conflict with Yuansheng..
Hal needs to be added under android/device/qcom/sepolicy/msm8996 /_ xiaoheifingerprint_ default. Te file, the code is as follows:

//hal_xiaoheifingerprint_default.te
type hal_xiaoheifingerprint_default, domain; //Set a new domain called hal_xiaoheifingerprint_default

type hal_xiaoheifingerprint_default_exec, exec_type, vendor_file_type, file_type; //Set your exec file type
init_daemon_domain(hal_xiaoheifingerprint_default) //Setup for domain transition

hal_server_domain(hal_xiaoheifingerprint_default, hal_fingerprint); //Set your domain as server domain of hal_fingerprint in which define by AOSP already

In this way, hal's code is completed!

Write APP and call hal interface directly

Compiling the above hal file will generate HIDL files, so we can directly call the interface without having to write a JNI for APP as before. It is also possible to write a JNI. For details, please refer to the radio module. The path is android/hardware/interfaces/broadcastradio. The JNI is in android/frameworks/base/services/core/jni/BroadcastRadio. In Android P, it is found that the JNI is directly removed from the radio module, so it seems that the JNI of Android O is just a transition.

Just throw the code.

//MainActivity.java
package example.com.fingerprinttest;

import android.hardware.fingerprint.V1_0.FingerprintValue;
import android.hardware.fingerprint.V1_0.IFingerprint;
import android.app.Activity;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity implements View.OnClickListener {

    Button btn_subscribe, btn_unsubscribe, btn_set;
    IFingerprint fingerprintService;
    FingerprintCallback fingerprintCallback = new FingerprintCallback();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
	
	try {
            fingerprintService = IFingerprint.getService();//Get service
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        
        btn_subscribe = (Button) this.findViewById(R.id.btn_subscribe);
        btn_unsubscribe = (Button) this.findViewById(R.id.btn_unsubscribe);
        btn_set = (Button) this.findViewById(R.id.btn_set);

        btn_subscribe.setOnClickListener(this);
        btn_unsubscribe.setOnClickListener(this);
        btn_set.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_subscribe:
                try {
                    fingerprintService.subscribe(fingerprintCallback);//Subscribe to callback
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.btn_unsubscribe:
                try {
                    fingerprintService.unsubscribe();//Unsubscribe callback
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.btn_set:
                FingerprintValue fingerprintValue = new FingerprintValue();
                fingerprintValue.values.add(1);
                fingerprintValue.values.add(3);
                try {
                    fingerprintService.set(fingerprintValue);//Send message to hal
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}
//FingerprintCallback.java
package example.com.fingerprinttest;

import android.hardware.fingerprint.V1_0.FingerprintValue;
import android.hardware.fingerprint.V1_0.IFingerprintCallback;
import android.os.RemoteException;
import android.util.Log;

public class FingerprintCallback extends IFingerprintCallback.Stub{

    @Override
    public void onFingerprintEvent(FingerprintValue fingerprintValue) throws RemoteException {
            Log.d("Fingerprint", "FingerprintCallback onFingerprintEvent~~~~~~");
            //The logic for handling callbacks here
    }
}
//Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_PACKAGE_NAME := FingerprintTest
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
LOCAL_DEX_PREOPT := false
LOCAL_PROGUARD_ENABLED:= disabled
LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.fingerprint-V1.0-java-static
LOCAL_SRC_FILES := $(call all-subdir-java-files)
include $(BUILD_PACKAGE)

Compiling under the source code requires writing a mk file.

fingerprint hal code download

Topics: Android