Android 重学系列 SurfaceFlinger 的HAL层初始化

前言

上一篇文章我们研究了SF的初始化。但是还有一个很大也是核心的模块没有聊到,那就是HAL层对应的初始化。什么是HAL层,有简单的话来讲就是硬件驱动和软件之间的中间层,为了更好的兼容Android系统而诞生。

在Android 8.0之后会涉及应用开发及其少接触的一个新类型文件.hal文件。本质上.hal和.aidl文件十分相似,设计初衷和aidl也很相似。aidl为的是Android跨进程通信,而.hal则是为了让android软件层和硬件层通信而做的隔离,因此这种交互方式又称为HIDL。

闲话不多说,我们先来看看hal文件的用法之后,再去看看SF中IComposer,IComposerClient中都做了什么事情。

如果遇到什么问题可以来本文讨论:https://www.jianshu.com/p/8e29c3d9b27a

正文

在Android 8.0之后,Google为了各大手机厂商的方便,执行了一个名为Treble计划,其目的就是为了让各大手机厂商和硬件商能够在最小代价在Android系统升级的时候,能快速更新硬件相关的特性。

hal的升级历程.png

大致可以把hal层的历史分为如下3个时期:

  • 1.Legacy Hal:Android 8.0 之前版本的 HAL 都是编译成 so,然后动态链接到各个 frameworks service 中去。

  • 2.Passthrough Hal:该模式是为了兼容旧版的 HAL,旧版 HAL 实现仍以动态库的方式提供,只是 binder service 链接了动态库 HAL 实现,即 binder service 通过 hw_get_module 链接了旧版的 hal 实现,而用户端通过与 binder service IPC 通信,间接实现了与旧版 HAL 的交互。

  • 3.Binderized HAL:HAL 与 用户调用在不同的进程中,HAL 被写成 binder service,而用户接口如 frameworks 作为 binder client 通过 IPC 机制实现跨进程接口调用。这个是 Google 的最终设计目标。

能看到,从历史来看,整个hal层的隔离越来越厉害,越来越接近AIDL的方向设计。因此,Google选择和AIDL的方式,构建一个hal文件,让硬件商和系统只需要关注暴露在hal文件中接口的逻辑即可。

注意一下,这里面的binder 并非是我之前分析的那个Binder,而是hwBinder,不过代码和原理是一摸一样。不过前者负责进程间通信,hwBinder则负责硬件进程和上层进程的通信。

这样设计其实很简单,如果阅读过我的Binder源码解析就能明白,Binder中有一套复杂的事务传输逻辑,如果把硬件驱动也接进来,就没办法达到软件可以快速响应硬件回调或者软件调用硬件。

为了更好的理解HIDL语言,我们可以完全把它当作AIDL这种语言进行类比学习。

本文重点不是hidl的原理,只是作为基础知识和大家介绍。具体的细节,有机会再和大家聊聊。

hal文件介绍

我们就以IComposer.hal为例子,先来看看2.1版本。
文件:/hardware/interfaces/graphics/composer/2.1/IComposer.hal

package android.hardware.graphics.composer@2.1;

import IComposerClient;

interface IComposer {

    enum Capability : int32_t {
        INVALID = 0,

        SIDEBAND_STREAM = 1,

        SKIP_CLIENT_COLOR_TRANSFORM = 2,

        PRESENT_FENCE_IS_NOT_RELIABLE = 3,
    };

    @entry
    @exit
    @callflow(next="*")
    getCapabilities() generates (vec<Capability> capabilities);

    @entry
    @exit
    @callflow(next="*")
    dumpDebugInfo() generates (string debugInfo);

    @entry
    @callflow(next="*")
    createClient() generates (Error error, IComposerClient client);
};

能看到在hal文件中,只能注意的是,我上一篇文章聊到的createClient方法,创建一个IComposerClient对象返回上层。

能看到实际上和aidl十分相似不是吗。当我们把它类比成aidl就特别好理解了。一般的,编写了一个hal文件之后,就需要使用hidl-gen工具,类似aidl一样生成一个真正的cpp文件,里面包含着真正的跨进程通信逻辑,以及相关的接口文件。

当我们写好hal文件之后,并且通过hidl-gen生成的文件中实现接口后,写好Android.bp文件。大致上能得到类似的目录:


image.png

当然这是composer 2.2版本,不过从大体设计看来差距不是很大。

一般的会在default子目录编写好入口方法:


image.png

能看到2.2版本下只剩下一个service.cpp。我们再看看2.1版本又是如何:


image.png

能看到2.1版本中,多了一个cpp文件passthrough。顾名思义,passthrough就是直通模式,service就是binder service模式。
文件:/hardware/interfaces/graphics/composer/2.1/default/Android.bp

cc_library_shared {
    name: "android.hardware.graphics.composer@2.1-impl",
    defaults: ["hidl_defaults"],
    vendor: true,
    relative_install_path: "hw",
    srcs: ["passthrough.cpp"],
    header_libs: [
        "android.hardware.graphics.composer@2.1-passthrough",
    ],
    shared_libs: [
        "android.hardware.graphics.composer@2.1",
        "android.hardware.graphics.mapper@2.0",
        "libbase",
        "libcutils",
        "libfmq",
        "libhardware",
        "libhidlbase",
        "libhidltransport",
        "liblog",
        "libsync",
        "libutils",
        "libhwc2on1adapter",
        "libhwc2onfbadapter",
    ],
    cflags: [
        "-DLOG_TAG=\"ComposerHal\""
    ],
}

cc_binary {
    name: "android.hardware.graphics.composer@2.1-service",
    defaults: ["hidl_defaults"],
    vendor: true,
    relative_install_path: "hw",
    srcs: ["service.cpp"],
    init_rc: ["android.hardware.graphics.composer@2.1-service.rc"],
    shared_libs: [
        "android.hardware.graphics.composer@2.1",
        "libbinder",
        "libhidlbase",
        "libhidltransport",
        "liblog",
        "libsync",
        "libutils",
    ],
}

能看到这个bp文件中,有两个cc编译工具,一个是以passthrough.cpp为入口,一个是以service.cpp为入口。也就是说,不同的模式启动Composer,将会打包进不同的入口。

值得注意的一点是,在share_libs一行中已经打包进了实现的so文件android.hardware.graphics.composer@2.1 以及 图元申请工具android.hardware.graphics.mapper@2.0
文件:/hardware/interfaces/graphics/composer/2.1/Android.bp

hidl_interface {
    name: "android.hardware.graphics.composer@2.1",
    root: "android.hardware",
    vndk: {
        enabled: true,
    },
    srcs: [
        "types.hal",
        "IComposer.hal",
        "IComposerCallback.hal",
        "IComposerClient.hal",
    ],
    interfaces: [
        "android.hardware.graphics.common@1.0",
        "android.hidl.base@1.0",
    ],
    types: [
        "Error",
    ],
    gen_java: false,
}

就能看到这个时候就看是引入了我们之前写入的hal文件。在整个Composer的HAL层包含了三个文件,一个在Composer中的是IComposer对象,一个是IComposerClient对象,还有一个是实现了IComposerCallback进行监听刷新屏幕热插拔的ComposerCallbackBridge。全部都囊括在内。

当我们选择的是直通模式,将会到下面这个android.hardware.graphics.composer@2.1-passthrough实现中,而这个对应的bp如下:
文件:/hardware/interfaces/graphics/composer/2.1/utils/passthrough/Android.bp

cc_library_headers {
    name: "android.hardware.graphics.composer@2.1-passthrough",
    defaults: ["hidl_defaults"],
    vendor: true,
    shared_libs: [
        "libhardware",
        "libhwc2on1adapter",
        "libhwc2onfbadapter",
    ],
    export_shared_lib_headers: [
        "libhardware",
        "libhwc2on1adapter",
        "libhwc2onfbadapter",
    ],
    header_libs: [
        "android.hardware.graphics.composer@2.1-hal",
    ],
    export_header_lib_headers: [
        "android.hardware.graphics.composer@2.1-hal",
    ],
    export_include_dirs: ["include"],
}

能看到进一步的引入了hal,以及compser2.2转2.1的adapter等。值得注意的是export_include_dirs这个命令,这个命令是指把这个目录下的头文件全部引入到该模块中,而在这个文件夹中,就是hal硬件抽象层的实现代码。


image.png

而这两个文件就是Composer在hal中passthrough模式进入到具体业务的入口。

