CameraProvider启动流程分析

CameraProvider进程中hidl文件

HIDL文件 服务端 客户端 BinderName
ICameraProvider.hidl CameraProvider进程(CameraProvider.cpp) CameraService进程中使用(CameraProviderManager:: ProviderInfo) legacy/0
ICameraDevice.hidl CameraProvder进程中(CameraDevice.cpp) CameraService进程中使用 匿名Binder
ICameraDeviceCallback.hal CameraService进程 CameraProvider进程中CameraDevice.cpp在open()函数中作为参数传递进来 进程间的回调
ICameraDeviceSession.hal CameraProvider进程 CameraDeviceSession.cpp 在CameraService进程打开摄像头流程中CameraProviderManager::openSession获取 匿名Binder
type.hal CameraProvider进程

CameraProvider进程启动时序图

CameraProvider 启动时序图.png

CameraProvider 进程类图(后续会更新)

CameraProvider进程类图.png

CameraProvider进程的启动流程

1、CameraProvider是Camera的Hal层程序(android.hardware.camera.provider@2.4-service )。这是一个独立的进程,它在系统启动的时候就会启动。它通过HIDL机制来和native层的CameraService进程进行通信。
它的启动由rc文件控制

//hardware/interfaces/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service.rc
service vendor.camera-provider-2-4 /vendor/bin/hw/android.hardware.camera.provider@2.4-service
    class hal
    user cameraserver
    group audio camera input drmrpc
    ioprio rt 4
    capabilities SYS_NICE
    writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks

1.程序的入口Service.cpp

#define LOG_TAG "android.hardware.camera.provider@2.4-service"

#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
#include <hidl/LegacySupport.h>

#include <binder/ProcessState.h>

using android::hardware::camera::provider::V2_4::ICameraProvider;
using android::hardware::defaultPassthroughServiceImplementation;

int main()
{
    ALOGI("Camera provider Service is starting.");
    // The camera HAL may communicate to other vendor components via
    // /dev/vndbinder
    android::ProcessState::initWithDriver("/dev/vndbinder");
    // 在LegacySupport.h中defaultPassthroughServiceImplementation为模板类函数,将会通过 sp<ICameraProvider> service = 
    // ICameraProvider::getService(name, true /* getStub */) 获取 CameraProvider 实例化对象,以上操作,将会进入 CameraProviderAll.cpp。
    return defaultPassthroughServiceImplementation<ICameraProvider>("legacy/0", /*maxThreads*/ 6);
}

