顯示框架之app與SurfaceFlinger通信

SurfaceFlinger是android顯示的核心進程,在整個顯示框架中起到一個承上啟下的作用,“承上”指的是與app進程間的通信,“啟下”指的是與Composer進程的通信。Surfaceflinger本身不進行繪制,是app數據上屏的中樞通路,先來看下SurfaceFlinger在整個顯示流程中的位置。


顯示流程圖.png

從顯示流程圖看可知,SurfaceFlinger位于中間層的位置,目前的應用會調起renderthread使用GPU來渲染,應用側使用surface來管理顯示數據,surfaceflinger使用layer來對應應用側的surface,surfaceflinger會根據合成的方式,選擇device還是GPU合成,最后將layer數據提交給Composer進程,進而通過display 驅動上屏。本文就來研究下app和SurfaceFlinger之間的通信,主要針對native層的分析,代碼基于android11。

APP與SurfaceFlinger之間的連接

首先框架會創建surfaceControl來管理surface,在jni層會創建一個surfaceComposerClient對象,這個對象是負責與SurfaceFlinger進程通信的重要載體。

文件:frameworks/base/core/jni/android_view_SurfaceControl.cpp

static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jobject metadataParcel) {
    ScopedUtfChars name(env, nameStr);
    sp<SurfaceComposerClient> client;
    if (sessionObj != NULL) {
        client = android_view_SurfaceSession_getClient(env, sessionObj);
    } else {
         // 如果java側沒有該對象則創建SurfaceComposerClient對象
        client = SurfaceComposerClient::getDefault();
    }
    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
    sp<SurfaceControl> surface;
   ...
    // 通過SurfaceComposerClient創建一個Surface
    status_t err = client->createSurfaceChecked(
            String8(name.c_str()), w, h, format, &surface, flags, parent, std::move(metadata));
   ...
    // 返回給上層surface對象
    return reinterpret_cast<jlong>(surface.get());
}

文件:frameworks/native/libs/gui/SurfaceComposerClient.cpp 

sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() {
    return DefaultComposerClient::getComposerClient();
 }

class DefaultComposerClient: public Singleton<DefaultComposerClient> {
    Mutex mLock;
    sp<SurfaceComposerClient> mClient;
    friend class Singleton<ComposerService>;
public:
    static sp<SurfaceComposerClient> getComposerClient() {
        // 使用單例模式創建DefaultComposerClient 對象
        DefaultComposerClient& dc = DefaultComposerClient::getInstance();
        Mutex::Autolock _l(dc.mLock);
        if (dc.mClient == nullptr) {
            // 創建SurfaceComposerClient 對象
            dc.mClient = new SurfaceComposerClient;
        }
        return dc.mClient;
    }
};
// 由于SurfaceComposerClient是sp指針,第一次創建時會執行onFirstRef 函數
void SurfaceComposerClient::onFirstRef() {
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if (sf != nullptr && mStatus == NO_INIT) {
        sp<ISurfaceComposerClient> conn;
        conn = sf->createConnection();
        if (conn != nullptr) {
            mClient = conn;
            mStatus = NO_ERROR;
        }
    }
}
//在getComposerService 函數中調connectLocked來get到SurfaceFlinger服務并注冊了死亡監聽
void ComposerService::connectLocked() {
    const String16 name("SurfaceFlinger");
    while (getService(name, &mComposerService) != NO_ERROR) {
        usleep(250000);
    }
    assert(mComposerService != nullptr);

    // Create the death listener.
    class DeathObserver : public IBinder::DeathRecipient {
        ComposerService& mComposerService;
        virtual void binderDied(const wp<IBinder>& who) {
            ALOGW("ComposerService remote (surfaceflinger) died [%p]",
                  who.unsafe_get());
            mComposerService.composerServiceDied();
        }
     public:
        explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
    };

    mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));
    IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
}
文件:frameworks/native/services/surfaceflinger/surfaceflinger.cpp

// 與SurfaceFlinger建立聯系,Client持有SurfaceFlinger對象
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
    const sp<Client> client = new Client(this);
    return client->initCheck() == NO_ERROR ? client : nullptr;
}

看注釋,至此完成了從SurfaceControl-> SurfaceComposerClient -> SurfaceFlinger的連接過程。應用要創建Surface時,對應SurfaceFlinger會創建layer與之對應。

