android Q HIDL(小屏显示)

android Q HIDL(小屏显示)

手机设备上添加了一个小的lcd屏,需求是可以显示文字与图片.且可以在每个应用里面使用,过CTS,那么可供选择的实现方式也就没几种了.

 

(a)过cts的话最好是通过hidl与底层驱动通讯,hidl使用起来也挺方便.

(b)驱动使用的数据为32*40大小的char,大约1kB多点,数据量较小采用序列化的数据传输,不选择使用共享内存

(c)需要满足应用可使用则需要提供java接口,目前属于调试阶段,无法判断后期第三方应用是否能使用,故选择已有的DisplayManager服务 ,扩展其接口,有点是SE权限好控制,缺点是数据在传输过程中多了一次copy.

(d)数据的生成是采用从Bitmap中获取像素点的方式.毕竟是需要图片显示的,同时图片为了适配lcd显示的大小,也要使用Canvas缩小或者扩大其Bitmap.

(一)添加hw hal module

(1)添加h532blcd.default hal模块,创建h532blcd_t,同时生成HAL_MODULE_INFO_SYM.

#define H532BLCD_MODULE_API_VERSION_1_1 HARDWARE_MODULE_API_VERSION(1, 1)
#define H532BLCD_HARDWARE_MODULE_ID "h532blcd"

typedef struct h532blcd_module {
    struct hw_module_t common;
} h532blcd_module_t;

typedef struct h532blcd {
    struct hw_device_t common;

    int (*lcd_send_data)(struct h532blcd *dev,const signed char *data);

} h532blcd_t;

/*
 * Generic device handling
 */
static int h532blcd_open(const hw_module_t* module, const char* name,
        hw_device_t** device) {
    if (device == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "NULL device on open");
        return -EINVAL;
    }

    __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Device open");
    h532blcd_t *dev = malloc(sizeof(h532blcd_t));
    memset(dev, 0, sizeof(h532blcd_t));

    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (struct hw_module_t*) module;
    dev->common.close = h532blcd_close;
#if 1
    dev->lcd_send_data = lcd_send_data;
#endif
    *device = (hw_device_t*) dev;
    return 0;
}


static struct hw_module_methods_t h532blcd_module_methods = {
        .open = h532blcd_open,
};

h532blcd_module_t HAL_MODULE_INFO_SYM = {
        .common = {
                .tag = HARDWARE_MODULE_TAG,
                .module_api_version = H532BLCD_MODULE_API_VERSION_1_1,
                .hal_api_version = HARDWARE_HAL_API_VERSION,
                .id = H532BLCD_HARDWARE_MODULE_ID,
                .name = "h532blcd",
                .author = "The Android Open Source Project",
                .methods = &h532blcd_module_methods,
        },
};

(2)打开设备节点与处于内核空间的lcd屏进行数据传输

typedef struct
{
	unsigned char data[32*40]; // 1280
} __attribute__((packed))pixData;

#define H532BLCD_SEND_DATA				_IOW('C', 0x01, pixData)

static int fd = -1;

extern int open_dev();
extern int close_dev();
extern int send_data(pixData *data);

(3)生成h532blcd.default.so文件至vendor/lib64/hw/h532blcd.default.so vendor/lib/hw/h532blcd.default.so.

注意H532BLCD_HARDWARE_MODULE_ID的值必须与生成的so文件前缀相同,这个是hal注册的关键

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional
LOCAL_HEADER_LIBRARIES := libhardware_headers
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_SRC_FILES := ioctl_h532blcd.c hw_h532blcd.c
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE := h532blcd.default
LOCAL_INIT_RC := init.h532blcd.rc

include $(BUILD_SHARED_LIBRARY)

(4)修改设备节点权限 init.h532blcd.rc

on boot
# h532blcd
    chmod 0666 /dev/h532blcdc

(5)添加SE权限

device.te
type simplelcd_device, dev_type;

file_contexts
/dev/h532blcdc u:object_r:simplelcd_device:s0

(二)注册hidl服务

开机后运行

(1)创建manifest.xml

<manifest version="1.0" type="device">
    <hal format="hidl">
        <name>vendor.hct.hardware.simplelcd</name>
        <transport>hwbinder</transport>
        <impl level="generic"></impl>
        <version>1.0</version>
        <interface>
            <name>ISimpleLcd</name>
            <instance>default</instance>
        </interface>
    </hal>