这里ICameraProvider.hal 会生成 ICameraProvider.h BpHwCameraProvider.h BnHwCameraProvider.h以及CameraProviderAll.cpp文件。 其中CameraProviderAll.cpp是上面上个头文件的具体内容。
然后这里的defaultPassthroughServiceImplementation()是模板函数,会调用到CameraProviderAll.cpp里面(这里使用的是HIDL直通式,所以会调用到ICameraProvider对应的CameraProviderALL.cpp中的getService()中,这个相当于是一种固定模式

#CameraProviderAll.cpp:

//这里传递过来的  serviceName--"legacy/0"   getStub--true (defaultPassthroughServiceImplementation()过来的流程)
::android::sp<ICameraProvider> ICameraProvider::getService(const std::string &serviceName, const bool getStub) {
    return ::android::hardware::details::getServiceInternal<BpHwCameraProvider>(serviceName, true, getStub);
}


-------------------------------------------------------------------------------------------------------------------------------
#  system/libhidl/transport/include/hidl/HidlTransportSupport.h

template <typename BpType, typename IType = typename BpType::Pure,
          typename = std::enable_if_t<std::is_same<i_tag, typename IType::_hidl_tag>::value>,
          typename = std::enable_if_t<std::is_same<bphw_tag, typename BpType::_hidl_tag>::value>>
// instatnce  这里是传递来的 "legacy/0"  或者 "external/0"  retry--true getStub--false
sp<IType> getServiceInternal(const std::string& instance, bool retry, bool getStub) {
    using ::android::hidl::base::V1_0::IBase;
    //IType::descriptor --- ICameraProvider::descriptor("android.hardware.camera.provider@2.4::ICameraProvider")
    sp<IBase> base = getRawServiceInternal(IType::descriptor, instance, retry, getStub);
    if (base == nullptr) {
        return nullptr;
    }
    if (base->isRemote()) {
        // getRawServiceInternal guarantees we get the proper class
        return sp<IType>(new BpType(toBinder<IBase>(base)));
    }
    return IType::castFrom(base);
}

---------------------------------------------------------------------------------------------------------------------------------------------
#  system/libhidl/transport/Servicemanagement.cpp

sp<::android::hidl::base::V1_0::IBase> getRawServiceInternal(const std::string& descriptor,const std::string& instance, bool retry, bool getStub) {
    using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport;
    using ::android::hidl::base::V1_0::IBase;
    using ::android::hidl::manager::V1_0::IServiceManager;
    sp<Waiter> waiter;
    // 获取 hwservicemanager 服务, 用于获取Service的client端即BpXXX代理类
    const sp<IServiceManager1_1> sm = defaultServiceManager1_1();
    if (sm == nullptr) {
        ALOGE("getService: defaultServiceManager() is null");
        return nullptr;
    }
    Return<Transport> transportRet = sm->getTransport(descriptor, instance);
    if (!transportRet.isOk()) {
        ALOGE("getService: defaultServiceManager()->getTransport returns %s", transportRet.description().c_str());
        return nullptr;
    }
    Transport transport = transportRet;
    const bool vintfHwbinder = (transport == Transport::HWBINDER);
    const bool vintfPassthru = (transport == Transport::PASSTHROUGH);
   
    、、、、、、   
   //此处省略的部分代码 主要是 getStub为false的情况 因为CameraProvider的启动流程中的getStub的值为true所以暂不分析

    if (getStub || vintfPassthru || vintfLegacy) {
        const sp<IServiceManager> pm = getPassthroughServiceManager();  //获取HIDL直通式服务管理对象
        if (pm != nullptr) {
            sp<IBase> base = pm->get(descriptor, instance).withDefault(nullptr); //然后调用到了该服务对象中的get函数
            if (!getStub || trebleTestingOverride) {
                base = wrapPassthrough(base);
            }
            return base;
        }
    }
    return nullptr;
}

--------------------------------------------------------------------------------------------------------------------------------------------
#  上面的pm->get(descriptor, instance)会调用到如下函数
#  system/libhidl/transport/Servicemanagement.cpp

//这里的fqName--android.hardware.camera.provider@2.4::ICameraProvider   name-- legacy/0
Return<sp<IBase>> get(const hidl_string& fqName,const hidl_string& name) override {
        sp<IBase> ret = nullptr;
        //此处为Lambda表达式,简单理解为函数指针即可,先执行 openLibs() 其中第一个参数就是fqName,第二个参数是回调函数。
        // 回调函数中的  handle--dlopen()的返回值   lib--android.hardware.camera.provider@2.4-impl.so  sym--HIDL_FETCH_ICameraProvider
        openLibs(fqName, [&](void* handle, const std::string &lib, const std::string &sym) {
            //下面是回调函数的内部逻辑
            // 声明一个函数
            IBase* (*generator)(const char* name);
            //返回 HIDL_FETCH_ICameraProvider() 函数对应的函数地址作为上面声明的函数的具体内容
            *(void **)(&generator) = dlsym(handle, sym.c_str());
            if(!generator) {
                const char* error = dlerror();
                LOG(ERROR) << "Passthrough lookup opened " << lib<< " but could not find symbol " << sym << ": "<< (error == nullptr ? "unknown error" : error);
                dlclose(handle);
                return true;
            }
            // 执行HIDL_FETCH_ICameraProvider()函数 ret作为返回值 其中ret为ICameraProvider对象
            ret = (*generator)(name.c_str());
            if (ret == nullptr) {
                dlclose(handle);
                return true; // this module doesn't provide this instance name
            }
            // Actual fqname might be a subclass.
            // This assumption is tested in vts_treble_vintf_test
            using ::android::hardware::details::getDescriptor;
            std::string actualFqName = getDescriptor(ret.get());
            CHECK(actualFqName.size() > 0);
            registerReference(actualFqName, name);  //进行服务端的注册,这一块暂时不做深入解析
            return false;
        });
        return ret;
    }

2、HIDL直通式后会进入到CameraProvider.cpp里面
上面HIDL相关的代码执行完成后会调用到CameraProvider对象里面的HIDL_FETCH_ICameraProvider()函数。在这个函数里面的参数name 的值就是之前的程序入口处传递来的 "legacy/0" 。接下来我们继续走到了CameraProvider.cpp里面看看。

ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name) { //这里name  "legacy/0"
    if (strcmp(name, kLegacyProviderName) == 0) {   //kLegacyProviderName是一个值为 "legacy/0"的常量
        //调用待构造函数 创建对象  构造函数里面会调用到内部的initialize()函数来初始化
        //并将初始化的结果保存到mInitFailed的全局变量中
        CameraProvider* provider = new CameraProvider();
        if (provider == nullptr) {
            ALOGE("%s: cannot allocate camera provider!", __FUNCTION__);
            return nullptr;
        }
       //
        if (provider->isInitFailed()) { //这里就是获取的mInitFailed
            ALOGE("%s: camera provider init failed!", __FUNCTION__);
            delete provider;
            return nullptr;
        }
        return provider;
    } else if (strcmp(name, kExternalProviderName) == 0) {
        ExternalCameraProvider* provider = new ExternalCameraProvider();
        return provider;
    }
    ALOGE("%s: unknown instance name: %s", __FUNCTION__, name);
    return nullptr;
}

