SurfaceFlinger学习

SurfaceFlinger学习

参考资料:

https://blog.csdn.net/hexiaolong2009/article/details/99225637

BufferState

BufferState tracks the states in which a buffer slot can be

    uint32_t mDequeueCount;
    uint32_t mQueueCount;
    uint32_t mAcquireCount;
    bool mShared;
    // A buffer can be in one of five states, represented as below:
    //
    //         | mShared | mDequeueCount | mQueueCount | mAcquireCount |
    // --------|---------|---------------|-------------|---------------|
    // FREE    |  false  |       0       |      0      |       0       |
    // DEQUEUED|  false  |       1       |      0      |       0       |
    // QUEUED  |  false  |       0       |      1      |       0       |
    // ACQUIRED|  false  |       0       |      0      |       1       |
    // SHARED  |  true   |      any      |     any     |      any      |

FREE--->DEQUEUED--->QUEUED--->ACQUIRED--->FREE

    // FREE indicates that the buffer is available to be dequeued by the
    // producer. The slot is "owned" by BufferQueue. It transitions to DEQUEUED
    // when dequeueBuffer is called.
    //
    // DEQUEUED indicates that the buffer has been dequeued by the producer, but
    // has not yet been queued or canceled. The producer may modify the
    // buffer's contents as soon as the associated release fence is signaled.
    // The slot is "owned" by the producer. It can transition to QUEUED (via
    // queueBuffer or attachBuffer) or back to FREE (via cancelBuffer or
    // detachBuffer).
    //
    // QUEUED indicates that the buffer has been filled by the producer and
    // queued for use by the consumer. The buffer contents may continue to be
    // modified for a finite time, so the contents must not be accessed until
    // the associated fence is signaled. The slot is "owned" by BufferQueue. It
    // can transition to ACQUIRED (via acquireBuffer) or to FREE (if another
    // buffer is queued in asynchronous mode).
    //
    // ACQUIRED indicates that the buffer has been acquired by the consumer. As
    // with QUEUED, the contents must not be accessed by the consumer until the
    // acquire fence is signaled. The slot is "owned" by the consumer. It
    // transitions to FREE when releaseBuffer (or detachBuffer) is called. A
    // detached buffer can also enter the ACQUIRED state via attachBuffer.
    //
    // SHARED indicates that this buffer is being used in shared buffer
    // mode. It can be in any combination of the other states at the same time,
    // except for FREE (since that excludes being in any other state). It can
    // also be dequeued, queued, or acquired multiple times.

BufferSlot

struct BufferSlot {
        sp<GraphicBuffer> mGraphicBuffer;
        BufferState mBufferState;
        bool mRequestBufferCalled;
coreduffer.jpg

mSlots = mFreeSlots + mFreeBuffers + mActiveBuffers + mUnusedSlots

BufferQueueCore

BufferQueueCore 负责维护 BufferQueue 的基本数据结构,而 BufferQueueProducer 和 BufferQueueConsumer 则负责提供操作 BufferQueue 的基本接口

#include <gui/BufferItem.h>
#include <gui/BufferQueueDefs.h>
#include <gui/BufferSlot.h>
class IConsumerListener;
class IProducerListener;

class BufferQueueCore : public virtual RefBase {

    friend class BufferQueueProducer;
    friend class BufferQueueConsumer;

    typedef Vector<BufferItem> Fifo;//数组链表
    /*BufferItem的部分成员变量
    sp<GraphicBuffer> mGraphicBuffer;
    int mSlot;
    */
    bool mIsAbandoned; 
    bool mIsAllocating;
    
    mutable std::condition_variable mDequeueCondition;
    mutable std::condition_variable mIsAllocatingCondition;
    // mConsumerListener is used to notify the connected consumer of
    // asynchronous events that it may wish to react to. It is initially
    // set to NULL and is written by consumerConnect and consumerDisconnect.
    sp<IConsumerListener> mConsumerListener;

    // mLinkedToDeath is used to set a binder death notification on
    // the producer.
    sp<IProducerListener> mLinkedToDeath;

    // mConnectedProducerListener is used to handle the onBufferReleased
    // and onBuffersDiscarded notification.
    sp<IProducerListener> mConnectedProducerListener;

    BufferQueueDefs::SlotsType mSlots;

    // mQueue is a FIFO of queued buffers used in synchronous mode.
    Fifo mQueue;
    // mFreeSlots contains all of the slots which are FREE and do not currently
    // have a buffer attached.
    std::set<int> mFreeSlots;

