Android手机NFC POS的EMV设计框架(HIDL实现)

HIDL背景

Treble 是 Google Android 团队的一项重大项目,意在 Android 操作系统框架在架构方面的一项重大改变,旨在让制造商以更低的成本更轻松、更快速地将设备更新到新版 Android 系统。Android 7.x 及更早版本中没有正式的供应商接口,因此设备制造商必须更新大量 Android 代码才能将设备更新到新版 Android 系统。

Android O以后,Treble 提供了一个稳定的新供应商接口,供设备制造商访问 Android 代码中特定于硬件的部分,这样一来,设备制造商只需更新 Android 操作系统框架,即可跳过芯片制造商直接提供新的 Android 版本。

HIDL简介

HIDL 是用于指定 HAL 与其用户之间接口的一个接口描述语言(Interface Description Language),它允许将指定的类型与函数调用收集到接口(Interface)和包(Package)中。更广泛地说,HIDL 是一个可以让那些独立编译的代码库(Libraries)之间进行通信的系统。 HIDL 实际上是用于进行进程间通信(Inter-process Communication,IPC)的。进程间的通信可以称为 Binder 化(Binderized)。对于必须连接到进程的库,也可以使用 passthough 模式(但在Java中不支持)。 HIDL 将指定的数据结构与方法签名组织到接口中,这些接口又会被收集到包中以供使用。它的语法与 C++、JAVA 是类似的,不过关键字集合不尽相同。其注释风格与 JAVA 是一致的。

HIDL设计

设计 HIDL 这个机制的目的,主要是想把框架(framework)与 HAL 进行隔离,使得框架部分可以直接被覆盖、更新,而不需要重新对 HAL 进行编译。HAL 的部分将会放在设备的 /vendor 分区中,并且是由设备供应商(vendors)或 SOC 制造商来构建。这使得框架部分可以通过 OTA 方式更新,同时不需要重新编译 HAL。

直通式HAL( Passthrough 模式)和 绑定式 HAL (Binderized模式)

为了将以往设备的 Android 版本更新到 Android O,开发者需要将传统的 HAL 封装到新的 HIDL 接口中,这个接口为 HAL 提供了 Binder 化以及 Passthrough 模式。这个封装过程对 HAL 以及 Android Framework 都是透明的。
Passthrough 模式仅对 C++ 客户端与实现适用,以往的 Android 版本设备中,HAL 不会采用 JAVA 语言来写,所以 JAVA HAL 必然是 Binder 化的。

绑定式 HAL。以 HAL 接口定义语言 (HIDL) 表示的 HAL。这些 HAL 取代了早期 Android 版本中使用的传统 HAL 和旧版 HAL。在绑定式 HAL 中,Android 框架和 HAL 之间通过 Binder 进程间通信 (IPC) 调用进行通信。所有在推出时即搭载了 Android 8.0 或后续版本的设备都必须只支持绑定式 HAL。

下面介绍HIDL的实现流程:

一、编译环境

1. 下载AOSP源码
android 8.1.0_r52 OPM7.181205.001
2. 编译代码:
source build/envset.sh
lunch ,选择一个对应手机的编译项
make
3、hidl-gen工具已经安装,安装命令

make hidl-gen
4、调试平台
Nexus 5X手机

二、设计实现

NFC POS实现设计在HAL层,隔离framework框架,HAL层实现EMV Level one和EMV Level two。本文实现EMV Level 2为例:

1、创建接口文件
在hardware/interfaces/目录下新建nfcEMV/1.0目录,并在1.0目录中创建接口INfcEMV.hal。
mkdir -p hardware/interfaces/nfcEMV/1.0
vim hardware/interfaces/nfcEMV/1.0/INfcEMV.hal
vim hardware/interfaces/nfcEMV/1.0/INfcNotify.hal
vim hardware/interfaces/nfcEMV/1.0/types.hal
目录结构如下:

eric@ubuntu:~/android-8.1.0_r52/hardware/interfaces/nfcEMV$ tree
.
└── 1.0
├── INfcEMV.hal
├── INfcNotify.hal
└── types.hal

INfcEMV.hal文件里面有一个接口INfcEMV和一个方法transceive(string command),文件实现如下:

package android.hardware.nfcEMV@1.0;
interface INfcEMV{
        init();
        release();
        transceive(string command) generates (string response);
        setCallback(INfcNotify callback);
};

其中transceive为收发命令接口。
setCallback:让client端设置一个callback方法到server端
下面来看看这个callback里面都定义了些啥,我们要为这个callback实现一个接口INfcNotify.hal