最后,我们需要在下面如同应用程序注册四大组建一样,注册到Android系统的xml中:
文件:/device/linaro/hikey/manifest.xml

    <hal format="hidl">
        <name>android.hardware.graphics.composer</name>
        <transport arch="32+64">passthrough</transport>
        <version>2.1</version>
        <interface>
            <name>IComposer</name>
            <instance>default</instance>
        </interface>
    </hal>

能看到的是hikey海思就是这么注册composer的,同时使用的还是passthrough模式。

当然,还有其他的如Google给的案例:

    <hal format="hidl">
        <name>android.hardware.graphics.composer</name>
        <transport>hwbinder</transport>
        <version>2.1</version>
        <interface>
            <name>IComposer</name>
            <instance>default</instance>
        </interface>
      </hal>

这里则是使用Binder的方式注册hal层逻辑。

别忘了最后编写一个rc文件,让init进程解析运行起来:
文件:/hardware/interfaces/graphics/composer/2.1/default/android.hardware.graphics.composer@2.1-service.rc

service vendor.hwcomposer-2-1 /vendor/bin/hw/android.hardware.graphics.composer@2.1-service
    class hal animation
    user system
    group graphics drmrpc
    capabilities SYS_NICE
    writepid /dev/cpuset/system-background/tasks

on property:init.svc.surfaceflinger=stopped
    restart vendor.hwcomposer-2-1

其实了解这么多就可以了,如果想要全面的了解hidl的使用,不妨阅读下面这片文章:

如果想要探索hwBinder 进程service的方式是如何把Compser hal进程注册到hwServiceManager,又是如何找到对应的进程的,不妨看看下面这篇文章,写的挺好的:

当我们有了一定的了解之后,我们可以进行对Composer的HAL进行解析。无论是直通模式还是binder service模式,只是启动方式不一样而已,不影响我们研究整个Composer 的HAL核心机制。

本文就以直通模式来和大家聊聊,其中的原理。

小结 hidl的启动与注册

简单的总结一下,Hal进程的启动和HIDL服务注册的原理:
还记得获得IComposer服务的方式如下:

mComposer = V2_1::IComposer::getService(serviceName);

通过getService获取。这个函数做的事情有如下几件事情:

  • 1.先通过hwservicemanager 查询已经从manifest解析好的数据,先检查该服务需要以哪种形式启动的也就是Transport类型,是hwbinder的绑定模式还是passthrough的直通模式。

  • 2.当为hwbinder模式时候,将会从hwservicemanager查询服务

  • 3.当为passthrough模式时候,将会从PassthroughServiceManager获取服务

  • 4.如果是passthrough启动,则会先先从系统目录下查找有没有对应的so,接着通过dlopen打开so,并用dlsym查找HIDL_FETCH_I+interface的名字,也就是HIDL_FETCH_IComposer方法获取IComposer对象,并执行起来。

  • 5.拿到IComposer之后,就会注册到hwservicemanager

HAL passthrough模式的启动入口

我们先来看IComposer 2.1版本:
文件:/hardware/interfaces/graphics/composer/2.1/default/passthrough.cpp

#include <android/hardware/graphics/composer/2.1/IComposer.h>
#include <composer-passthrough/2.1/HwcLoader.h>

using android::hardware::graphics::composer::V2_1::IComposer;
using android::hardware::graphics::composer::V2_1::passthrough::HwcLoader;

extern "C" IComposer* HIDL_FETCH_IComposer(const char* /* name */) {
    return HwcLoader::load();
}

可以看到本质上是HwcLoader调用了load方法实例化出来的。

HwcLoader使用IComposer 初始化

文件:/hardware/interfaces/graphics/composer/2.1/utils/passthrough/include/composer-passthrough/2.1/HwcLoader.h

static IComposer* load() {
        const hw_module_t* module = loadModule();
        if (!module) {
            return nullptr;
        }

        auto hal = createHalWithAdapter(module);
        if (!hal) {
            return nullptr;
        }

        return createComposer(std::move(hal));
    }

能从该方法初步看到,IComposer初始化分为三个步骤:

  • 1.loadModule 加载hw_module_t 结构体,这个结构体实际上是一个hal模块的结构体。
  • 2.createHalWithAdapter 通过hw_module_t 初始化hwc2_device_t结构体,让其拥有和顶层通信的能力也就是函数指针,并且适配2.1和2.2版本
  • 3.createComposer 把hwc2_device_t转化为IComposer上传给客户端,也就是SF。

接下来,我们就围绕着着三个方法,来聊聊整个逻辑。

loadModule 加载 hw_module_t

#define HWC_HARDWARE_MODULE_ID "hwcomposer"
#define GRALLOC_HARDWARE_MODULE_ID "gralloc"
    // load hwcomposer2 module
    static const hw_module_t* loadModule() {
        const hw_module_t* module;
        int error = hw_get_module(HWC_HARDWARE_MODULE_ID, &module);
        if (error) {
            ALOGI("falling back to gralloc module");
            error = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
        }

        if (error) {
            ALOGE("failed to get hwcomposer or gralloc module");
            return nullptr;
        }

        return module;
    }

能看到这个过程中会调用hw_get_module尝试获取对应名字的模块hwcomposer,如果获取不到则退化成gralloc,整个机制也就蜕化成HWC还没有出现之前的版本.我们先不去探索失败,我们来看看成功。

hw_get_module 查找 hw_module_t

文件:/hardware/libhardware/hardware.c

int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{

...

found:
    /* load the module, if this fails, we're doomed, and we should not try
     * to load a different variant. */
    return load(class_id, path, module);
}

int hw_get_module(const char *id, const struct hw_module_t **module)
{
    return hw_get_module_by_class(id, NULL, module);
}

这个过程会根据传进来的id名字来查找是否已经有如下几个路径下的so,

#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib/hw"

找到了则执行load方法:

static int load(const char *id,
        const char *path,
        const struct hw_module_t **pHmi)
{
    int status = -EINVAL;
    void *handle = NULL;
    struct hw_module_t *hmi = NULL;
#ifdef __ANDROID_VNDK__
    const bool try_system = false;
#else
    const bool try_system = true;
#endif

    /*
     * load the symbols resolving undefined symbols before
     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
     * RTLD_NOW the external symbols will not be global
     */
    if (try_system &&
        strncmp(path, HAL_LIBRARY_PATH1, strlen(HAL_LIBRARY_PATH1)) == 0) {
        /* If the library is in system partition, no need to check
         * sphal namespace. Open it with dlopen.
         */
        handle = dlopen(path, RTLD_NOW);
    } else {
        handle = android_load_sphal_library(path, RTLD_NOW);
    }
    if (handle == NULL) {
        char const *err_str = dlerror();
        ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
        status = -EINVAL;
        goto done;
    }

    /* Get the address of the struct hal_module_info. */
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    hmi = (struct hw_module_t *)dlsym(handle, sym);
    if (hmi == NULL) {
        ALOGE("load: couldn't find symbol %s", sym);
        status = -EINVAL;
        goto done;
    }

    /* Check that the id matches */
    if (strcmp(id, hmi->id) != 0) {
        ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
        status = -EINVAL;
        goto done;
    }

    hmi->dso = handle;

    /* success */
    status = 0;

    done:
    if (status != 0) {
        hmi = NULL;
        if (handle != NULL) {
            dlclose(handle);
            handle = NULL;
        }
    } else {
        ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
                id, path, *pHmi, handle);
    }

    *pHmi = hmi;

    return status;
}

如果没有打开,则执行dlopen拿到对应so的句柄,接着同dlsym查找HMI字符,这个字符所指向的内存地址就是我们所说的hw_module_t.换算到现在名字可能是hwcomposer.xx.so

我们随便挑选一个作为源码解析,就以msm8960为例子:
文件:/hardware/qcom/display/msm8960/libhwcomposer/hwc.cpp

static struct hw_module_methods_t hwc_module_methods = {
    open: hwc_device_open
};

hwc_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .version_major = 2,
        .version_minor = 0,
        .id = HWC_HARDWARE_MODULE_ID,
        .name = "Qualcomm Hardware Composer Module",
        .author = "CodeAurora Forum",
        .methods = &hwc_module_methods,
        .dso = 0,
        .reserved = {0},
    }
};

这样就能找到hwc_module_t对应在底层的结构体。

createHalWithAdapter 生成hw_device_t

  // create a ComposerHal instance, insert an adapter if necessary
    static std::unique_ptr<hal::ComposerHal> createHalWithAdapter(const hw_module_t* module) {
        bool adapted;
        hwc2_device_t* device = openDeviceWithAdapter(module, &adapted);
        if (!device) {
            return nullptr;
        }
        auto hal = std::make_unique<HwcHal>();
        return hal->initWithDevice(std::move(device), !adapted) ? std::move(hal) : nullptr;
    }