    // mFreeBuffers contains all of the slots which are FREE and currently have
    // a buffer attached.
    std::list<int> mFreeBuffers;

    // mUnusedSlots contains all slots that are currently unused. They should be
    // free and not have a buffer attached.
    std::list<int> mUnusedSlots;

    // mActiveBuffers contains all slots which have a non-FREE buffer attached.
    std::set<int> mActiveBuffers;

    // mDequeueCondition is a condition variable used for dequeueBuffer in
    // synchronous mode.
    mutable std::condition_variable mDequeueCondition;

ComposerService

class ComposerService : public Singleton<ComposerService>
{
    sp<ISurfaceComposer> mComposerService;
    sp<IBinder::DeathRecipient> mDeathObserver;
    Mutex mLock;

    ComposerService();
    void connectLocked();
    void composerServiceDied();
    friend class Singleton<ComposerService>;
public:

    // Get a connection to the Composer Service.  This will block until
    // a connection is established.
    static sp<ISurfaceComposer> getComposerService();
};

ConsumerBase

class ConsumerBase : public virtual RefBase,
        protected ConsumerListener {
public:
    struct FrameAvailableListener : public virtual RefBase {
        // See IConsumerListener::onFrame{Available,Replaced}
        virtual void onFrameAvailable(const BufferItem& item) = 0;
        virtual void onFrameReplaced(const BufferItem& /* item */) {}
        virtual void onFrameDequeued(const uint64_t){};
        virtual void onFrameCancelled(const uint64_t){};
        virtual void onFrameDetached(const uint64_t){};
    };
            
    wp<FrameAvailableListener> mFrameAvailableListener;

    // The ConsumerBase has-a BufferQueue and is responsible for creating this object
    // if none is supplied
    sp<IGraphicBufferConsumer> mConsumer;

Surface

class Surface
    : public ANativeObjectBase<ANativeWindow, Surface, RefBase>

ISurfaceComposerClient

ISurfaceComposerClient,自定义接口(继承于IInterface),BnSurfaceComposerClient是Binder本地对象,BpSurfaceComposerClient是Binder代理对象,在cpp中定义。

class ISurfaceComposerClient : public IInterface {
public:
    DECLARE_META_INTERFACE(SurfaceComposerClient)//定义
        
  
class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
    public:
    BnSurfaceComposerClient()
          : SafeBnInterface<ISurfaceComposerClient>("BnSurfaceComposerClient") {}

    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
};
//cpp中的内部类
class BpSurfaceComposerClient : public SafeBpInterface<ISurfaceComposerClient> {   
    
    IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient");//实现

SurfaceComposerClient

class SurfaceComposerClient : public RefBase
    private:
    virtual void onFirstRef();

static sp<SurfaceComposerClient> getDefault();

    mutable     Mutex                       mLock;
                status_t                    mStatus;
                sp<ISurfaceComposerClient>  mClient;
    //! Create a surface
    sp<SurfaceControl> createSurface(const String8& name,              // name of the surface
                                     uint32_t w,                       // width in pixel
                                     uint32_t h,                       // height in pixel
                                     PixelFormat format,               // pixel-format desired
                                     uint32_t flags = 0,               // usage flags
                                     SurfaceControl* parent = nullptr, // parent
                                     LayerMetadata metadata = LayerMetadata(), // metadata
                                     uint32_t* outTransformHint = nullptr);

b4ef45bfc3abcc050cbc73085239fc2.jpg

https://www.jianshu.com/p/8e7a9a0b5726

ISurfaceComposer

ISurfaceComposer自定义接口,BnSurfaceComposer是Binder本地对象,BpSurfaceComposer是Binder代理对象

class ISurfaceComposer: public IInterface {

public:
    DECLARE_META_INTERFACE(SurfaceComposer)
    
class BnSurfaceComposer: public BnInterface<ISurfaceComposer>
        virtual status_t onTransact(uint32_t code, const Parcel& data,
            Parcel* reply, uint32_t flags = 0);

//cpp内部类
class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
    
    IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer");
    
        virtual sp<ISurfaceComposerClient> createConnection()
    {
        Parcel data, reply;//data是发送的数据,reply是接受的数据
        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());//android.ui.ISurfaceComposer
        remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply);
            //remote()得到的是一个BpBinder代理对象,与BBinder本地对象进程间通信
        return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
            //得到一个BpBinder代理对象,以其为参数,转换为ISurfaceComposerClient类型的代理对象
    }
    virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
                                   bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
                                   ui::Rotation rotation, bool captureSecureLayers) {
        Parcel data, reply;
        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
        data.writeStrongBinder(display);
        data.writeInt32(static_cast<int32_t>(reqDataspace));
        data.writeInt32(static_cast<int32_t>(reqPixelFormat));
        data.write(sourceCrop);
        data.writeUint32(reqWidth);
        data.writeUint32(reqHeight);
        data.writeInt32(static_cast<int32_t>(useIdentityTransform));
        data.writeInt32(static_cast<int32_t>(rotation));
        data.writeInt32(static_cast<int32_t>(captureSecureLayers));
        status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
        if (result != NO_ERROR) {
            ALOGE("captureScreen failed to transact: %d", result);
            return result;
        }
        result = reply.readInt32();
        if (result != NO_ERROR) {
            ALOGE("captureScreen failed to readInt32: %d", result);
            return result;
        }

        *outBuffer = new GraphicBuffer();
        reply.read(**outBuffer);
        outCapturedSecureLayers = reply.readBool();

        return result;
    }

SurfaceControl

// ---------------------------------------------------------------------------

class IGraphicBufferProducer;
class Surface;
class SurfaceComposerClient;

// ---------------------------------------------------------------------------

class SurfaceControl : public RefBase
    
        SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
                   const sp<IGraphicBufferProducer>& gbp, uint32_t transformHint = 0);
    friend class SurfaceComposerClient;
    friend class Surface;

    sp<SurfaceComposerClient>   mClient;//消费者
    sp<IBinder>                 mHandle;
    sp<IGraphicBufferProducer>  mGraphicBufferProducer;//生产者
    mutable Mutex               mLock;
    mutable sp<Surface>         mSurfaceData;
    uint32_t mTransformHint;

两个函数实例

status_t SurfaceControl::writeSurfaceToParcel(
        const sp<SurfaceControl>& control, Parcel* parcel)
{
    sp<IGraphicBufferProducer> bp;
    if (control != nullptr) {
        bp = control->mGraphicBufferProducer;
    }
    return parcel->writeStrongBinder(IInterface::asBinder(bp));
}

mClient->getClient()得到的是ISurfaceComposerClient(注意类数据成员)

void SurfaceControl::writeToParcel(Parcel* parcel)
{
    parcel->writeStrongBinder(ISurfaceComposerClient::asBinder(mClient->getClient()));
    parcel->writeStrongBinder(mHandle);
    parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
    parcel->writeUint32(mTransformHint);
}

ProducerListener

class ProducerListener : public virtual RefBase//

class IProducerListener : public ProducerListener, public IInterface


class BnProducerListener : public BnInterface<IProducerListener>
    //cpp内部类
    class BpProducerListener : public BpInterface<IProducerListener>
        
        
        IMPLEMENT_HYBRID_META_INTERFACE(ProducerListener,
        "android.gui.IProducerListener")

ConsumerListener

class ConsumerListener : public virtual RefBase {
    
class IConsumerListener : public ConsumerListener, public IInterface {
DECLARE_META_INTERFACE(ConsumerListener)
class BnConsumerListener : public SafeBnInterface<IConsumerListener> {
    
        status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                        uint32_t flags = 0) override;
//cpp内部类
    class BpConsumerListener : public SafeBpInterface<IConsumerListener> {
        
        IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener");

通知事件类型:

enum class Tag : uint32_t {
    ON_DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
    ON_FRAME_AVAILABLE,
    ON_FRAME_REPLACED,
    ON_BUFFERS_RELEASED,
    ON_SIDEBAND_STREAM_CHANGED,
    ON_FRAME_DEQUEUED,
    ON_FRAME_CANCELLED,
    ON_FRAME_DETACHED,
    LAST = ON_FRAME_DETACHED,
};

IGraphicBufferProducer

class IGraphicBufferProducer : public IInterface {
    DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer,
    
 struct QueueBufferInput : public Flattenable<QueueBufferInput> {  
 struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
                                  
class BnGraphicBufferProducer : public BnInterface<IGraphicBufferProducer>
//cpp内部类
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;

virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
                                   PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
                                   FrameEventHistoryDelta* outTimestamps) = 0;

virtual status_t detachBuffer(int slot) = 0;

virtual status_t attachBuffer(int* outSlot,
            const sp<GraphicBuffer>& buffer) = 0;

virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
            QueueBufferOutput* output) = 0;

IGraphicBufferConsumer

class IGraphicBufferConsumer : public IInterface {
    public:
    DECLARE_META_INTERFACE(GraphicBufferConsumer)
class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> {
//cpp内部类
    class BpGraphicBufferConsumer : public SafeBpInterface<IGraphicBufferConsumer> {
        using Signature = decltype(&IGraphicBufferConsumer::consumerConnect);//?
        IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer");
    virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
                                   uint64_t maxFrameNumber = 0) = 0;