文件:frameworks/native/libs/gui/SurfaceComposerClient.cpp 

status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
                                                     PixelFormat format,
                                                     sp<SurfaceControl>* outSurface, uint32_t flags,
                                                     SurfaceControl* parent, LayerMetadata metadata,
                                                     uint32_t* outTransformHint) {
    sp<SurfaceControl> sur;
    ...
        // 會執行到Client.cpp里面的createSurface
        err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
                                     &handle, &gbp, &transformHint);
       ...
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            // 根據返回的handle 和 gbp 創建 SurfaceControl
            *outSurface = new SurfaceControl(this, handle, gbp, transformHint);
        }
    }
    return err;
}

文件: frameworks/native/services/surfaceflinger/Client.cpp

status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                               uint32_t flags, const sp<IBinder>& parentHandle,
                               LayerMetadata metadata, sp<IBinder>* handle,
                               sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) {
    // We rely on createLayer to check permissions.
    return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
                                 parentHandle, nullptr, outTransformHint);
}

文件: frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

tatus_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
                                     uint32_t h, PixelFormat format, uint32_t flags,
                                     LayerMetadata metadata, sp<IBinder>* handle,
                                     sp<IGraphicBufferProducer>* gbp,
                                     const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
   ...
    sp<Layer> layer;
   ...
    switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
            // 對于surface類型創建layer類型,主要看下帶有BufferQueue的layer
            result = createBufferQueueLayer(client, std::move(uniqueName), w, h, flags,
                                            std::move(metadata), format, handle, gbp, &layer);

            break;
    ...
    // 將創建的layer放到mCurrentState 里面,SurfaceFlinger內部管理了mCurrentState 和 mDrawingState
    result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer,
                            addToCurrentState, outTransformHint);
    ...
    setTransactionFlags(eTransactionNeeded);
    return result;
}
文件: frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::string name,
                                                uint32_t w, uint32_t h, uint32_t flags,
                                                LayerMetadata metadata, PixelFormat& format,
                                                sp<IBinder>* handle,
                                                sp<IGraphicBufferProducer>* gbp,
                                                sp<Layer>* outLayer) {
    ...
    sp<BufferQueueLayer> layer;
    LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
    ...
        Mutex::Autolock lock(mStateLock);
        layer = getFactory().createBufferQueueLayer(args);
    ...
    if (err == NO_ERROR) {
         // 在surfaceflinger側 new了一個handle,這個handle指向這個layer
        *handle = layer->getHandle();
        //gbp是創建的BufferQueueProducer對象
        *gbp = layer->getProducer();
        *outLayer = layer;
    }
        
    ALOGE_IF(err, "createBufferQueueLayer() failed (%s)", strerror(-err));
    return err;
}

文件: frameworks/native/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp

sp<BufferQueueLayer> DefaultFactory::createBufferQueueLayer(const LayerCreationArgs& args) {
    // new 了BufferQueueLayer對象,sp指針第一次創建時會執行onFirstRef 函數
    return new BufferQueueLayer(args);
}

文件:frameworks/native/services/surfaceflinger/BufferQueueLayer.cpp

void BufferQueueLayer::onFirstRef() {
    BufferLayer::onFirstRef();

    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    // 創建一個BufferQueue對象,跟著創建一個BufferQueueCore, BufferQueueProducer和 BufferQueueConsumer對象
    mFlinger->getFactory().createBufferQueue(&producer, &consumer, true);
    // 對producer 的一個封裝,MonitoredProducer實質與 producer無差別
    mProducer = mFlinger->getFactory().createMonitoredProducer(producer, mFlinger, this);
    // 創建BufferLayerConsumer ,首先創建父類ConsumerBase 對象
    mConsumer =
            mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(),
                                                             mTextureName, this);
   ...
    // 注冊了一個監聽,將BufferQueueLayer的函數方法注冊到BufferQueueCore里面,說明當Buffer有變化時會通知BufferQueueLayer執行相應的動作
    mContentsChangedListener = new ContentsChangedListener(this);
    mConsumer->setContentsChangedListener(mContentsChangedListener);
    mConsumer->setName(String8(mName.data(), mName.size()));

    // BufferQueueCore::mMaxDequeuedBufferCount is default to 1
    // 設置producer最大dequeue的buffer數量
    if (!mFlinger->isLayerTripleBufferingDisabled()) {
        mProducer->setMaxDequeuedBufferCount(2);
    }
}