</manifest>

(2)创建hal文件ISimpleLcd.hal,同时运行hardware/interfaces/update-makefiles.sh自动生成Android.bp文件

package vendor.hct.hardware.simplelcd@1.0;

interface ISimpleLcd {
    lcd_send_data(int8_t[1280] arg) generates (int32_t result);
}

(3)创建可执行文件vendor.hct.hardware.simplelcd@1.0-service

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := vendor.hct.hardware.simplelcd@1.0-service
LOCAL_INIT_RC := vendor.hct.hardware.simplelcd@1.0-service.rc
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_SRC_FILES := \
    SimpleLcd.cpp \
    service.cpp \

LOCAL_SHARED_LIBRARIES := \
    libcutils \
    liblog \
    libhidlbase \
    libhidltransport \
    libhardware \
    libutils \
    vendor.hct.hardware.simplelcd@1.0 \

include $(BUILD_EXECUTABLE)

vendor.hct.hardware.simplelcd@1.0-service.rc

service simplelcd_hal /vendor/bin/hw/vendor.hct.hardware.simplelcd@1.0-service
    # "class hal" causes a race condition on some devices due to files created
    # in /data. As a workaround, postpone startup until later in boot once
    # /data is mounted.
    class late_start
    user system
    group system input

SimpleLcd.cpp

namespace vendor {
namespace hct {
namespace hardware {
namespace simplelcd {
namespace V1_0 {
namespace implementation {

SimpleLcd *SimpleLcd::sInstance = nullptr;

SimpleLcd::SimpleLcd() : mDevice(nullptr){
    LOGD("SimpleLcd()");
    sInstance = this; // keep track of the most recent instance
    mDevice = openHal();
}
SimpleLcd::~SimpleLcd() {
    LOGD("~SimpleLcd()");
    if (mDevice == nullptr) {
        LOGD("No valid device");
        return;
    }
    int err;
    h532blcd_t* dev = reinterpret_cast<h532blcd_t*>(mDevice);
    if (0 != (err = dev->common.close(mDevice))) {
        LOGD("Can't close h532blcd module, error: %d", err);
        return;
    }
    mDevice = nullptr;
}

ISimpleLcd* SimpleLcd::getInstance() {
    if (!sInstance) {
      sInstance = new SimpleLcd();
    }
    return sInstance;
}
Return<int32_t> SimpleLcd::lcd_send_data(const hidl_array<int8_t, 1280>& arg){
    if(mDevice == NULL){
        return -1;
    }

    h532blcd_t* dev = reinterpret_cast<h532blcd_t*>(mDevice);
    LOGD("hal Device lcd_send_data \n");
    return dev-> lcd_send_data(dev,arg.data());
}
hw_device_t* SimpleLcd::openHal(){
    hw_device_t* dev = nullptr;
    hw_module_t const* module;
    int err = hw_get_module(H532BLCD_HARDWARE_MODULE_ID, &module);
    if (err != 0) {
        LOGD("Can't open SimpleLcd detect HW Module, error: %d", err);
        return nullptr;
    }

    err = module->methods->open(module, "h532blcd", &dev);
    if (err < 0) {
        LOGD("Can't open SimpleLcd Detect, error: %d", err);
        return nullptr;
    }
    LOGD("Open SimpleLcd hal");
    return dev;
}

} //namespace implementation
} //namespace V1_0
} //namespace simplelcd
} //namespace hardware
} //namespace hct
} //namespace vendor

service.cpp

#define LOG_TAG "vendor.hct.hardware.simplelcd@1.0-service"

#include <android/log.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
#include <vendor/hct/hardware/simplelcd/1.0/ISimpleLcd.h>
#include "SimpleLcd.h"

using vendor::hct::hardware::simplelcd::V1_0::ISimpleLcd;
using vendor::hct::hardware::simplelcd::V1_0::implementation::SimpleLcd;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::sp;

int main() {
    android::sp<ISimpleLcd> bio = SimpleLcd::getInstance();

    configureRpcThreadpool(1, true /*callerWillJoin*/);

    if (bio != nullptr) {
        bio->registerAsService();
    } else {
        ALOGE("Can't create instance of BiometricsFingerprint, nullptr");
    }

    joinRpcThreadpool();

    return 0; // should never get here
}

(三)frameworks曾添加接口