package android.hardware.nfcEMV@1.0;
​interface INfcNotify{
 oneway onNotify(HalEvent event);
};

回调函数里面有一个回调方法,可以让server传一个HalEvent的结构体到client端,这个结构体也是自定义的,在types.hal,可以定义自己喜欢的类型,这里是一个简单的int成员变量

package android.hardware.nfcEMV@1.0;
​struct HalEvent {
int32_t  id;
 int32_t value;
};

2、hidl-gen命令产生接口实现文件
Google为我们提供了一些工具来生成HAL层相关的代码框架和代码实例,这样子我们只需要关心实现部分,而不需要写一堆代码。

创建脚本nfcEMV.sh
vim nfcEMV.sh
chmod 777 nfcEMV.sh
脚本会使用hidl-gen工具,自动生成对应的c++文件和Android.bp文件,实现如下:

PACKAGE=android.hardware.nfcEMV@1.0
LOC=hardware/interfaces/nfcEMV/1.0/default/
# make hidl-gen -j64
#使用hidl-gen生成default目录里的C++文件
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
#使用hidl-gen生成default目录 里的Android.bp文件
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

现在的目录如下:
eric@ubuntu:~/android-8.1.0_r52/hardware/interfaces/nfcEMV$ tree
.
└── 1.0
├── default
│ ├── Android.bp**
│ ├── NfcEMV.cpp**
│ ├── NfcEMV.h**
│ ├── NfcNotify.cpp**
│ └── NfcNotify.h**
├── INfcEMV.hal
├── INfcNotify.hal
└── types.hal

其中有一个代码是用不到的,NfcNotify.h和NfcNotify.cpp,删掉这两个文件。
default 是新生成的目录,打开NfcEMV.h文件,去掉下面的注释

// extern "C" INfcEMV* HIDL_FETCH_INfcEMV(const char* name);

HIDL的实现有两种方式,一种是Binderized模式,另一种是Passthrough模式,我们使用直通式HAL(Passthrough 模式)来通信。

NfcEMV.cpp文件也要进行对应的修改, 去掉如下的注释:

//INfcEMV* HIDL_FETCH_INfcEMV(const char* /* name */) {
//    return new NfcEMV();
//}

整个文件代码如下:
vim hardware/interfaces/nfcEMV/1.0/default/NfcEMV.cpp