文件:frameworks/native/services/surfaceflinger/Layer.cpp

sp<IBinder> Layer::getHandle() {
    Mutex::Autolock _l(mLock);
    if (mGetHandleCalled) {
        ALOGE("Get handle called twice" );
        return nullptr;
    }
    mGetHandleCalled = true;
   // 創建一個handle對應layer
    return new Handle(mFlinger, this);
}

看注釋,SurfaceFlinger側創建了handle和GraphicsBufferProducer對象給到SurfaceControl,handle 指向surfaceflinger的layer,GraphicsBufferProducer 提供surface對Buffer的操作接口,上層操作surface的handle來作用到surfaceflinger的layer。至此,surfaceflinger創建layer的流程分析完了,比較繞的地方是涉及BufferQueue對象之間的關系,用一副類的關系圖來說明之間的關系。


BufferQueue類圖.png

APP與SurfaceFlinger之間數據的傳遞

現在應用一般采用GPU去渲染加速,GPU操作在RenderThread里(HWUI 里面的邏輯很復雜,后續會進行剖析),應用要繪制數據首先需要申請一塊Buffer,由前面分析可知,應用是一個GraphicsBufferProducer的角色,故可以通過dequeueBuffer來向surfaceflinger申請一塊Buffer。

DequeueBuffer

文件: frameworks/native/libs/gui/Surface.cpp

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
    ATRACE_CALL();
    ALOGV("Surface::dequeueBuffer");

   ...
    // 從應用進程調到SurfaceFlinger進程,實現在SurfaceFlinger側
    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
                                                            reqFormat, reqUsage, &mBufferAge,
                                                            enableFrameTimestamps ? &frameTimestamps
                                                                                  : nullptr);
    ...
     // 后續會通過requestBuffer拿到Buffer
    sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);

    ...
        // requestBuffer將通過importBuffer將Buffer映射到應用進程
        result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
    ...
       *buffer = gbuf.get();
    ...

       mDequeuedSlots.insert(buf);
       return OK;
}

文件: frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
                                            uint32_t width, uint32_t height, PixelFormat format,
                                            uint64_t usage, uint64_t* outBufferAge,
                                            FrameEventHistoryDelta* outTimestamps) {
    ...
    int found = BufferItem::INVALID_BUFFER_SLOT;
    while (found == BufferItem::INVALID_BUFFER_SLOT) {
          // 優先從mFreeBuffers 里面獲取slot, 若沒有則從mFreeSlots 里面獲取,從mFreeSlots 里面獲取的都是還未分配Buffer的slot
          status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
    ...
    }
    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
        ...
        //若得到的slot對應的buffer為空則重新分配一塊GraphicsBuffer
        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
                width, height, format, BQ_LAYER_COUNT, usage,
                {mConsumerName.string(), mConsumerName.size()});
        ...
        mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
        ...
}

文件: frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
    ...
    mSlots[slot].mRequestBufferCalled = true;
     // GraphicsBuffer跨進程傳遞執行unflatten
    *buf = mSlots[slot].mGraphicBuffer;
    return NO_ERROR;
}

文件: frameworks/native/libs/ui/GraphicBuffer.cpp

status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& fds,
                                  size_t& count) {
...

if (handle != nullptr) {
        buffer_handle_t importedHandle;
        status_t err = mBufferMapper.importBuffer(handle, uint32_t(width), uint32_t(height),
                uint32_t(layerCount), format, usage, uint32_t(stride), &importedHandle);
...
        handle = importedHandle;
...

       return NO_ERROR;
}

應用的surface執行dequeueBuffer是在應用進程,具體實現是通過BufferQueueProducer的dequeueBuffer,是在SurfaceFlinger進程,所以需要通過requestBuffer將Buffer映射到應用進程,具體實現是通過importBuffer。真正分配Buffer的地方是在ion,ion是怎么分配Buffer的,以及Buffer怎么實現app和SurfaceFlinger共享,先用一張簡圖概述下,后續會分析。


Buffer共享.png

至此app就拿到了可繪制的Buffer,GPU可以將數據渲染到這塊Buffer上。GPU將數據渲染到Buffer后,會執行eglSwapBuffers交換front和back buffer,eglSwapBuffers會調用queueBuffer,android系統用queueBuffer來請求vsync 喚醒SurfaceFlinger刷新。

queueBuffer

文件: frameworks/native/libs/gui/Surface.cpp

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
    ...
     // dequeue出來的buffer對應的slot
    int i = getSlotFromBufferLocked(buffer);
    ...
    // 傳入一些參數,其中 fenceFd 是由GPU帶過來的,表示GPU渲染是否完成,這里為acquireFence
    IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
            static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
            mTransform ^ mStickyTransform, fence, mStickyTransform,
            mEnableFrameTimestamps);
    ...
    // 調到SurfaceFlinger進程的queueBuffer
    status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
    ...
}