这个过程做了两件事情,第一件事情就是openDeviceWithAdapter初始化hwc2_device_t,第二件事情就是实例化HwcHal对象并且设置hwc2_device_t。

    static hwc2_device_t* openDeviceWithAdapter(const hw_module_t* module, bool* outAdapted) {
        if (module->id && std::string(module->id) == GRALLOC_HARDWARE_MODULE_ID) {
            *outAdapted = true;
            return adaptGrallocModule(module);
        }

        hw_device_t* device;
        int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device);
        if (error) {
            ALOGE("failed to open hwcomposer device: %s", strerror(-error));
            return nullptr;
        }

        int major = (device->version >> 24) & 0xf;
        if (major != 2) {
            *outAdapted = true;
            return adaptHwc1Device(std::move(reinterpret_cast<hwc_composer_device_1*>(device)));
        }

        *outAdapted = false;
        return reinterpret_cast<hwc2_device_t*>(device);
    }

能够看到,如果失败则会调用adaptGrallocModule,初始化的是gralloc的hal模块。成果则会调用hw_module_t中的open方法。判断如果当前主版本不是2,则调用adaptHwc1Device适配hw_device_t,最后返回。

这里需要注意一点hw_device_t,会被向下转型成hwc_composer_device_1对象后传入adaptHwc1Device。其原理和类的继承很相似,因为hwc_composer_device_1包裹了一个hw_device_t。又因为struct是连续分配的空间,这样才能够强转(这种设计在socket系统调用到处都是)。

typedef struct hwc_composer_device_1 {
   
    struct hw_device_t common;


    int (*prepare)(struct hwc_composer_device_1 *dev,
                    size_t numDisplays, hwc_display_contents_1_t** displays);

    int (*set)(struct hwc_composer_device_1 *dev,
                size_t numDisplays, hwc_display_contents_1_t** displays);

    int (*eventControl)(struct hwc_composer_device_1* dev, int disp,
            int event, int enabled);

    union {
      
        int (*blank)(struct hwc_composer_device_1* dev, int disp, int blank);

        int (*setPowerMode)(struct hwc_composer_device_1* dev, int disp,
                int mode);
    };


    int (*query)(struct hwc_composer_device_1* dev, int what, int* value);


    void (*dump)(struct hwc_composer_device_1* dev, char *buff, int buff_len);

    int (*getDisplayConfigs)(struct hwc_composer_device_1* dev, int disp,
            uint32_t* configs, size_t* numConfigs);

    int (*getDisplayAttributes)(struct hwc_composer_device_1* dev, int disp,
            uint32_t config, const uint32_t* attributes, int32_t* values);

    int (*getActiveConfig)(struct hwc_composer_device_1* dev, int disp);

    int (*setActiveConfig)(struct hwc_composer_device_1* dev, int disp,
            int index);

    int (*setCursorPositionAsync)(struct hwc_composer_device_1 *dev, int disp, int x_pos, int y_pos);

    void* reserved_proc[1];

} hwc_composer_device_1_t;

在上文能看到此时open的方法,指向的是hwc_device_open函数指针。

hwc_device_open 初始化hw_device_t
static int hwc_device_open(const struct hw_module_t* module, const char* name,
                           struct hw_device_t** device)
{
    int status = -EINVAL;

    if (!strcmp(name, HWC_HARDWARE_COMPOSER)) {
        struct hwc_context_t *dev;
        dev = (hwc_context_t*)malloc(sizeof(*dev));
        memset(dev, 0, sizeof(*dev));

        //Initialize hwc context
        initContext(dev);

        //Setup HWC methods
        dev->device.common.tag          = HARDWARE_DEVICE_TAG;
        dev->device.common.version      = HWC_DEVICE_API_VERSION_1_2;
        dev->device.common.module       = const_cast<hw_module_t*>(module);
        dev->device.common.close        = hwc_device_close;
        dev->device.prepare             = hwc_prepare;
        dev->device.set                 = hwc_set;
        dev->device.eventControl        = hwc_eventControl;
        dev->device.blank               = hwc_blank;
        dev->device.query               = hwc_query;
        dev->device.registerProcs       = hwc_registerProcs;
        dev->device.dump                = hwc_dump;
        dev->device.getDisplayConfigs   = hwc_getDisplayConfigs;
        dev->device.getDisplayAttributes = hwc_getDisplayAttributes;
        *device = &dev->device.common;
        status = 0;
    }
    return status;
}

能看到在这个方法中,为hw_device_t这个结构体每一个函数指针都真正赋予了其含义,最后所有的hal层执行都会走到hwc.cpp这里。

这里要注意了version的计算并不是上面那个version_major,而是HWC_DEVICE_API_VERSION_1_2。其实他的计算如下

define HWC_DEVICE_API_VERSION_1_2  HARDWARE_DEVICE_API_VERSION_2(1, 2, HWC_HEADER_VERSION)
#define HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) \
            ((((maj) & 0xff) << 24) | (((min) & 0xff) << 16) | ((hdr) & 0xffff))

很有误导性,其实是1在高24位,5在低16位,最后一个是头信息。因此是其实1.2版本,而不是版本2。

所以接下来会走到适配的分之adaptHwc1Device。

adaptHwc1Device 适配hwc2_device_t

    static hwc2_device_t* adaptHwc1Device(hwc_composer_device_1* device) {
        int minor = (device->common.version >> 16) & 0xf;
        if (minor < 1) {
            ALOGE("hwcomposer 1.0 is not supported");
            device->common.close(&device->common);
            return nullptr;
        }

        return new HWC2On1Adapter(device);
    }

能看到小版本小于1则非法,只会适配大于等于的版本,就会生成一个HWC2On1Adapter对象。
文件:/hardware/interfaces/graphics/composer/2.1/utils/hwc2on1adapter/HWC2On1Adapter.cpp

其实这个对象就是hwc2_device_t:

class HWC2On1Adapter : public hwc2_device_t

我们来看看他的构造函数。

HWC2On1Adapter::HWC2On1Adapter(hwc_composer_device_1_t* hwc1Device)
  : mDumpString(),
    mHwc1Device(hwc1Device),
    mHwc1MinorVersion(getMinorVersion(hwc1Device)),
    mHwc1SupportsVirtualDisplays(false),
    mHwc1SupportsBackgroundColor(false),
    mHwc1Callbacks(std::make_unique<Callbacks>(*this)),
    mCapabilities(),
    mLayers(),
    mHwc1VirtualDisplay(),
    mStateMutex(),
    mCallbacks(),
    mHasPendingInvalidate(false),
    mPendingVsyncs(),
    mPendingHotplugs(),
    mDisplays(),
    mHwc1DisplayMap()
{
    common.close = closeHook;
    getCapabilities = getCapabilitiesHook;
    getFunction = getFunctionHook;
    populateCapabilities();
    populatePrimary();
    mHwc1Device->registerProcs(mHwc1Device,
            static_cast<const hwc_procs_t*>(mHwc1Callbacks.get()));
}

在这里面设置两个很核心的东西一个是getFunctionHook,一个调用了hwc_device_t的registerProcs方法。

前者是一个模版方法,调用hwc_device_t中方法指针。后者是hal层注册监听驱动的核心逻辑。

首先看看mHwc1Callbacks实际上是HWC2On1Adapter::Callbacks:

class HWC2On1Adapter::Callbacks : public hwc_procs_t {
    public:
        explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
            invalidate = &invalidateHook;
            vsync = &vsyncHook;
            hotplug = &hotplugHook;
        }

        static void invalidateHook(const hwc_procs_t* procs) {
            auto callbacks = static_cast<const Callbacks*>(procs);
            callbacks->mAdapter.hwc1Invalidate();
        }

        static void vsyncHook(const hwc_procs_t* procs, int display,
                int64_t timestamp) {
            auto callbacks = static_cast<const Callbacks*>(procs);
            callbacks->mAdapter.hwc1Vsync(display, timestamp);
        }

        static void hotplugHook(const hwc_procs_t* procs, int display,
                int connected) {
            auto callbacks = static_cast<const Callbacks*>(procs);
            callbacks->mAdapter.hwc1Hotplug(display, connected);
        }

    private:
        HWC2On1Adapter& mAdapter;
};