3、CameraProvider的构造函数和初始化函数--initialize()
然后调用到了CameraProvider的构造函数,在构造函数里面只有一句代码: mInitFailed = initialize() 这样就会调用到内部的initialize()函数,进行初始化,并将初始化结果存储到全局变量mInitFailed(标识是否已经初始化了)。接下来我们看看initialize()函数主要是做什么:

CameraProvider::CameraProvider() :
        camera_module_callbacks_t({sCameraDeviceStatusChange,
                                   sTorchModeStatusChange}) {
    mInitFailed = initialize();
}

--------------------------------------------------------------------------------------------------------------------------------------------------
bool CameraProvider::initialize() {
    camera_module_t *rawModule;   //这里的camera_module_t 是一个结构体,它继承了 hw_module_t的结构体。这里就是硬件抽象的基本形式了。
    //加载对应的so库(camera.default.so) 给camera_module_t对象赋值
    int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,(const hw_module_t **)&rawModule);
    if (err < 0) {
        ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
        return true;
    }
    // rawModule 将指向 HAL 中的 camera_module_t 类型结构体
    //  此时,CameraProvider 与 camera HAL 绑定成功,可以通过CameraProvider操作camera HAL
    // 创建了一个CameraModule.cpp:android/hardware/interfaces/camera/common/1.0/default对象
    // 并将该对象赋值给全局变量mModule保存起来(这里provider调用mModule对象然后调用到CameraModule
    // 内部的camera_module_t来操作cameraHAL)
    mModule = new CameraModule(rawModule);
    // init()里面会调用 cameraHAL的init()函数,然后会获取camera number 并存放在CameraModule中的mCameraInfoMap 里面
    err = mModule->init();
    if (err != OK) {
        ALOGE("Could not initialize camera HAL module: %d (%s)", err, strerror(-err));
        mModule.clear();
        return true;
    }
    ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());
    // Setup vendor tags here so HAL can setup vendor keys in camera characteristics
    VendorTagDescriptor::clearGlobalVendorTagDescriptor();
    if (!setUpVendorTags()) {
        ALOGE("%s: Vendor tag setup failed, will not be available.", __FUNCTION__);
    }
    // 这里是设置camera_module_callbacks_t的回调,会在当前对象接收这些回调传递的数据
    err = mModule->setCallbacks(this);
    if (err != OK) {
        ALOGE("Could not set camera module callback: %d (%s)", err, strerror(-err));
        mModule.clear();
        return true;
    }
    // 从系统属性中获取HAL3的 minor的值
    mPreferredHal3MinorVersion =property_get_int32("ro.vendor.camera.wrapper.hal3TrebleMinorVersion", 3);
    ALOGV("Preferred HAL 3 minor version is %d", mPreferredHal3MinorVersion);
    switch(mPreferredHal3MinorVersion) {
        case 2:
        case 3:
            // OK
            break;
        default:
            ALOGW("Unknown minor camera device HAL version %d in property "
                    "'camera.wrapper.hal3TrebleMinorVersion', defaulting to 3", mPreferredHal3MinorVersion);
            mPreferredHal3MinorVersion = 3;
    }
    // 获取CameraModule中存放的摄像头数量信息
    mNumberOfLegacyCameras = mModule->getNumberOfCameras();
    for (int i = 0; i < mNumberOfLegacyCameras; i++) {
        struct camera_info info;
        auto rc = mModule->getCameraInfo(i, &info);//获取摄像头的基本属性信息
        if (rc != NO_ERROR) {
            ALOGE("%s: Camera info query failed!", __func__);
            mModule.clear();
            return true;
        }
        if (checkCameraVersion(i, info) != OK) {
            ALOGE("%s: Camera version check failed!", __func__);
            mModule.clear();
            return true;
        }
        char cameraId[kMaxCameraIdLen];
        snprintf(cameraId, sizeof(cameraId), "%d", i);
        std::string cameraIdStr(cameraId);
        mCameraStatusMap[cameraIdStr] = CAMERA_DEVICE_STATUS_PRESENT;
        addDeviceNames(i);
    }
    return false; // mInitFailed
}