(1)frameworks/base/services/core/Android.bp

    static_libs: [
        "vendor.hct.hardware.simplelcd-V1.0-java",
    ],

(2)frameworks/base/core/java/android/hardware/display/IDisplayManager.aidl

/** @hide */
interface IDisplayManager {
    // Temporarily sets the display brightness.
    void setSimpleLcdShow(in byte[] data);
}

(3)frameworks/base/core/java/android/hardware/display/DisplayManager.java

提供两个接口(a)setSimpleLcdText显示文字(b)setSimpleLcdDrawable显示图片

    /**
     * @param text The text for LCD show.
     *
     * @hide
     */
    public void setSimpleLcdText(CharSequence text){
        Log.d("SimpleLcdDrawable","text ++ ");
        if (Looper.getMainLooper() != Looper.myLooper()) {
            throw new RuntimeException("setSimpleLcdText must called in MainThread!");
        }

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.BLACK);
        setLedTextSize(paint,DEFAULT_TEXT_SIZE);
        measureTextBound(paint,text.toString(),LCD_HORIZONTAL_END,LCD_VERTICAL_END);
        byte[] data = generateLedBitmap(renderText(text.toString(), paint));
        Log.d("SimpleLcdDrawable","text -- ");
        mGlobal.setSimpleLcdShow(data);
    }

    /**
     * @param drawable The drawable for LCD show.
     *
     * @hide
     */
    public void setSimpleLcdDrawable(Drawable drawable){
        Log.d("SimpleLcdDrawable","drawable ++ ");
        if (Looper.getMainLooper() != Looper.myLooper()) {
            throw new RuntimeException("setSimpleLcdDrawable must called in MainThread!");
        }
        Drawable ledImage = drawable;
        byte[] data = generateLedBitmap(renderDrawable(ledImage, LCD_HORIZONTAL_END, LCD_VERTICAL_END));
        Log.d("SimpleLcdDrawable","drawable -- ");
        mGlobal.setSimpleLcdShow(data);
    }

    /**
     * set the text size
     * @param paint paint
     * @param size text size
     */
    private void setLedTextSize(Paint paint ,float size) {
        Log.d("SimpleLcdText", "setLedTextSize :" + size);
        float ledTextSize = size;
        paint.setTextSize(ledTextSize);
        if(ledTextSize > 30){
            paint.setTypeface(Typeface.DEFAULT_BOLD);
        }else {
            paint.setTypeface(Typeface.MONOSPACE);
        }
    }

    /**
     * measure the text width and height
     * @param paint paint
     * @param text text content
     * @param width    the Led Width
     * @param height   the Led Height
     */
    private void measureTextBound(Paint paint,String text, int width, int height) {

        Paint.FontMetrics m = paint.getFontMetrics();
        measureTextWidth = (int) paint.measureText(text);
        measureTextHeight = (int) (m.bottom - m.ascent);
        float sacle = Math.min((float) width / measureTextWidth, (float) height / measureTextHeight);
        if(sacle < 1){
            setLedTextSize(paint, paint.getTextSize() * sacle);
            measureTextBound(paint, text,width,height);
        }
    }

    /**
     * Transform text to bitmap
     *
     * @param text  text content
     * @param paint paint
     * @return the bitmap of text
     */
    private Bitmap renderText(CharSequence text, Paint paint) {
        Bitmap bitmap = Bitmap.createBitmap(measureTextWidth, measureTextHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2));
        canvas.drawText(text.toString(), 0, yPos, paint);
        return bitmap;
    }

    /**
     * Transform the image drawable to bitmap
     *
     * @param drawable the content drawable
     * @param width    the Led Width
     * @param height   the Led Height
     * @return bitmap of drawable
     */
    private static Bitmap renderDrawable(Drawable drawable, int width, int height) {
        Bitmap bitmap = getBitmapFromDrawable(drawable);
        float sacle = Math.min((float) width / bitmap.getWidth(), (float) height / bitmap.getHeight());
        if(sacle < 1){
            return Bitmap.createScaledBitmap(bitmap,
                    (int)(bitmap.getWidth() * sacle),
                    (int)(bitmap.getHeight() * sacle), true);
        }else {
            return bitmap;
        }
    }
    /**
     * Get bitmap from drawable, Copy from CircleImageView
     *
     * @param drawable the drawable
     * @return the bitmap of drawable
     */
    private static Bitmap getBitmapFromDrawable(Drawable drawable) {
        if (drawable == null) {
            return null;
        }

        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        try {
            Bitmap bitmap;

            if (drawable instanceof ColorDrawable) {
                bitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
            } else {
                bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ALPHA_8);
            }

            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Transform a bitmap to a led bitmap
     *
     * @param src the original bitmap
     * @return led bitmap
     */
    private byte[] generateLedBitmap(Bitmap src) {

        byte[] data = new byte[LCD_VERTICAL_END * LCD_HORIZONTAL_END/8];
        int bitmapH = src.getHeight();
        int bitmapW = src.getWidth();

        int paddStart = (LCD_HORIZONTAL_END - bitmapW)/2;
        int paddTop = (LCD_VERTICAL_END- bitmapH)/2;
        //StringBuffer stringBuffer = new StringBuffer();
        for(int y = 0; y < src.getHeight();y++){
            for(int x = 0;x < src.getWidth();x++){
                int color = isInRange(src, x , y);
                int lcdX = paddStart + x;
                int lcdY = paddTop + y;

                int dataID = (lcdX/8 + lcdY*32);
                int byteId = 7 - lcdX%8;

                if (color != 0) {
                    data[dataID] = (byte) (data[dataID] | (0x1<<byteId));
                    //stringBuffer.append("1");
                }else {
                    //stringBuffer.append("0");
                }
            }
            //stringBuffer.append("\n");
        }
        //Log.d("SimpleLcdDrawable",stringBuffer.toString());

        return data;
    }

    /**
     * Measure if x and y is in range of leds
     *
     * @param bitmap the origin bitmap
     * @param x      led x
     * @param y      led y
     * @return the color , if color is zero means empty
     */
    private int isInRange(Bitmap bitmap, int x, int y) {
        if (bitmap == null)
            return 0;
        if (y> 0 && y < bitmap.getHeight()
                && x > 0 && x < bitmap.getWidth()) {
            int px = bitmap.getPixel(x, y);
            if(px == 0){
                return 0;
            }
            return Color.argb(0xff, 0x00, 0x00, 0x00);
        }else {
            return 0;
        }
    }

(4)frameworks/base/core/java/android/hardware/display/DisplayManagerGlobal.java

实现IDisplayManager.aidl的接口

    /**
     *
     * @param data The data for LCD.
     *
     * @hide
     */
    public void setSimpleLcdShow(byte[] data) {
        if(data != null && data.length == 1280){
            StringBuffer stringBuffer = new StringBuffer();
            for (int iLine=0; iLine<40; iLine++) {
                for(int i = iLine*32; i < (iLine*32+31); i++) {
                    byte pointChar = data[i];
                    for(int j=7; j>=0; j--) {
                        if((pointChar>>j&0x1)!= 0){
                            stringBuffer.append("1");
                        }else{
                            stringBuffer.append("0");
                        }
                    }
                }
                if (DEBUG) {
                    Log.d("SimpleLcdDrawable",stringBuffer.toString());
                }
                stringBuffer.setLength(0);
            }
        }else {
            throw new RuntimeException("setSimpleLcdShow data.length must 1280 ,but now is " + (data == null ? 0:data.length));
        }

        try {
            mDm.setSimpleLcdShow(data);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

(5)frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java

实现IDisplayManager.aidl接口,同时通过hidl传递到hal层

        @Override // Binder call
        public void setSimpleLcdShow(byte[] data) {
            final long token = Binder.clearCallingIdentity();
            try {
                Slog.d(TAG, "setSimpleLcdShow");
                if (mSimpleLcd != null) {
                    int result = mSimpleLcd.lcd_send_data(data);
                    Slog.d(TAG, "setSimpleLcdShow dev(0) result = " + result);
                }else{
                    mSimpleLcd = ISimpleLcd.getService();
                    Slog.d(TAG, "ISimpleLcd.getService");
                    int result = mSimpleLcd.lcd_send_data(data);
                    Slog.d(TAG, "setSimpleLcdShow dev(0) result = " + result);
                }
            }catch (RemoteException re){
                Slog.d(TAG, "setSimpleLcdShow re = " + re.getMessage());
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

(6)添加system_service能掉hidl的权限

system_server.te
allow system_server hal_simplelcd_hwservice:hwservice_manager find;

(四)调用实例

通过getSystemService获取到DisplayManager,然后调用setSimpleLcdText("HELLO")就可以在lcd屏上看到显示了.

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值