可以看到三个十分熟悉的回调,invalidate,vsync,hotplug。那么其实这个回调就是从底层驱动程序上传第一个回调,之后才有其他的。每个方法又会调用HWC2On1Adapter对应的回调方法。

记住,在这里面同时把hwc_procs_t三个函数指针invalidate,vsync,hotplug都指向了这个回调中的方法。换句话说所有从底层响应上来,都该类中调用所有的回调,最终都会汇总到这个Callback。

我们暂时把流程停在这里,稍后让我把监听的接进来,再去更加底层看看是怎么回事?

HwcHal的初始化

文件:/hardware/interfaces/graphics/composer/2.1/utils/passthrough/include/composer-passthrough/2.1/HwcHal.h

namespace android {
namespace hardware {
namespace graphics {
namespace composer {
namespace V2_1 {
namespace passthrough {

namespace detail {

using android::hardware::graphics::common::V1_0::ColorMode;
using android::hardware::graphics::common::V1_0::ColorTransform;
using android::hardware::graphics::common::V1_0::Dataspace;
using android::hardware::graphics::common::V1_0::Hdr;
using android::hardware::graphics::common::V1_0::PixelFormat;
using android::hardware::graphics::common::V1_0::Transform;

// HwcHalImpl implements V2_*::hal::ComposerHal on top of hwcomposer2
template <typename Hal>
class HwcHalImpl : public Hal {
   public:
    virtual ~HwcHalImpl() {
        if (mDevice) {
            hwc2_close(mDevice);
        }
    }

...
    bool initWithDevice(hwc2_device_t* device, bool requireReliablePresentFence) {
        // we own the device from this point on
        mDevice = device;

        initCapabilities();
        if (requireReliablePresentFence &&
            hasCapability(HWC2_CAPABILITY_PRESENT_FENCE_IS_NOT_RELIABLE)) {
            ALOGE("present fence must be reliable");
            mDevice->common.close(&mDevice->common);
            mDevice = nullptr;
            return false;
        }

        if (!initDispatch()) {
            mDevice->common.close(&mDevice->common);
            mDevice = nullptr;
            return false;
        }

        return true;
    }

...

}  // namespace detail

using HwcHal = detail::HwcHalImpl<hal::ComposerHal>;

}  // namespace passthrough
}  // namespace V2_1
}  // namespace composer
}  // namespace graphics
}  // namespace hardware
}  // namespace android

HwcHalImpl 本质上就是继承hal::ComposerHal,也就是说ComposerHal持有
将会持有一个hw_device_t结构体,作为真正的操作对象。

第二点,调用initDispatch,为mDispatch结构体中所有的函数指针都初始化。之后所有调用函数都是是通过mDispatch调用hw_device_t的方法

    struct {
        HWC2_PFN_ACCEPT_DISPLAY_CHANGES acceptDisplayChanges;
        HWC2_PFN_CREATE_LAYER createLayer;
        HWC2_PFN_CREATE_VIRTUAL_DISPLAY createVirtualDisplay;
        HWC2_PFN_DESTROY_LAYER destroyLayer;
        HWC2_PFN_DESTROY_VIRTUAL_DISPLAY destroyVirtualDisplay;
        HWC2_PFN_DUMP dump;
        HWC2_PFN_GET_ACTIVE_CONFIG getActiveConfig;
        HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES getChangedCompositionTypes;
        HWC2_PFN_GET_CLIENT_TARGET_SUPPORT getClientTargetSupport;
        HWC2_PFN_GET_COLOR_MODES getColorModes;
        HWC2_PFN_GET_DISPLAY_ATTRIBUTE getDisplayAttribute;
        HWC2_PFN_GET_DISPLAY_CONFIGS getDisplayConfigs;
        HWC2_PFN_GET_DISPLAY_NAME getDisplayName;
        HWC2_PFN_GET_DISPLAY_REQUESTS getDisplayRequests;
        HWC2_PFN_GET_DISPLAY_TYPE getDisplayType;
        HWC2_PFN_GET_DOZE_SUPPORT getDozeSupport;
        HWC2_PFN_GET_HDR_CAPABILITIES getHdrCapabilities;
        HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT getMaxVirtualDisplayCount;
        HWC2_PFN_GET_RELEASE_FENCES getReleaseFences;
        HWC2_PFN_PRESENT_DISPLAY presentDisplay;
        HWC2_PFN_REGISTER_CALLBACK registerCallback;
        HWC2_PFN_SET_ACTIVE_CONFIG setActiveConfig;
        HWC2_PFN_SET_CLIENT_TARGET setClientTarget;
        HWC2_PFN_SET_COLOR_MODE setColorMode;
        HWC2_PFN_SET_COLOR_TRANSFORM setColorTransform;
        HWC2_PFN_SET_CURSOR_POSITION setCursorPosition;
        HWC2_PFN_SET_LAYER_BLEND_MODE setLayerBlendMode;
        HWC2_PFN_SET_LAYER_BUFFER setLayerBuffer;
        HWC2_PFN_SET_LAYER_COLOR setLayerColor;
        HWC2_PFN_SET_LAYER_COMPOSITION_TYPE setLayerCompositionType;
        HWC2_PFN_SET_LAYER_DATASPACE setLayerDataspace;
        HWC2_PFN_SET_LAYER_DISPLAY_FRAME setLayerDisplayFrame;
        HWC2_PFN_SET_LAYER_PLANE_ALPHA setLayerPlaneAlpha;
        HWC2_PFN_SET_LAYER_SIDEBAND_STREAM setLayerSidebandStream;
        HWC2_PFN_SET_LAYER_SOURCE_CROP setLayerSourceCrop;
        HWC2_PFN_SET_LAYER_SURFACE_DAMAGE setLayerSurfaceDamage;
        HWC2_PFN_SET_LAYER_TRANSFORM setLayerTransform;
        HWC2_PFN_SET_LAYER_VISIBLE_REGION setLayerVisibleRegion;
        HWC2_PFN_SET_LAYER_Z_ORDER setLayerZOrder;
        HWC2_PFN_SET_OUTPUT_BUFFER setOutputBuffer;
        HWC2_PFN_SET_POWER_MODE setPowerMode;
        HWC2_PFN_SET_VSYNC_ENABLED setVsyncEnabled;
        HWC2_PFN_VALIDATE_DISPLAY validateDisplay;
    } mDispatch = {};

createComposer 构建IComposer对象

    static IComposer* createComposer(std::unique_ptr<hal::ComposerHal> hal) {
        return hal::Composer::create(std::move(hal)).release();
    }

文件:/hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/Composer.h

    static std::unique_ptr<ComposerImpl> create(std::unique_ptr<Hal> hal) {
        return std::make_unique<ComposerImpl>(std::move(hal));
    }

很简单,本质上就是一个ComposerImpl持有了ComposerHal。

嵌套的层级有点深,接下来让我把整个UML图画出来就清晰了,再结合一下上一篇文章,梳理一下。


HWC关键数据结构.jpg

Composer初始化中Hal的工作

在上一篇文章中有两个hal方法没有进一步的聊,第一个是createClient创建IComposerClient,另一个是注册监听。

Composer创建ComposerClient

我们先把视角转移到
文件:/frameworks/native/services/surfaceflinger/DisplayHardware/ComposerHal.cpp

Composer::Composer(const std::string& serviceName)
    : mWriter(kWriterInitialSize),
      mIsUsingVrComposer(serviceName == std::string("vr"))
{
    mComposer = V2_1::IComposer::getService(serviceName);

    mComposer->createClient(
            [&](const auto& tmpError, const auto& tmpClient)
            {
                if (tmpError == Error::NONE) {
                    mClient = tmpClient;
                }
            });
...

    // 2.2 support is optional
    sp<IComposer> composer_2_2 = IComposer::castFrom(mComposer);
    if (composer_2_2 != nullptr) {
        mClient_2_2 = IComposerClient::castFrom(mClient);
...
    }
}

mComposer通过getService拿到hal层的IComposer对象。

文件:/hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/Composer.h

    Return<void> createClient(IComposer::createClient_cb hidl_cb) override {
        std::unique_lock<std::mutex> lock(mClientMutex);
        if (!waitForClientDestroyedLocked(lock)) {
            hidl_cb(Error::NO_RESOURCES, nullptr);
            return Void();
        }

        sp<IComposerClient> client = createClient();
        if (!client) {
            hidl_cb(Error::NO_RESOURCES, nullptr);
            return Void();
        }

        mClient = client;
        hidl_cb(Error::NONE, client);
        return Void();
    }