CameraProvider::initialize()中首先就是会调用hw_get_module()(这里会接触到一些Android硬件抽象化的一些基础知识),最终会调用到hardware.c中的load()函数里面主要是加载so库(这个一般是厂家提供的对摄像头具体操作的封装,也就是对camera_module中的一些方法的具体实现)。
通过hw_get_module() 会得到rawModule对象(可以通过这个rawModule对摄像头进行一些操作)。然后将rawModule传递到CameraModule里面保存(CameraModule::mModule)起来,然后会调用到上面的camera_module_t中的init()函数。
mModule->setCallbacks(this)会将camera_module_callbacks_t中的函数回调到当前对象CameraProvider中
下面是涉及到的一些结构体的代码:

typedef struct camera_module_callbacks {
    void (*camera_device_status_change)(const struct camera_module_callbacks*,int camera_id, int new_status);
    void (*torch_mode_status_change)(const struct camera_module_callbacks*,const char* camera_id,int new_status);
} camera_module_callbacks_t;


typedef struct camera_module {
    hw_module_t common;
    int (*get_number_of_cameras)(void);
    int (*get_camera_info)(int camera_id, struct camera_info *info);
    int (*set_callbacks)(const camera_module_callbacks_t *callbacks);
    void (*get_vendor_tag_ops)(vendor_tag_ops_t* ops);
    int (*open_legacy)(const struct hw_module_t* module, const char* id,
            uint32_t halVersion, struct hw_device_t** device);
    int (*set_torch_mode)(const char* camera_id, bool enabled);
    int (*init)();
    void* reserved[5];
} camera_module_t


typedef struct camera_info {
    int facing;
    int orientation;
    uint32_t device_version;
    const camera_metadata_t *static_camera_characteristics;
    int resource_cost; //当前摄像头使用所消耗的资源
    char** conflicting_devices;  //标识当前的摄像头和那些摄像头冲突
    size_t conflicting_devices_length;
} camera_info_t;

4、mModule->getCameraInfo(i, &info)获取所有摄像头的基本信息并保存在本地
接下来会根据hal层返回的摄像头数量遍历获取每个摄像头的信息,并根据这些信息对每个摄像头进行检查。这个里面会调用到CameraModule::getCameraInfo(int cameraId, struct camera_info *info) ,在CameraModule中主要是通过前面的camera_module_t中的get_camera_info()函数获取摄像头信息最后将摄像头信息保存到 CameraModule::mCameraInfoMap

int CameraModule::getCameraInfo(int cameraId, struct camera_info *info) {
    ATRACE_CALL();
    Mutex::Autolock lock(mCameraInfoLock);
    if (cameraId < 0) {
        ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
        return -EINVAL;
    }
    // 获取摄像头信息 如果是API 2.0以前的就不保存
    int apiVersion = mModule->common.module_api_version;
    if (apiVersion < CAMERA_MODULE_API_VERSION_2_0) {
        int ret;
        ATRACE_BEGIN("camera_module->get_camera_info");
        ret = mModule->get_camera_info(cameraId, info);
        // Fill in this so CameraService won't be confused by
        // possibly 0 device_version
        info->device_version = CAMERA_DEVICE_API_VERSION_1_0;
        ATRACE_END();
        return ret;
    }
    //API是2.0以及之后的就需要将摄像头信息保存起来
    ssize_t index = mCameraInfoMap.indexOfKey(cameraId);
    if (index == NAME_NOT_FOUND) {
        // Get camera info from raw module and cache it
        camera_info rawInfo, cameraInfo;
        ATRACE_BEGIN("camera_module->get_camera_info");
        int ret = mModule->get_camera_info(cameraId, &rawInfo);
        ATRACE_END();
        if (ret != 0) {
            return ret;
        }
        int deviceVersion = rawInfo.device_version;
        if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_0) {
            // static_camera_characteristics is invalid
            *info = rawInfo;
            return ret;
        }
        CameraMetadata m;
        m.append(rawInfo.static_camera_characteristics);
        deriveCameraCharacteristicsKeys(rawInfo.device_version, m);
        cameraInfo = rawInfo;
        cameraInfo.static_camera_characteristics = m.release();
        index = mCameraInfoMap.add(cameraId, cameraInfo);
    }
    assert(index != NAME_NOT_FOUND);
    // return the cached camera info
    *info = mCameraInfoMap[index]; //将摄像头信息返回上个流程的函数进行检查
    return OK;
}