#define LOG_TAG     "NfcEMV"
#include "NfcEMV.h"
#include <log/log.h>
namespace android {
namespace hardware {
namespace nfcEMV {
namespace V1_0 {
namespace implementation {
//eric: server callback handler
sp<INfcNotify> NfcEMV::mCallback = nullptr;
// Methods from INfcEMV follow.
Return<void> NfcEMV::init() {
    // TODO implement
    mExit = false;
    run("NfcEMV");
    return Void();
}

Return<void> NfcEMV::release() {
    // TODO implement
    mExit = true;
    return Void();
}

Return<void> NfcEMV::transceive(const hidl_string& command, transceive_cb _hidl_cb) {
    // TODO implement
    //Eric: here print the command input from client
    char buf[100];
    ::memset(buf, 0x00, 100);
    ::snprintf(buf, 100, "___EricLog:transceive Command:, %s", command.c_str());
    hidl_string response(buf);

    _hidl_cb(response);
    return Void();
    return Void();
}

Return<void> NfcEMV::setCallback(const sp<INfcNotify>& callback) {
    // TODO implement
    mCallback = callback;
    if(mCallback != nullptr) {
     ALOGD("setCallback: done");
 }
    return Void();
}

bool NfcEMV::threadLoop()
{
    static int32_t count = 0;
    HalEvent event;
    while(!mExit) {
       ::sleep(2);
       event.value = count ++;
       if(mCallback != nullptr) {
          mCallback->onNotify(event);
       }
    }//end while
    ALOGD("threadLoop: exit");
    return false;
}

// Methods from ::android::hidl::base::V1_0::IBase follow.

INfcEMV* HIDL_FETCH_INfcEMV(const char* /* name */) {
    return new NfcEMV();
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace nfcEMV
}  // namespace hardware
}  // namespace android

3. 然后使用脚本update-makefiles.sh来更新Makefile
自动在hardware/interfaces/nfcEMV/1.0目录生成Android.mk和 Android.bp,hardware/interfaces/nfcEMV目录生成Android.bp。命令如下

./hardware/interfaces/update-makefiles.sh

再来添加两个空文件:
touch hardware/interfaces/nfcEMV/1.0/default/android.hardware.nfcEMV@1.0-service.rc
touch hardware/interfaces/nfcEMV/1.0/default/service.cpp
目录结构如下:
eric@ubuntu:~/android-8.1.0_r52/hardware/interfaces/nfcEMV$ tree
.
├── 1.0
│ ├── Android.bp
│ ├── Android.mk
│ ├── default
│ │ ├── Android.bp
│ │ ├── android.hardware.nfcEMV@1.0-service.rc**
│ │ ├── NfcEMV.cpp
│ │ ├── NfcEMV.h
│ │ ├── NfcNotify.cpp
│ │ ├── NfcNotify.h
│ │ └── service.cpp**
│ ├── INfcEMV.hal
│ ├── INfcNotify.hal
│ └── types.hal
└── Android.bp

其中android.hardware.nfcEMV@1.0-service.rc是程序的入口函数,实现如下:
vim hardware/interfaces/nfcEMV/1.0/default/android.hardware.nfcEMV@1.0-service.rc

service nfcEMV_hal_service /vendor/bin/hw/android.hardware.nfcEMV@1.0-service
    class hal
    user system
    group system

很简单,就是在设备启动的时候执行/vendor/bin/hw/android.hardware.nfcEMV@1.0-service程序:

vim hardware/interfaces/nfcEMV/1.0/default/service.cpp

# define LOG_TAG "android.hardware.nfcEMV@1.0-service"

# include <android/hardware/nfcEMV/1.0/INfcEMV.h>

# include <hidl/LegacySupport.h>

using android::hardware::nfcEMV::V1_0::INfcEMV;
using android::hardware::defaultPassthroughServiceImplementation;

int main() {
    return defaultPassthroughServiceImplementation<INfcEMV>();
}

这个service是注册了INfcEMV接口文件里面的接口,作为binder server端,很简单就一句话,因为我们使用了passthrough的模式,Android帮我们封装了这个函数,不需要我们自己去addService
打开hardware/interfaces/nfcEMV/1.0/default目录下的Android.bp,添加编译service.cpp成为可执行文件的代码。具体添加内容如下:
vim hardware/interfaces/nfcEMV/1.0/default/Android.bp

cc_binary {
    name: "android.hardware.nfcEMV@1.0-service",
    defaults: ["hidl_defaults"],
    proprietary: true,
    relative_install_path: "hw",
    srcs: ["service.cpp"],
    init_rc: ["android.hardware.nfcEMV@1.0-service.rc"],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
        "android.hardware.nfcEMV@1.0",
        "android.hardware.nfcEMV@1.0-impl",
    ],
}

然后编译:
mmm hardware/interfaces/nfcEMV/1.0/default/

编译后可以在, vendor/bin/hw/下找到对应的文件android.hardware.nfcEMV@1.0-service*。
ll out/target/product/bullhead/vendor/bin/hw/

生成的HAL server端的程序列表:

  1. out/target/product/bullhead/vendor/lib64/hw/android.hardware.nfcEMV@1.0-impl.so*
  2. out/target/product/bullhead/system/lib64/android.hardware.nfcEMV@1.0.so*
  3. out/target/product/bullhead/vendor/bin/hw/android.hardware.nfcEMV@1.0-service*

目前,我们server端的进程和实现端共享库已经完成了。

在manifest文件里添加vendor接口的定义, 编辑device/lge/bullhead/manifest.xml文件(拿Nexus5x手机举例),添加android.hardware.nfcEMV的声明,不然在client端是没法拿到service的。如下:
vim device/lge/bullhead/manifest.xml

<manifest version="1.0" type="device">
    <hal format="hidl">
        <name>android.hardware.graphics.allocator</name>
        <transport>hwbinder</transport>
        <version>2.0</version>
        <interface>
            <name>IAllocator</name>
            <instance>default</instance>
        </interface>
    </hal>
    <hal format="hidl">
        <name>android.hardware.wifi</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IWifi</name>
            <instance>default</instance>
        </interface>
    </hal>
    <hal format="hidl">
        <name>android.hardware.wifi.supplicant</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>ISupplicant</name>
            <instance>default</instance>
        </interface>
    </hal>
    <hal format="hidl">
        <name>android.hardware.nfcEMV</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>INfcEMV</name>
            <instance>default</instance>
        </interface>
    </hal>
</manifest>

4. 客户端的实现

4.1 使用C++实现客户端调用

在hardware/interfaces/nfcEMV/1.0目录下新建test目录,并且在test目录下新建Android.bp跟nfcEMVTest.cpp文件,这两个文件的内容如下:
vim hardware/interfaces/nfcEMV/1.0/test/nfcEMVTest.cpp

#define LOG_TAG     "nfcEMV_Test"
#include <log/log.h>
#include <android/hardware/nfcEMV/1.0/INfcEMV.h>
#include <android/hardware/nfcEMV/1.0/types.h>
#include <android/hardware/nfcEMV/1.0/INfcNotify.h>
#include <hidl/Status.h>
#include <hidl/HidlSupport.h>
using android::sp;
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::nfcEMV::V1_0::INfcEMV;
using android::hardware::nfcEMV::V1_0::HalEvent
using android::hardware::nfcEMV::V1_0::INfcNotify

class nfcEMVCallback: public INfcNotify {
    public:
    nfcEMVCallback() {}
    ~nfcEMVCallback() {}
    Return<void> onNotify(const HalEvent& event) {
    ALOGD("onNotify: id = %d,  value = %d", event.id, event.value);
    return Void();
}
};
int main(void)
{
    sp<INfcEMV> service = INfcEMV::getService();
    if(service == nullptr) {
        ALOGE("main: failed to get nfcEMV service");
    return -1;
    }
    sp<nfcEMVCallback> callback = new nfcEMVCallback();
    service->setCallback(callback);
    service->init();
    ::sleep(20);
    service->release();
    return 0;
}

vim -p hardware/interfaces/nfcEMV/1.0/test/Android.bp

cc_binary {
    relative_install_path: "hw",
    defaults: ["hidl_defaults"],
    name: "nfcEMV_client",
    proprietary: true,

    srcs: ["nfcEMVTest.cpp"],
    shared_libs: [
        "liblog",
        "libhardware",
        "libhidlbase",
        "libhidltransport",
        "libutils",

        "android.hardware.nfcEMV@1.0",
    ],
}

执行以下命令:

./hardware/interfaces/update-makefiles.sh
mmm hardware/interfaces/nfcEMV/1.0/
执行第一条命令是为了更新hardware/interfaces/nfcEMV/目录下的Android.bp文件,如下:
vim hardware/interfaces/nfcEMV/Android.bp

// This is an autogenerated file, do not edit.
subdirs = [
    "1.0",
    "1.0/default",
    "1.0/test",
]

执行第二条命令会生成可执行文件:

out/target/product/bullhead/vendor/bin/hw/nfcEMV_client
调试HIDL
建立脚本nfcEMV_adb_push.sh,把生成的service复制到手机的目录。
vim nfcEMV_adb_push.sh

adb push out/target/product/bullhead/vendor/lib64/hw/android.hardware.nfcEMV@1.0-impl.so /vendor/lib64/hw/
adb push out/target/product/bullhead/system/lib64/android.hardware.nfcEMV@1.0.so /system/lib64/
adb push out/target/product/bullhead/vendor/bin/hw/android.hardware.nfcEMV@1.0-service* /vendor/bin/hw/
adb push out/target/product/bullhead/vendor/etc/init/android.hardware.nfcEMV@1.0-service.rc /vendor/etc/init/
adb push out/target/product/bullhead/vendor/bin/hw/nfcEMV_client /vendor/bin/hw/
adb push device/lge/bullhead/manifest.xml /vendor/

然后进入手机adb device检查手机是否连接,adb shell进入命令行。

adb shell
cd /vendor/bin/hw/
./nfcEMV_client

4.2 java层实现客户端的HIDL调用
为了方便eclipse或者Android Studio调用接口函数,需要编译出classes.jar包。但是jack编译出来的文件是classes.jack。

检查目录

eric@ubuntu:~/android-8.1.0_r52/out/target/common/obj/JAVA_LIBRARIES/android.hardware.nfcEMV-V1.0-java_intermediates$ tree
.
|-- anno
|-- classes
|-- classes-desugar.jar
|-- classes-full-debug.jar
|-- classes.dex
|-- classes.jack
|-- classes.jar(需要这个文件)
|-- desugar_dumped_classes
|-- jack-rsc
|-- jack-rsc.java-source-list
|-- jack_res_jar_flags
|-- javalib.jar
|-- link_type
为了编译出classes.jar,需要打开hardware/interfaces/nfcEMV/1.0目录的Android.mk,在include $(CLEAR_VARS)下面添加ANDROID_COMPILE_WITH_JACK := false。这样编译的时候就不走jack编译了
vim hardware/interfaces/nfcEMV/1.0/Android.mk

# This file is autogenerated by hidl-gen. Do not edit manually.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
#LOCAL_JACK_ENABLED := disabled
ANDROID_COMPILE_WITH_JACK := false
LOCAL_MODULE := android.hardware.nfcEMV-V1.0-java
LOCAL_MODULE_CLASS := JAVA_LIBRARIES

执行下面命令

mmm hardware/interfaces/nfcEMV/1.0/
执行完后生成classes.jar.

新建Android项目HIDLdemo,将classes.jar导入项目,MainActivity代码实现如下:

vim packages/apps/HIDLdemo/src/com/example/eric/hidldemo/MainActivity.java

package com.example.eric.hidldemo;

import android.hardware.nfcEMV.V1_0.INfcEMV;
import android.os.Bundle;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    INfcEMV iNfcEMVService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hidl);
        try {
            iNfcEMVService = INfcEMV.getService(); 
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public void hidlTest(View view){
        if (iNfcEMVService != null){
            Log.d("NfcEMV", "service is connect.");
            String s = null;
            try {
                s = iNfcEMVService.transceive("NfcEMV");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            Log.d("nfcEMV", s);
            Toast.makeText(this, s, Toast.LENGTH_LONG).show();
        }
    }
}

添加Android.mk文件,然后将项目放到packages/apps/进行编译。Android.mk文件内容如下:

vim packages/apps/HIDLdemo/Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := HIDLdemo
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE_TAGS :=optional
LOCAL_DEX_PREOPT := false
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-gridlayout
#LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.nfcEMV-V1.0-java-static

LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/appcompat/res
LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/gridlayout/res

LOCAL_CERTIFICATE := platform
#LOCAL_PRIVILEGED_MODULE := true
LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat:android.support.v7.gridlayout
#LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.fingerprint-V1.0-java-static 
#LOCAL_SRC_FILES := $(call all-subdir-java-files)
include $(BUILD_PACKAGE)

执行命令

mmm packages/apps/HIDLdemo

会生成out/target/product/bullhead/system/app/HIDLdemo/HIDLdemo.apk
push到手机:

adb install -r out/target/product/bullhead/system/app/HIDLdemo/HIDLdemo.apk

问题:这个时候会出现base.apk code is missing的error 信息,

adb: failed to install out/target/product/bullhead/system/app/HIDLdemo/HIDLdemo.apk: Failure [INSTALL_FAILED_INVALID_APK: Package couldn't be installed in /data/app/com.example.eric.hidldemo-3AZN-XemgZKqyJPiOwx0Jg==: Package /data/app/com.example.eric.hidldemo-3AZN-XemgZKqyJPiOwx0Jg==/base.apk code is missing]

解决方案:在Android.mk中增加LOCAL_DEX_PREOPT := false

Android客户端执行
先启动./android.hardware.nfcEMV@1.0-service服务,然后通过命令拉起MainActivity界面

adb shell am start -n com.example.eric.hidldemo/.MainActivity

执行时会出现SELinux : avc: denied的error信息;添加HIDL的SELinux步骤:
vim device/lge/bullhead/sepolicy/service.te

type per_mgr_service,           service_manager_type;
type atfwd_service,             service_manager_type;
type cne_service,               service_manager_type;
type nfcEMV_hal_service,        service_manager_type;

vim device/lge/bullhead/sepolicy/service_contexts

android.hardware.nfcEMV::INfcEMV  u:object_r:nfcEMV_hal_service:s0
android.hardware.nfcEMV::NfcEMVEvent  u:object_r:nfcEMV_hal_service:s0
android.hardware.nfcEMV::INfcEMVClientCallback  u:object_r:nfcEMV_hal_service:s0

vim device/lge/bullhead/sepolicy/file_contexts
添加:

/vendor/bin/hw/android\.hardware\.nfcEMV@1\.0-service                  u:object_r:nfcEMV_hal_service:s0

android 8.0之后在只能在device/xxx/sepoilcy中添加avc权限,/sysetm/sepolicy/Android.mk 编译的out生成路径在 out/target/product/xxx/vendor/etc/selinux/

下面提供不用修改devices/设备名称/sepolicy/ *.te 的方法

直接修改 :android-8.1.0_r15/system/sepolicy/private/service_contexts

window                                    u:object_r:window_service:s0
gesture                                    u:object_r:window_service:s0
*                                         u:object_r:default_android_service:s0

gesture 就是Context定义的service name
修改之后需要在源码目录下面make selinux_policy -j11
会生成out/target/product/marlin/system/etc/selinux/plat_service_contexts
把这个文件adb root && adb remount && adb push plat_service_contexts /system/etc/selinux/ 重启手机

或者make systemimage 然后使用fastboot flash system system.img 就可以了

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

推荐阅读更多精彩内容