    void onClientDestroyed() {
        std::lock_guard<std::mutex> lock(mClientMutex);
        mClient.clear();
        mClientDestroyedCondition.notify_all();
    }

    virtual IComposerClient* createClient() {
        auto client = ComposerClient::create(mHal.get());
        if (!client) {
            return nullptr;
        }

        auto clientDestroyed = [this]() { onClientDestroyed(); };
        client->setOnClientDestroyed(clientDestroyed);

        return client.release();
    }

实际上还是调用了ComposerClient::create方法实例化一个IComposerClient方法,接着才会把这个这个方法通过回调回调出去。同时让Composer持有一个mClient对象,当销毁的时候,会调用mClient的clear方法,并且唤起阻塞。也就是指销毁一个HWC::Device的mClient对象。

接下来看看ComposerClient的create方法:
文件:/hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h

    static std::unique_ptr<ComposerClientImpl> create(Hal* hal) {
        auto client = std::make_unique<ComposerClientImpl>(hal);
        return client->init() ? std::move(client) : nullptr;
    }

    bool init() {
        mResources = createResources();
        if (!mResources) {
            ALOGE("failed to create composer resources");
            return false;
        }

        mCommandEngine = createCommandEngine();

        return true;
    }

    virtual std::unique_ptr<ComposerResources> createResources() {
        return ComposerResources::create();
    }

    virtual std::unique_ptr<ComposerCommandEngine> createCommandEngine() {
        return std::make_unique<ComposerCommandEngine>(mHal, mResources.get());
    }

能看到Client会持有两个对象,一个是ComposerResources,一个是ComposerCommandEngine。

  • ComposerResources 控制整个SF的Hal的资源,如绘制面Layer,如图元
  • ComposerCommandEngine 处理从SF上层到hal层的一些命令,用来实现一些需要直接通信到驱动的命令。

ComposerResources 初始化

 static std::unique_ptr<ComposerResources> create() {
        auto resources = std::make_unique<ComposerResources>();
        return resources->init() ? std::move(resources) : nullptr;
    }

    bool init() { return mImporter.init(); }

在ComposerResources初始化的同时,还会初始化ComposerHandleImporter对象。

class ComposerHandleImporter {
   public:
    bool init() {
        mMapper = mapper::V2_0::IMapper::getService();
        return mMapper != nullptr;
    }

该对象初始化了一个IMapper的Hal服务,其实该Hal服务就是图元申请器。换句话说Composer将会通过ComposerResources调用ComposerHandleImporter控制图元的状态。

ComposerCommandEngine 初始化

    ComposerCommandEngine(ComposerHal* hal, ComposerResources* resources)
        : mHal(hal), mResources(resources) {}

这个对象等到用的时候再去理解。

这两个对象很重要,记住就好,后面会和它们打交道。

IComposer通过注册监听Hal层实现监听驱动的关键动作

还记得在SF的init方法中,我们还注册了一个Callback到Hal层中:
文件:/frameworks/native/services/surfaceflinger/DisplayHardware/HWC2.cpp

void Device::registerCallback(ComposerCallback* callback, int32_t sequenceId) {
    if (mRegisteredCallback) {
        ALOGW("Callback already registered. Ignored extra registration "
                "attempt.");
        return;
    }
    mRegisteredCallback = true;
    sp<ComposerCallbackBridge> callbackBridge(
            new ComposerCallbackBridge(callback, sequenceId));
    mComposer->registerCallback(callbackBridge);
}

Device中的mComposer,其实是Composer,而这个对象才是统一处理HIDL对应在客户端的对象。

void Composer::registerCallback(const sp<IComposerCallback>& callback)
{
    auto ret = mClient->registerCallback(callback);
    if (!ret.isOk()) {
        ALOGE("failed to register IComposerCallback");
    }
}

文件:/hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h

    Return<void> registerCallback(const sp<IComposerCallback>& callback) override {
        // no locking as we require this function to be called only once
        mHalEventCallback = std::make_unique<HalEventCallback>(callback, mResources.get());
        mHal->registerEventCallback(mHalEventCallback.get());
        return Void();
    }

能看到在这里面会把IComposerCallback再一次包裹成HalEventCallback对象,才会注册到mHal,也就是HwcHalImpl对象。

来看看HalEventCallback这个包裹类:

    class HalEventCallback : public Hal::EventCallback {
       public:
        HalEventCallback(const sp<IComposerCallback> callback, ComposerResources* resources)
            : mCallback(callback), mResources(resources) {}

        void onHotplug(Display display, IComposerCallback::Connection connected) {
            if (connected == IComposerCallback::Connection::CONNECTED) {
                mResources->addPhysicalDisplay(display);
            } else if (connected == IComposerCallback::Connection::DISCONNECTED) {
                mResources->removeDisplay(display);
            }

            auto ret = mCallback->onHotplug(display, connected);
            ALOGE_IF(!ret.isOk(), "failed to send onHotplug: %s", ret.description().c_str());
        }

        void onRefresh(Display display) {
            auto ret = mCallback->onRefresh(display);
            ALOGE_IF(!ret.isOk(), "failed to send onRefresh: %s", ret.description().c_str());
        }

        void onVsync(Display display, int64_t timestamp) {
            auto ret = mCallback->onVsync(display, timestamp);
            ALOGE_IF(!ret.isOk(), "failed to send onVsync: %s", ret.description().c_str());
        }

       protected:
        const sp<IComposerCallback> mCallback;
        ComposerResources* const mResources;
    };

这个方法本质上就是为了集中IComposerCallback和mResources处理。在HalEventCallback中对应每一种回调都有自己的方法,他除了会调用IComposerCallback对应的回调之外,还会特别处理屏幕的热插拔,如果是插入则将一个屏幕对象添加到ComposerResources,进行管理。

    void registerEventCallback(hal::ComposerHal::EventCallback* callback) override {
        mMustValidateDisplay = true;
        mEventCallback = callback;

        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_HOTPLUG, this,
                                   reinterpret_cast<hwc2_function_pointer_t>(hotplugHook));
        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_REFRESH, this,
                                   reinterpret_cast<hwc2_function_pointer_t>(refreshHook));
        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC, this,
                                   reinterpret_cast<hwc2_function_pointer_t>(vsyncHook));
    }

此时将会调用mDispatch的registerCallback方法。其实这个方法是一个模版方法,其实就是调用mDevice的registerCallback方法,并且把后面几个作为参数设置进去。

从上文就解析到mDevice其实是hw2_device_t结构体也是HWC2On1Adapter,那么其实就是调用HWC2On1Adapter中的注册方法。
文件:/hardware/interfaces/graphics/composer/2.1/utils/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h

Error HWC2On1Adapter::registerCallback(Callback descriptor,
        hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) {
    if (!isValid(descriptor)) {
        return Error::BadParameter;
    }


    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);

    if (pointer != nullptr) {
        mCallbacks[descriptor] = {callbackData, pointer};
    } else {
        ALOGI("unregisterCallback(%s)", to_string(descriptor).c_str());
        mCallbacks.erase(descriptor);
        return Error::None;
    }

    bool hasPendingInvalidate = false;
    std::vector<hwc2_display_t> displayIds;
    std::vector<std::pair<hwc2_display_t, int64_t>> pendingVsyncs;
    std::vector<std::pair<hwc2_display_t, int>> pendingHotplugs;

    if (descriptor == Callback::Refresh) {
        hasPendingInvalidate = mHasPendingInvalidate;
        if (hasPendingInvalidate) {
            for (auto& displayPair : mDisplays) {
                displayIds.emplace_back(displayPair.first);
            }
        }
        mHasPendingInvalidate = false;
    } else if (descriptor == Callback::Vsync) {
        for (auto pending : mPendingVsyncs) {
            auto hwc1DisplayId = pending.first;
            if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
                ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d",
                        hwc1DisplayId);
                continue;
            }
            auto displayId = mHwc1DisplayMap[hwc1DisplayId];
            auto timestamp = pending.second;
            pendingVsyncs.emplace_back(displayId, timestamp);
        }
        mPendingVsyncs.clear();
    } else if (descriptor == Callback::Hotplug) {
        // Hotplug the primary display
        pendingHotplugs.emplace_back(mHwc1DisplayMap[HWC_DISPLAY_PRIMARY],
                static_cast<int32_t>(Connection::Connected));

        for (auto pending : mPendingHotplugs) {
            auto hwc1DisplayId = pending.first;
            if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
                ALOGE("hwc1Hotplug: Couldn't find display for HWC1 id %d",
                        hwc1DisplayId);
                continue;
            }
            auto displayId = mHwc1DisplayMap[hwc1DisplayId];
            auto connected = pending.second;
            pendingHotplugs.emplace_back(displayId, connected);
        }
    }

    // Call pending callbacks without the state lock held
    lock.unlock();

    if (hasPendingInvalidate) {
        auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(pointer);
        for (auto displayId : displayIds) {
            refresh(callbackData, displayId);
        }
    }
    if (!pendingVsyncs.empty()) {
        auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
        for (auto& pendingVsync : pendingVsyncs) {
            vsync(callbackData, pendingVsync.first, pendingVsync.second);
        }
    }
    if (!pendingHotplugs.empty()) {
        auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer);
        for (auto& pendingHotplug : pendingHotplugs) {
            hotplug(callbackData, pendingHotplug.first, pendingHotplug.second);
        }
    }
    return Error::None;
}