5、addDeviceNames(i) 添加摄像头设备到本地
继续CameraProvider::init()函数中的流程的最后一步 addDeviceName(i)。这个会执行到当前对象的
addDeviceNames(int camera_id, CameraDeviceStatus status = CameraDeviceStatus::PRESENT,bool cam_new = false)函数中:

/status的默认值CameraDeviceStatus::PRESENT  cam_new的默认值false
void CameraProvider::addDeviceNames(int camera_id, CameraDeviceStatus status, bool cam_new)
{
    char cameraId[kMaxCameraIdLen];
    snprintf(cameraId, sizeof(cameraId), "%d", camera_id);
    std::string cameraIdStr(cameraId);

    mCameraIds.add(cameraIdStr);   //将camera_id 添加到全局的mCameraIds列表中

     //给 mCameraDeviceNames 和 mOpenLegacySupported 这两个集合赋值
    mOpenLegacySupported[cameraIdStr] = false;
    int deviceVersion = mModule->getDeviceVersion(camera_id);
    // pair可以将两个值视为一个单元。容器类别map和multimap就是使用pairs来管理其健值/实值(key/value)的成对元素。可以参考Java中的实体Bean理解
    // 无需写出型别, 就可以生成一个pair对象 这里的就是std::pair<std::string, std::string>
    auto deviceNamePair = std::make_pair(cameraIdStr, getHidlDeviceName(cameraIdStr, deviceVersion));
    mCameraDeviceNames.add(deviceNamePair);
    if (cam_new) {
        mCallbacks->cameraDeviceStatusChange(deviceNamePair.second, status);
    }
    if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_2 &&
            mModule->isOpenLegacyDefined()) {
        // try open_legacy to see if it actually works
        struct hw_device_t* halDev = nullptr;
        int ret = mModule->openLegacy(cameraId, CAMERA_DEVICE_API_VERSION_1_0, &halDev);
        if (ret == 0) {
            mOpenLegacySupported[cameraIdStr] = true;
            halDev->close(halDev);
            deviceNamePair = std::make_pair(cameraIdStr,
                            getHidlDeviceName(cameraIdStr, CAMERA_DEVICE_API_VERSION_1_0));
            mCameraDeviceNames.add(deviceNamePair);
            if (cam_new) {
                mCallbacks->cameraDeviceStatusChange(deviceNamePair.second, status);
            }
        } else if (ret == -EBUSY || ret == -EUSERS) {
            // Looks like this provider instance is not initialized during
            // system startup and there are other camera users already.
            // Not a good sign but not fatal.
            ALOGW("%s: open_legacy try failed!", __FUNCTION__);
        }
    }
}

这个函数主要就是给下面的三个集合赋值
mCameraIds :SortedVector<std::string> 存储的是摄像头的ID 如 : "1" "2"
mCameraDeviceNames :SortedVector<std::pair<std::string, std::string>> 存储的是一个 (cameraId string, hidl device name) pairs 对应ID的摄像头的设备名称
mOpenLegacySupported : std::map<std::string, bool> 存储的是对应ID的摄像头是否支持 HAL1.0(也就是老版本API)

总结:

整体流程就是这样的。
service.cpp ---> ICameraProvider.cpp --> CameraProviderAll.cpp --> HidlTransportSupport.h --> Servicemanagement.cpp --> CameraProvider.cpp --> CameraModule -->CameraHAL.so
发现CameraProvider进程主要有两个作用1、给CameraService进程接口以及主动回调信息给CameraService进程。2、加载厂商提供的CameraHAL的so库,然后获取摄像头信息和操作摄像头。
和CameraService进程通信
所以CameraProvider进程主要是使用了HIDL直通式的当时注册了ICameraProvider.hal中的服务端,这个是用来给CameraService进程来主动调用的。然后这ICameraProvider.hal里面会有setCallback的函数主要是通过回调的形式让CameraService进程接收CameraProvider进程主动发送的消息。
加载CameraHAL的so库
在初始化的时候就通过hw_module_get() 加载对应的so库,获取到camera_module_t对象之后赋值给CameraModule,然后在CameraModule中通过camera_module_t获取摄像头的信息以及后续对摄像头进行操作。

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

推荐阅读更多精彩内容