文件: frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
...
    BufferItem item;
...
        // 將數據同步到item對象
        item.mAcquireCalled = mSlots[slot].mAcquireCalled;
        item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
        item.mCrop = crop;
        item.mTransform = transform &
                ~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
        item.mTransformToDisplayInverse =
                (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
        item.mScalingMode = static_cast<uint32_t>(scalingMode);
        item.mTimestamp = requestedPresentTimestamp;
        item.mIsAutoTimestamp = isAutoTimestamp;
        item.mDataSpace = dataSpace;
        item.mHdrMetadata = hdrMetadata;
        item.mFrameNumber = currentFrameNumber;
        item.mSlot = slot;
        item.mFence = acquireFence;
        item.mFenceTime = acquireFenceTime;
        item.mIsDroppable = mCore->mAsyncMode ||
                (mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||
                (mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) ||
                (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
        item.mSurfaceDamage = surfaceDamage;
        item.mQueuedBuffer = true;
        item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;
        item.mApi = mCore->mConnectedApi;
       ...
      
       // 把item放到mQueue里面
       mCore->mQueue.push_back(item);
       // 這里的mCore->mConsumerListener 實際上為ConsumerBase對象
       frameAvailableListener = mCore->mConsumerListener;
       ...
      if (frameAvailableListener != nullptr) {
        frameAvailableListener->onFrameAvailable(item);
      }...
}

文件: frameworks/native/libs/gui/ConsumerBase.cpp

void ConsumerBase::onFrameAvailable(const BufferItem& item) {
    CB_LOGV("onFrameAvailable");

    sp<FrameAvailableListener> listener;
    { // scope for the lock
        Mutex::Autolock lock(mFrameAvailableMutex);
        listener = mFrameAvailableListener.promote();
    }

    if (listener != nullptr) {
        CB_LOGV("actually calling onFrameAvailable");
        // listener實際上是BufferQueueLayer對象
        listener->onFrameAvailable(item);
    }
}

根據類圖關系,frameAvailableListener 實際上是ConsumerBase 對象,在ConsumerBase 初始化的時候進行賦值,通過 mConsumer->consumerConnect(proxy, controlledByApp)接口,所以會調到ConsumerBase::onFrameAvailable, 而mFrameAvailableListener 是BufferQueueLayer -> setContentsChangedListener(mContentsChangedListener) 設的,實際上是 BufferQueueLayer對象,因此最后走到BufferQueueLayer::onFrameAvailable 邏輯里面了。

文件: frameworks/native/services/surfaceflinger/BufferQueueLayer.cpp

void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
   ...
    // 將item 放到mQueueItems里面
    mQueueItems.push_back(item);
    mQueuedFrames++;
   ...
    // 申請vsync去觸發surfaceflinger刷新
    mFlinger->signalLayerUpdate();
    mConsumer->onBufferAvailable(item);
}

文件: frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp

void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
    if (connection->resyncCallback) {
        connection->resyncCallback();
    }
    std::lock_guard<std::mutex> lock(mMutex);

    if (connection->vsyncRequest == VSyncRequest::None) {
        // Vsync請求置為Single
        connection->vsyncRequest = VSyncRequest::Single;
        mCondition.notify_all();
    }
}

讓Surfaceflinger刷新需要2個必要條件: 1. 有requestNextVsync 的請求 2. 軟件vsync計算模型回調 onVSyncEvent創建一個vsyncEvent,涉及到vsync模型,后面會介紹。
到這里,app完成了與SurfaceFlinger的連接以及將應用數據傳給到SurfaceFlinger。這里需要注意的是,其實SurfaceFlinger并不會動Buffer里面的數據,只是中間傳遞的一個過程,Buffer數據是由GPU渲染,display顯示,下篇分析下SurfaceFlinger在刷新時做了哪些動作。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容