能看到有2个集合,一个标志位在Hal判断是否需要回调当顶层。当mPendingHotplugs存在还没有被通知的屏幕热插拔消息的时候将会调用hotplug;当mPendingVsyncs存在还没有通知到的屏幕的同步也会调用vsync;如果需要刷新则会有hasPendingInvalidate设置true的标志。

但是,如果是SF第一次进来就肯定不会有回调,因为此时还没有任何的拿到底层的任何数据。但是会把当前的监听类型和回调的方法指针都保存到mCallbacks数组中。

此时就会直接转型为每一个回调到上层,此时就是HwcHalImpl中的refreshHook,hotplugHook,vsyncHook。

HwcHalImpl refreshHook

    static void refreshHook(hwc2_callback_data_t callbackData, hwc2_display_t display) {
        auto hal = static_cast<HwcHalImpl*>(callbackData);
        hal->mMustValidateDisplay = true;
        hal->mEventCallback->onRefresh(display);
    }

此时就会回调到IComposerClient的HalEventCallback。
文件:/hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h

        void onRefresh(Display display) {
            auto ret = mCallback->onRefresh(display);
        }

这个Callback就是上层的ComposerCallbackBridge
文件:/frameworks/native/services/surfaceflinger/DisplayHardware/HWC2.cpp

    Return<void> onRefresh(Hwc2::Display display) override
    {
        mCallback->onRefreshReceived(mSequenceId, display);
        return Void();
    }

而这个Callback就是SF。此时就会走到SF的页面刷新

void SurfaceFlinger::onRefreshReceived(int sequenceId,
                                       hwc2_display_t /*display*/) {
    Mutex::Autolock lock(mStateLock);
    if (sequenceId != getBE().mComposerSequenceId) {
        return;
    }
    repaintEverything();
}

repaintEverything这个方法将会更新所有Layer中的图元。具体是什么意思,在之后的文章将会和大家聊聊

HwcHalImpl vsyncHook
    static void vsyncHook(hwc2_callback_data_t callbackData, hwc2_display_t display,
                          int64_t timestamp) {
        auto hal = static_cast<HwcHalImpl*>(callbackData);
        hal->mEventCallback->onVsync(display, timestamp);
    }
HalEventCallback onVsync
        void onVsync(Display display, int64_t timestamp) {
            auto ret = mCallback->onVsync(display, timestamp);
        }
ComposerCallbackBridge onVsync
    Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
    {
        mCallback->onVsyncReceived(mSequenceId, display, timestamp);
        return Void();
    }

最后就会走到SF中的回调

void SurfaceFlinger::onVsyncReceived(int32_t sequenceId,
        hwc2_display_t displayId, int64_t timestamp) {
    Mutex::Autolock lock(mStateLock);
    // Ignore any vsyncs from a previous hardware composer.
    if (sequenceId != getBE().mComposerSequenceId) {
        return;
    }

    int32_t type;
    if (!getBE().mHwc->onVsync(displayId, timestamp, &type)) {
        return;
    }

    bool needsHwVsync = false;

    { // Scope for the lock
        Mutex::Autolock _l(mHWVsyncLock);
        if (type == DisplayDevice::DISPLAY_PRIMARY && mPrimaryHWVsyncEnabled) {
            needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
        }
    }

    if (needsHwVsync) {
        enableHardwareVsync();
    } else {
        disableHardwareVsync(false);
    }
}

在这个回调中,会从硬件传来一个时间戳,对整个Vsync进行一次调整。究竟是怎么一个逻辑,我将放在后面的文章来聊。

本文最重要将会关注屏幕热插拔的回调。

HwcHalImpl hotplugHook

    static void hotplugHook(hwc2_callback_data_t callbackData, hwc2_display_t display,
                            int32_t connected) {
        auto hal = static_cast<HwcHalImpl*>(callbackData);
        hal->mEventCallback->onHotplug(display,
                                       static_cast<IComposerCallback::Connection>(connected));
    }

HalEventCallback hotplugHook


        void onHotplug(Display display, IComposerCallback::Connection connected) {
            if (connected == IComposerCallback::Connection::CONNECTED) {
                mResources->addPhysicalDisplay(display);
            } else if (connected == IComposerCallback::Connection::DISCONNECTED) {
                mResources->removeDisplay(display);
            }

            auto ret = mCallback->onHotplug(display, connected);
            ALOGE_IF(!ret.isOk(), "failed to send onHotplug: %s", ret.description().c_str());
        }

能看到在这个过程中就不是简单的回调,而是把回调上来的hwc2_display_t对象添加到ComposerResources中。

ComposerResources 添加Display管理

文件:/hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerResources.h

    Error addPhysicalDisplay(Display display) {
        auto displayResource =
            createDisplayResource(ComposerDisplayResource::DisplayType::PHYSICAL, 0);

        std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
        auto result = mDisplayResources.emplace(display, std::move(displayResource));
        return result.second ? Error::NONE : Error::BAD_DISPLAY;
    }

    virtual std::unique_ptr<ComposerDisplayResource> createDisplayResource(
        ComposerDisplayResource::DisplayType type, uint32_t outputBufferCacheSize) {
        return std::make_unique<ComposerDisplayResource>(type, mImporter, outputBufferCacheSize);
    }

在这个过程中,Composer会把每一个hwc2_display_t最终封装成一个个ComposerDisplayResource,加入到集合当中。

class ComposerDisplayResource {
   public:
    enum class DisplayType {
        PHYSICAL,
        VIRTUAL,
    };

    ComposerDisplayResource(DisplayType type, ComposerHandleImporter& importer,
                            uint32_t outputBufferCacheSize)
        : mType(type),
          mClientTargetCache(importer),
          mOutputBufferCache(importer, ComposerHandleCache::HandleType::BUFFER,
                             outputBufferCacheSize) {}

能看到,此时就是把ComposerResources中的图元申请服务交给ComposerDisplayResource持有,让Display自己拥有控制图元的能力,同时初始化缓存为0.

当缓存好ComposerDisplayResource之后就会继续回调,到顶层

ComposerCallbackBridge onHotplug

    Return<void> onHotplug(Hwc2::Display display,
                           IComposerCallback::Connection conn) override
    {
        HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
        mCallback->onHotplugReceived(mSequenceId, display, connection);
        return Void();
    }

此时终于来到了上一篇文章中解析的SF层中屏幕的初始化流程中。

注意小结

了解了整个回调机制之后,我们能够发现其实registerCallback在Hwc1OnAdapter(也是hwc2_device_t)中做的事情仅仅只是把当前的方法指针和回调类型存储起来,同时让刚注册进来的监听消费掉还没有回调上去的消息。

到这里,整个逻辑链路似乎就断开了。我曾经看源码也苦恼了很久,发现其实真正从硬件回调上来的地方其实是Hwc1OnAdapter::Callback中的回调。他是在hwc_device_t的registerProcs的时候注册进去的。

接下来让我们探索一下他的原理。

hwc_device_t的registerProcs

调用如下:

    mHwc1Device->registerProcs(mHwc1Device,
            static_cast<const hwc_procs_t*>(mHwc1Callbacks.get()));

文件:/hardware/qcom/display/msm8960/libhwcomposer/hwc.cpp

static void hwc_registerProcs(struct hwc_composer_device_1* dev,
                              hwc_procs_t const* procs)
{
    hwc_context_t* ctx = (hwc_context_t*)(dev);
    if(!ctx) {
        return;
    }
    ctx->proc = procs;

    init_uevent_thread(ctx);
    init_vsync_thread(ctx);
}

又一次见到这种设计把hwc_composer_device_1转型成hwc_context_t.

struct hwc_context_t {
    hwc_composer_device_1_t device;
    const hwc_procs_t* proc;