    virtual status_t detachBuffer(int slot) = 0;

BufferQueueProducer

Binder本地对象,IGraphicBufferProducer接口的实现,还注册了DeathRecipient,服务端死亡回调通知

class BufferQueueProducer : public BnGraphicBufferProducer,
                            private IBinder::DeathRecipient {
                                

    sp<BufferQueueCore> mCore;

    // This references mCore->mSlots. Lock mCore->mMutex while accessing.
    BufferQueueDefs::SlotsType& mSlots;

//const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);

status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
    ATRACE_CALL();
    BQ_LOGV("requestBuffer: slot %d", slot);
    std::lock_guard<std::mutex> lock(mCore->mMutex);

    if (mCore->mIsAbandoned) {
        BQ_LOGE("requestBuffer: BufferQueue has been abandoned");
        return NO_INIT;
    }

    if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
        BQ_LOGE("requestBuffer: BufferQueue has no connected producer");
        return NO_INIT;
    }

    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
        BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
                slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
        return BAD_VALUE;
    } else if (!mSlots[slot].mBufferState.isDequeued()) {
        BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
                "(state = %s)", slot, mSlots[slot].mBufferState.string());
        return BAD_VALUE;
    }

    mSlots[slot].mRequestBufferCalled = true;
    *buf = mSlots[slot].mGraphicBuffer;
    return NO_ERROR;
}
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);

virtual status_t dequeueBuffer(int* outSlot, sp<Fence>* outFence, uint32_t width,
                                   uint32_t height, PixelFormat format, uint64_t usage,
                                   uint64_t* outBufferAge,
                                   FrameEventHistoryDelta* outTimestamps) override;

    // See IGraphicBufferProducer::detachBuffer
    virtual status_t detachBuffer(int slot);

    // See IGraphicBufferProducer::attachBuffer
    virtual status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer);

    virtual status_t queueBuffer(int slot,
            const QueueBufferInput& input, QueueBufferOutput* output);
    virtual status_t cancelBuffer(int slot, const sp<Fence>& fence);

    virtual status_t connect(const sp<IProducerListener>& listener,
            int api, bool producerControlledByApp, QueueBufferOutput* output);

    // See IGraphicBufferProducer::disconnect
    virtual status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api);

BufferQueueConsumer

class BufferQueueConsumer : public BnGraphicBufferConsumer {
    sp<BufferQueueCore> mCore;

    // This references mCore->mSlots. Lock mCore->mMutex while accessing.
    BufferQueueDefs::SlotsType& mSlots;

    // This is a cached copy of the name stored in the BufferQueueCore.
    // It's updated during setConsumerName.
    String8 mConsumerName;

BufferItem

class BufferItem : public Flattenable<BufferItem> {
    sp<GraphicBuffer> mGraphicBuffer;

    // mFence is a fence that will signal when the buffer is idle.
    sp<Fence> mFence;
    uint32_t mTransform;

    // mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT).
    int mSlot;
    
    // Describes the portion of the surface that has been modified since the
    // previous frame
    Region mSurfaceDamage;

BufferQueue::createBufferQueue

https://blog.csdn.net/hexiaolong2009/article/details/99053375

void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
        sp<IGraphicBufferConsumer>* outConsumer,
        bool consumerIsSurfaceFlinger) {
    LOG_ALWAYS_FATAL_IF(outProducer == nullptr,
            "BufferQueue: outProducer must not be NULL");
    LOG_ALWAYS_FATAL_IF(outConsumer == nullptr,
            "BufferQueue: outConsumer must not be NULL");

    sp<BufferQueueCore> core(new BufferQueueCore());
    LOG_ALWAYS_FATAL_IF(core == nullptr,
            "BufferQueue: failed to create BufferQueueCore");

    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
    LOG_ALWAYS_FATAL_IF(producer == nullptr,
            "BufferQueue: failed to create BufferQueueProducer");

    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
    LOG_ALWAYS_FATAL_IF(consumer == nullptr,
            "BufferQueue: failed to create BufferQueueConsumer");

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

推荐阅读更多精彩内容