    qhwc::CopyBit *mCopyBit[MAX_DISPLAYS];

    overlay::Overlay *mOverlay;
    overlay::RotMgr *mRotMgr;

    //Primary and external FB updater
    qhwc::IFBUpdate *mFBUpdate[MAX_DISPLAYS];
    // External display related information
    qhwc::ExternalDisplay *mExtDisplay;
    qhwc::MDPInfo mMDP;
    qhwc::VsyncState vstate;
    qhwc::DisplayAttributes dpyAttr[MAX_DISPLAYS];
    qhwc::ListStats listStats[MAX_DISPLAYS];
    qhwc::LayerProp *layerProp[MAX_DISPLAYS];
    qhwc::LayerRotMap *mLayerRotMap[MAX_DISPLAYS];
    qhwc::MDPComp *mMDPComp[MAX_DISPLAYS];
    qhwc::CablProp mCablProp;
    overlay::utils::Whf mPrevWHF[MAX_DISPLAYS];

    //Securing in progress indicator
    bool mSecuring;
    //External Display configuring progress indicator
    bool mExtDispConfiguring;
    //Display in secure mode indicator
    bool mSecureMode;
    //Lock to prevent set from being called while blanking
    mutable Locker mBlankLock;
    //Lock to protect set when detaching external disp
    mutable Locker mExtSetLock;
    //DMA used for rotator
    bool mDMAInUse;
    //MDP rotater needed
    bool mNeedsRotator;
    //Check if base pipe is set up
    bool mBasePipeSetup;
    //Flags the transition of a video session
    bool mVideoTransFlag;
};

能看到这个方法初始化了2个线程,一个是vsync同步信号线程,一个是uevent的事件信号线程。还会把当前的hwc_procs_t这个回调结构体存储起来。

hwc 初始化事件线程

文件:/hardware/qcom/display/msm8960/libhwcomposer/hwc_uevents.cpp

static void *uevent_loop(void *param)
{
    int len = 0;
    static char udata[PAGE_SIZE];
    hwc_context_t * ctx = reinterpret_cast<hwc_context_t *>(param);
    char thread_name[64] = HWC_UEVENT_THREAD_NAME;
    prctl(PR_SET_NAME, (unsigned long) &thread_name, 0, 0, 0);
    setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
    uevent_init();

    while(1) {
        len = uevent_next_event(udata, sizeof(udata) - 2);
        handle_uevent(ctx, udata, len);
    }

    return NULL;
}

void init_uevent_thread(hwc_context_t* ctx)
{
    pthread_t uevent_thread;
    int ret;

    ALOGI("Initializing UEVENT Thread");
    ret = pthread_create(&uevent_thread, NULL, uevent_loop, (void*) ctx);
    if (ret) {
        ALOGE("%s: failed to create %s: %s", __FUNCTION__,
            HWC_UEVENT_THREAD_NAME, strerror(ret));
    }
}

能看到该方法其实就是实例化一个最高优先级的pthread线程,经过一个uevent_init初始化后进入了uevent_loop的循环。关键能看到下面这个死循环:

    while(1) {
        len = uevent_next_event(udata, (int)sizeof(udata) - 2);
        handle_uevent(ctx, udata, len);
    }

uevent_next_event不断的获取下一个需要处理事件,handle_uevent进行处理。

uevent_init

文件: /hardware/libhardware_legacy/uevent.c

int uevent_init()
{
    struct sockaddr_nl addr;
    int sz = 64*1024;
    int s;

    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    addr.nl_groups = 0xffffffff;

    s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if(s < 0)
        return 0;

    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));

    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        close(s);
        return 0;
    }

    fd = s;
    return (fd > 0);
}

能看到在msm8994中event的初始化其实就是一个socket服务端。这个socket设置协议族为SOCK_DGRAM也就是面向数据包的协议。同时设置了该socket是接收端,还设置了bind绑定了地址,并且回调了socket中的fd。

uevent_next_event 获取需要处理的事件

文件: /hardware/libhardware_legacy/uevent.c

int uevent_next_event(char* buffer, int buffer_length)
{
    while (1) {
        struct pollfd fds;
        int nr;
    
        fds.fd = fd;
        fds.events = POLLIN;
        fds.revents = 0;
        nr = poll(&fds, 1, -1);
     
        if(nr > 0 && (fds.revents & POLLIN)) {
            int count = recv(fd, buffer, buffer_length, 0);
            if (count > 0) {
                struct uevent_handler *h;
                pthread_mutex_lock(&uevent_handler_list_lock);
                LIST_FOREACH(h, &uevent_handler_list, list)
                    h->handler(h->handler_data, buffer, buffer_length);
                pthread_mutex_unlock(&uevent_handler_list_lock);

                return count;
            } 
        }
    }
    
    // won't get here
    return 0;
}

其核心方法就是调用了poll系统调用,监听socket文件描述符中数据流的变化。一旦nr大于0,说明又说来了,就调用recv从socket中读取数据,回去遍历查找已经通过uevent_add_native_handler添加进来的回调,并返回发生变化的数量。

然而这里面并没有通过uevent_add_native_handler进来一个回调。而是直接交给下一个函数处理。

handle_uevent

文件:/hardware/qcom/display/msm8960/libhwcomposer/hwc_uevents.cpp

此时用过uevent_next_event已经把数据拷贝到uevent_data中,就开始解析数据

enum {
    EXTERNAL_OFFLINE = 0,
    EXTERNAL_ONLINE,
    EXTERNAL_PAUSE,
    EXTERNAL_RESUME
};


static void handle_uevent(hwc_context_t* ctx, const char* udata, int len)
{
    const char *str = udata;
    bool usecopybit = false;
    int compositionType =
        qdutils::QCCompositionType::getInstance().getCompositionType();

    if (compositionType & (qdutils::COMPOSITION_TYPE_DYN |
                           qdutils::COMPOSITION_TYPE_MDP |
                           qdutils::COMPOSITION_TYPE_C2D)) {
        usecopybit = true;
    }

    if(!strcasestr("change@/devices/virtual/switch/hdmi", str) &&
       !strcasestr("change@/devices/virtual/switch/wfd", str)) {
        return;
    }
    int connected = -1; // initial value - will be set to  1/0 based on hotplug
    int extDpyNum = HWC_DISPLAY_EXTERNAL;
    char property[PROPERTY_VALUE_MAX];
    if((property_get("persist.sys.wfd.virtual", property, NULL) > 0) &&
            (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
             (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
        // This means we are using Google API to trigger WFD Display
        extDpyNum = HWC_DISPLAY_VIRTUAL;

    }

    int dpy = isHDMI(str) ? HWC_DISPLAY_EXTERNAL : extDpyNum;

    // update extDpyNum
    ctx->mExtDisplay->setExtDpyNum(dpy);

    while(*str) {
        if (!strncmp(str, "SWITCH_STATE=", strlen("SWITCH_STATE="))) {
            connected = atoi(str + strlen("SWITCH_STATE="));
            //Disabled until SF calls unblank
            ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isActive = false;
            //Ignored for Virtual Displays
            //ToDo: we can do this in a much better way
            ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = true;
            break;
        }
        str += strlen(str) + 1;
        if (str - udata >= len)
            break;
    }

    switch(connected) {
        case EXTERNAL_OFFLINE:
            {   // disconnect event
                ctx->mExtDisplay->processUEventOffline(udata);
                if(ctx->mFBUpdate[dpy]) {
                    Locker::Autolock _l(ctx->mExtSetLock);
                    delete ctx->mFBUpdate[dpy];
                    ctx->mFBUpdate[dpy] = NULL;
                }
                if(ctx->mCopyBit[dpy]){
                    Locker::Autolock _l(ctx->mExtSetLock);
                    delete ctx->mCopyBit[dpy];
                    ctx->mCopyBit[dpy] = NULL;
                }
                if(ctx->mMDPComp[dpy]) {
                    delete ctx->mMDPComp[dpy];
                    ctx->mMDPComp[dpy] = NULL;
                }
                ctx->dpyAttr[dpy].connected = false;
                Locker::Autolock _l(ctx->mExtSetLock);
                //hwc comp could be on
                ctx->proc->hotplug(ctx->proc, dpy, connected);
                break;
            }
        case EXTERNAL_ONLINE:
            {   // connect case
                ctx->mExtDispConfiguring = true;
                ctx->mExtDisplay->processUEventOnline(udata);
                ctx->mFBUpdate[dpy] =
                        IFBUpdate::getObject(ctx->dpyAttr[dpy].xres, dpy);
                ctx->dpyAttr[dpy].isPause = false;
                if(usecopybit)
                    ctx->mCopyBit[dpy] = new CopyBit();
                ctx->mMDPComp[dpy] =  MDPComp::getObject(
                        ctx->dpyAttr[dpy].xres, dpy);
                ctx->dpyAttr[dpy].connected = true;
                Locker::Autolock _l(ctx->mExtSetLock); //hwc comp could be on
                ctx->proc->hotplug(ctx->proc, dpy, connected);
                break;
            }
        case EXTERNAL_PAUSE:
            {   // pause case
                ctx->mExtDispConfiguring = true;
                ctx->dpyAttr[dpy].isActive = true;
                ctx->dpyAttr[dpy].isPause = true;
                break;
            }
        case EXTERNAL_RESUME:
            {  // resume case
                ctx->dpyAttr[dpy].isActive = true;
                ctx->dpyAttr[dpy].isPause = false;
                break;
            }
        default:
           ...
    }
}

其实这里表达的观点很简单,首先它只解析以"change@/devices/virtual/switch/hdmi"或者"change@/devices/virtual/switch/wfd"开头的消息。这种消息的格式如下:

change@/devices/virtual/switch/hdmi ACTION=change
SWITCH_STATE=0

需要判断的类型就是SWITCH_STATE对应的字符的int是什么进行响应的处理。有4个状态:

  • 1.EXTERNAL_OFFLINE 0
  • 2.EXTERNAL_ONLINE 1
  • 3.EXTERNAL_PAUSE 2
  • 4.EXTERNAL_RESUME 3

同时dpy代表屏幕类型,如果是change@/devices/virtual/switch/hdmi消息来了,说明是物理屏幕HWC_DISPLAY_EXTERNAL

当为EXTERNAL_OFFLINE 的时候就代表屏幕被关闭或者拔出。先调用 qhwc::ExternalDisplay->processUEventOffline,销毁保存在数组中数据,并让dpyAttr对应的id变成false,最后调用hotPlugin回调

EXTERNAL_ONLINE 代表屏幕联通了。先调用ExternalDisplay的processUEventOnline,申请资源,最后调用hotPlugin回调

EXTERNAL_PAUSE 屏幕冻结了,设置dpyAttr标志位

EXTERNAL_RESUME 屏幕恢复了,设置dpyAttr标志位

暂时理解这么多即可。

vysnc线程初始化

文件:/hardware/qcom/display/msm8960/libhwcomposer/hwc_vsync.cpp

void init_vsync_thread(hwc_context_t* ctx)
{
    int ret;
    pthread_t vsync_thread;
    ret = pthread_create(&vsync_thread, NULL, vsync_loop, (void*) ctx);
...
}
static void *vsync_loop(void *param)
{
    const char* vsync_timestamp_fb0 = "/sys/class/graphics/fb0/vsync_event";
    const char* vsync_timestamp_fb1 = "/sys/class/graphics/fb1/vsync_event";
    int dpy = HWC_DISPLAY_PRIMARY;

    hwc_context_t * ctx = reinterpret_cast<hwc_context_t *>(param);

    char thread_name[64] = HWC_VSYNC_THREAD_NAME;
    prctl(PR_SET_NAME, (unsigned long) &thread_name, 0, 0, 0);
    setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY +
                android::PRIORITY_MORE_FAVORABLE);

    const int MAX_DATA = 64;
    static char vdata[MAX_DATA];

    uint64_t cur_timestamp=0;
    ssize_t len = -1;
    int fd_timestamp = -1;
    bool fb1_vsync = false;
    bool logvsync = false;

    char property[PROPERTY_VALUE_MAX];
    if(property_get("debug.hwc.fakevsync", property, NULL) > 0) {
        if(atoi(property) == 1)
            ctx->vstate.fakevsync = true;
    }

    if(property_get("debug.hwc.logvsync", property, 0) > 0) {
        if(atoi(property) == 1)
            logvsync = true;
    }

    fd_timestamp = open(vsync_timestamp_fb0, O_RDONLY);
    if (fd_timestamp < 0) {
        ...
        ctx->vstate.fakevsync = true;
    }

    do {
        if (LIKELY(!ctx->vstate.fakevsync)) {
            len = pread(fd_timestamp, vdata, MAX_DATA, 0);
            if (len < 0) {
               ...
                continue;
            }
            // extract timestamp
            const char *str = vdata;
            if (!strncmp(str, "VSYNC=", strlen("VSYNC="))) {
                cur_timestamp = strtoull(str + strlen("VSYNC="), NULL, 0);
            }
        } else {
            usleep(16666);
            cur_timestamp = systemTime();
        }
        // send timestamp to HAL
        if(ctx->vstate.enable) {
            ctx->proc->vsync(ctx->proc, dpy, cur_timestamp);
        }

    } while (true);
    if(fd_timestamp >= 0)
        close (fd_timestamp);

    return NULL;
}

其核心原理十分简单,也是进入到一个死循环。该循环会读取/sys/class/graphics/fb0/vsync_event下的驱动文件,如果失败说明没有硬件驱动,那就使用软件模拟,能看到这里面就会沉睡16.66毫秒。

如果成功则读取里面的数据,一般为如下格式:

VSYNC=41800875994
获取后面的数值,最后把该时间顶层回调即可。

本文对vysnc同步的信号的探索到这里,之后会结合整个SF聊聊它的原理。

回调到HWC2On1Adapter::Callbacks

我们现在暂时只关注一个屏幕的热插拔逻辑。

        static void hotplugHook(const hwc_procs_t* procs, int display,
                int connected) {
            auto callbacks = static_cast<const Callbacks*>(procs);
            callbacks->mAdapter.hwc1Hotplug(display, connected);
        }
void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected) {
    ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected);

    if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) {
        return;
    }

    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);

    if (mCallbacks.count(Callback::Hotplug) == 0) {
        mPendingHotplugs.emplace_back(hwc1DisplayId, connected);
        return;
    }

    hwc2_display_t displayId = UINT64_MAX;
    if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
        if (connected == 0) {
            ALOGW("hwc1Hotplug: Received disconnect for unconnected display");
            return;
        }

        auto display = std::make_shared<HWC2On1Adapter::Display>(*this,
                HWC2::DisplayType::Physical);
        display->setHwc1Id(HWC_DISPLAY_EXTERNAL);
        display->populateConfigs();
        displayId = display->getId();
        mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId;
        mDisplays.emplace(displayId, std::move(display));
    } else {
        if (connected != 0) {
            ALOGW("hwc1Hotplug: Received connect for previously connected "
                    "display");
            return;
        }

        displayId = mHwc1DisplayMap[hwc1DisplayId];
        mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL);
        mDisplays.erase(displayId);
    }

    const auto& callbackInfo = mCallbacks[Callback::Hotplug];


    lock.unlock();

    auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackInfo.pointer);
    auto hwc2Connected = (connected == 0) ?
            HWC2::Connection::Disconnected : HWC2::Connection::Connected;
    hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected));
}

如果没有注册热插拔的监听,则会保存到mPendingHotplugs集合中,等待回调监听。

该回调只处理物理屏幕的逻辑。如果connect是0且mHwc1DisplayMap大于0说明有屏幕链接过,现在断开链接了,则从mHwc1DisplayMap中销毁对应id,销毁mDisplays对应的屏幕对象对象。

如果connect为1,说明有屏幕链接进来了。此时会生成一个HWC2On1Adapter::Display对象保存mDisplays。最后就是走上面registerCallback之上的逻辑了。

总结

重新梳理一遍:

  • Composer其实最重要的行为就是createClient,创建一个ComposerClient,ComposerClient此时真正的逻辑核心。
  • ComposerClient会持有HwcHalImpl,该类因为持有这hw2_device_t,所以拥有了和硬件通信的能力。ComposerResource中持有ComposerInputHandler这个对象负责图元服务的控制。ComposerCommandEngine则是处理一些来自上层的命令。
  • HWC2On1Adapter则是为了适配hw_device_t和版本2之间的区别,同时注册一个监听到驱动中。
  • 对于回调来讲,分为两部分,当registerCallback的时候会消耗从底层回调上来。另一部分则是从底层直接拿到对应的HwcHalImpl的hook函数向上回调。

原理如图:


ComposerCallback.png

了解Hal层的运作原理,为之后的逻辑打下基础。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349