第四章:PowerManagerService

簡述

PowerManagerServcie是android系統電源管理的核心服務,它在Framework層建立起一個策略控制方案,向下決策HAL層以及kernel層來控制設備待機狀態,主要功能是控制系統待機狀態,屏幕顯示,亮度調節,光線/距離傳感器的控制等。

除了與應用程序交互之外,還要與系統中其它模塊配合,在提供良好的能源管理同時提供友好的用戶體驗。比如聽音樂時持續保持系統喚醒,應用通知來臨喚醒手機屏幕等場景

分析一個服務,首先要從它對應用層提供的api PowerManager入手,觀察提供了哪些接口調用;

  • Wakeup():強制系統從睡眠狀態喚醒,此接口對應用是不開放的,應用想喚醒系統必須通過設置亮屏標志(后面即將講到);
  • gotoSleep():強制系統進入到睡眠狀態,此接口也是應用不開放。
  • userActivity():向PowerManagerService報告影響系統休眠的用戶活動,重計算滅屏時間,背光亮度等,例如觸屏,劃屏,power鍵等用戶活動;
  • Wakelock:wakelock是PowerManager的一個內部類,提供了相關的接口來操作wakelock鎖,比如newWakeLock()方法來創建wakelock鎖,acquire()和release()方法來申請和釋放鎖。下面例子有介紹!
  • isDeviceIdleMode():返回設備當前的狀態,如果處于Idle狀態,則返回true,Idle狀態是在手機長時間沒有被使用以及沒有運動的情況下,手機進入到一種Doze低功耗的模式下,這種狀態下手機可能會關掉網絡數據訪問,可以通過監視DEVICE_IDLE_MODE_CHANGED這個廣播信息,來監控手機狀態的改變

系統層級圖

image.png

電源管理架構
主要分為四個層次:

  • 應用接口層:PowerManager.java中開放給應用一系列接口,應用可以調用PM的接口申請wakelock,喚醒系統,使系統進入睡眠等操作;
  • Framework層:PowerManagerService.java計算系統中和Power相關的計算,是整個電源管理的決策系;
  • HAL層:該層只有一個power.c文件,該文件通過上層傳下來的參數,向/sys/power/wake_lock或者/sys/power/wake_unlock文件節點寫數據來與kernel進行通信,主要功能是申請/釋放鎖,維持屏幕亮滅。
  • Kernel層:內核層實現電源管理的方案主要包含三個部分:
    Kernel/power/:實現了系統電源管理框架機制。
    Arch/arm(ormips or powerpc)/mach-XXX/pm.c:實現對特定板的處理器電源管理。
    drivers/power:是設備電源管理的基礎框架,為驅動提供了電源管理接口。

一、 初始化

跟其他系統服務一樣,PowerManagerService也是繼承于SystemService并通過SystemServer啟動。
SystemServer啟動PowerManagerService服務
frameworks/base/services/java/com/android/server/SystemServer.java

private void startBootstrapServices() {
  ......
  mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
  ......
}

PowerManagerService構造
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

public final class PowerManagerService extends SystemService
        implements Watchdog.Monitor {
    public PowerManagerService(Context context) {
        super(context);
        mContext = context;
//創建消息處理線程,并啟動
        mHandlerThread = new ServiceThread(TAG,
                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
        mHandlerThread.start();
//創建Hanlder對象處理消息
        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
        mConstants = new Constants(mHandler);
        mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
        mBatterySaverPolicy = new BatterySaverPolicy(mHandler);

        synchronized (mLock) {
            //創建"PowerManagerService.WakeLocks"的SuspendBlocker
            mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
           // 創建"PowerManagerService.Display"的SuspendBlocker
            mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
           // 請求DisplaySuspendBlocker,禁止系統進入休眠
            mDisplaySuspendBlocker.acquire();
            mHoldingDisplaySuspendBlocker = true;
            mHalAutoSuspendModeEnabled = false;
            mHalInteractiveModeEnabled = true;
           // 設置mWakefulness為喚醒狀態
            mWakefulness = WAKEFULNESS_AWAKE;

            sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
           // 進入到native層初始化
            nativeInit();
            nativeSetAutoSuspend(false);
            nativeSetInteractive(true);
            nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
        }
    }
    ......
}

PowerManagerService構造函數中首先創建了處理消息的進程及對應的handler對象以進行消息處理,然后創建SuspendBlocker對象,用于WakeLocks與Display,并設置mWakefulness的初始狀態為WAKEFULNESS_AWAKE,最后進入到native層初始化。下面先看一下關于mWakefulness的定義。
frameworks/base/core/java/android/os/PowerManagerInternal.java

/**
 * 設備處于休眠狀態,只能被wakeUp()喚醒.
 */
public static final int WAKEFULNESS_ASLEEP = 0;

/**
 * 設備處于正常工作(fully awake)狀態.
 */
public static final int WAKEFULNESS_AWAKE = 1;

/**
 * 設備處于播放屏保狀態.
 */
public static final int WAKEFULNESS_DREAMING = 2;

/**
 * 設備處于doze狀態,只有低耗電的屏保可以運行,其他應用被掛起.
 */
public static final int WAKEFULNESS_DOZING = 3;

繼續回到PowerManagerService構造函數的native初始化中,首先來看nativeInit的實現。
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static void nativeInit(JNIEnv* env, jobject obj) {
   // 創建一個全局對象,引用PMS
   gPowerManagerServiceObj = env->NewGlobalRef(obj);
   // 利用hw_get_module加載power模塊
   status_t err = hw_get_module(POWER_HARDWARE_MODULE_ID,
           (hw_module_t const**)&gPowerModule);
   if (!err) {
       gPowerModule->init(gPowerModule);
   } else {
       ALOGE("Couldn't load %s module (%s)", POWER_HARDWARE_MODULE_ID, strerror(-err));
   }
}

nativeInit的主要任務時裝載power模塊,該模塊由廠商實現,以高通為例,如下。
device/qcom/common/power/power.c

tatic struct hw_module_methods_t power_module_methods = {
    .open = NULL,
};

struct power_module HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = POWER_MODULE_API_VERSION_0_2,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = POWER_HARDWARE_MODULE_ID,
        .name = "QCOM Power HAL",
        .author = "Qualcomm",
        .methods = &power_module_methods,
    },

    .init = power_init,
    .powerHint = power_hint,
    .setInteractive = set_interactive,
};

power_module中實現了init,powerHint,setInteractive,nativeInit最終調用到HAL power模塊的power_init具體實現中。接著看native初始化nativeSetAutoSuspend的實現。
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
    if (enable) {
        ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off");
        autosuspend_enable();
    } else {
        ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on");
        autosuspend_disable();
    }
}
static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
    if (gPowerModule) {
        if (enable) {
            ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");
            gPowerModule->setInteractive(gPowerModule, true);
        } else {
            ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");
            gPowerModule->setInteractive(gPowerModule, false);
        }
    }
}

static void nativeSetFeature(JNIEnv *env, jclass clazz, jint featureId, jint data) {
    int data_param = data;

    if (gPowerModule && gPowerModule->setFeature) {
        gPowerModule->setFeature(gPowerModule, (feature_t)featureId, data_param);
    }
}

system/core/libsuspend/autosuspend.c

int autosuspend_disable(void)
{
    int ret;

    ret = autosuspend_init();
    if (ret) {
        return ret;
    }

    ALOGV("autosuspend_disable\n");

    if (!autosuspend_enabled) {
        return 0;
    }

    ret = autosuspend_ops->disable();
    if (ret) {
        return ret;
    }

    autosuspend_enabled = false;
    return 0;
}

同nativeInit一樣,最終都是調用到HAL power模塊的具體實現中。

二、 啟動

下面繼續看PowerManagerService在系統啟動過程中回調onStart(),onBootPhase(),systemReady()的實現。
啟動服務SystemServiceManager.onStart
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

public void onStart() {
    publishBinderService(Context.POWER_SERVICE, new BinderService());
    publishLocalService(PowerManagerInternal.class, new LocalService());

    Watchdog.getInstance().addMonitor(this);
    Watchdog.getInstance().addThread(mHandler);
}

private final class BinderService extends IPowerManager.Stub {
    ......
}

private final class LocalService extends PowerManagerInternal {
    ......
}

onStart()中發布了BinderServiceLocalService分別供其他進程,進程內其他服務調用,并將PowerManagerService加入到Watchdog監控中。

啟動服務SystemServiceManager.onBootPhase

public void onBootPhase(int phase) {
    synchronized (mLock) {
        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
            ......
        } else if (phase == PHASE_BOOT_COMPLETED) {
            final long now = SystemClock.uptimeMillis();
            // 設置mBootCompleted狀態
            mBootCompleted = true;
            mDirty |= DIRTY_BOOT_COMPLETED;
            // 更新userActivity及PowerState,后面分析
            userActivityNoUpdateLocked(
                    now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
            updatePowerStateLocked();
            // 執行mBootCompletedRunnables中的runnable方法
            if (!ArrayUtils.isEmpty(mBootCompletedRunnables)) {
                Slog.d(TAG, "Posting " + mBootCompletedRunnables.length + " delayed runnables");
                for (Runnable r : mBootCompletedRunnables) {
                    BackgroundThread.getHandler().post(r);
                }
            }
            mBootCompletedRunnables = null;
        }
    }
}

onBootPhase()中主要設置mBootCompleted狀態,更新PowerState狀態,并執行mBootCompletedRunnables中的runnables方法(低電量模式會設置)。
啟動服務SystemServiceManager.systemReady

public void systemReady(IAppOpsService appOps) {
    synchronized (mLock) {
        mSystemReady = true;
        // 獲取AppOpsService
        mAppOps = appOps;
        // 獲取DreamManager 屏保
        mDreamManager = getLocalService(DreamManagerInternal.class);
        // 獲取DisplayManagerService  屏幕顯示
        mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
      //窗口策略
        mPolicy = getLocalService(WindowManagerPolicy.class);
        // 獲取mBatteryService 電池電量
        mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);

        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        // 獲取屏幕默認,最大,最小亮度
        mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
        mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
        mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
        // 獲取SensorManager
        SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());

        mBatteryStats = BatteryStatsService.getService();
        // 創建Notifier對象,用于廣播power state的變化
        mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
                mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
                mPolicy);
        // 無線充電檢測
        mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
                createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
                mHandler);
        // 監聽設置的變化
        mSettingsObserver = new SettingsObserver(mHandler);

        mLightsManager = getLocalService(LightsManager.class);
        mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);

        // Initialize display power management.
        mDisplayManagerInternal.initPowerManagement(
                mDisplayPowerCallbacks, mHandler, sensorManager);

        // Register for settings changes.
        final ContentResolver resolver = mContext.getContentResolver();
        resolver.registerContentObserver(Settings.Secure.getUriFor(
                Settings.Secure.SCREENSAVER_ENABLED),
        ......
        IVrManager vrManager =
                (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);
        try {
            vrManager.registerListener(mVrStateCallbacks);
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to register VR mode state listener: " + e);
        }
        // 讀取配置
        readConfigurationLocked();
        updateSettingsLocked();
        mDirty |= DIRTY_BATTERY_STATE;
        updatePowerStateLocked();
    }

    // Register for broadcasts from other components of the system.
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
    filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
    mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);

    filter = new IntentFilter();
    filter.addAction(Intent.ACTION_DREAMING_STARTED);
    filter.addAction(Intent.ACTION_DREAMING_STOPPED);
    mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);

    filter = new IntentFilter();
    filter.addAction(Intent.ACTION_USER_SWITCHED);
    mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);

    filter = new IntentFilter();
    filter.addAction(Intent.ACTION_DOCK_EVENT);
    mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
}

總而言之在SystemReady方法中完成的主要工作如下:

  • 1.獲取與PowerManagerServcie相關的系統服務以及本地服務;
    獲取屏幕最大,最小以及默認亮度值;
  • 2.創建SensorManager 對象,用于和SensorService交互;
  • 3.創建Notifier對象,用于通知系統中電源狀態的改變;
  • 4.創建WirelessChargerDetector對象,用于檢測無線充電的傳感器(市面上支持的手機較少)
  • 5.調用DisplayManagerService的initPowerManagement()方法來初始化Power顯示模塊。
  • 6.注冊SettingsObserver監聽系統設置的變化

userActivity
userActivity是定義在PowerManager中的SystemApi,用戶向PowerManagerService報告用戶活動,以更新PowerManagerService內部時間/狀態值,推遲系統休眠的時間。
PowerManager中userActivity請求調用服務端PowerManagerService BinderService的userActivity,即調用內部方法userActivityNoUpdateLocked

private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
    // 如果發生時間是上一次休眠或喚醒前,或當前沒有開機完成到systemReady,不采取操作直接返回
    if (eventTime < mLastSleepTime || eventTime < mLastWakeTime
            || !mBootCompleted || !mSystemReady) {
        return false;
    }

    try {
        // 更新mLastInteractivePowerHintTime時間
        if (eventTime > mLastInteractivePowerHintTime) {
            powerHintInternal(POWER_HINT_INTERACTION, 0);
            mLastInteractivePowerHintTime = eventTime;
        }

        // 通過mNotifier通知BatteryStats UserActivity事件
        mNotifier.onUserActivity(event, uid);

        if (mUserInactiveOverrideFromWindowManager) {
            mUserInactiveOverrideFromWindowManager = false;
            mOverriddenTimeout = -1;
        }

        // 如果系統處于休眠狀態,不進行處理
        if (mWakefulness == WAKEFULNESS_ASLEEP
                || mWakefulness == WAKEFULNESS_DOZING
                || (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
            return false;
        }

        // 根據flag是否在已變暗的情況下是否重啟活動超時更新mLastUserActivityTimeNoChangeLights或mLastUserActivityTime
        // 并且設置mDirty DIRTY_USER_ACTIVITY
        if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
            if (eventTime > mLastUserActivityTimeNoChangeLights
                    && eventTime > mLastUserActivityTime) {
                mLastUserActivityTimeNoChangeLights = eventTime;
                mDirty |= DIRTY_USER_ACTIVITY;
                return true;
            }
        } else {
            if (eventTime > mLastUserActivityTime) {
                mLastUserActivityTime = eventTime;
                mDirty |= DIRTY_USER_ACTIVITY;
                return true;
            }
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }
    return false;
}

三、 應用場景

例如:長連接需要在后臺服務需要持續在線;安卓系統底層優化策略會在系統休眠這段時間調整CPU的運作,我們需要將應用保持CPU一直運轉。
mqtt或者websocket等長連接框架就需要如此!

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock w1 = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "MyTag");
w1.acquire();
//在這個過程,屏幕會保持光亮! 
//或者網絡協議執行心跳包
w1.release();

上述newWakeLock( )的第一個flag標記,這些標記不同程度的影響系統電源.
這些標記都是獨占的,并且每次只能指定其中一個。

  • PARTIAL_WAKE_LOCK:保持CPU 運轉,屏幕和鍵盤燈有可能是關閉的。
  • SCREEN_DIM_WAKE_LOCK:保持CPU 運轉,允許保持屏幕顯示但有可能是灰的,允許關閉鍵盤燈
  • SCREEN_BRIGHT_WAKE_LOCK:保持CPU 運轉,允許保持屏幕高亮顯示,允許關閉鍵盤燈
  • FULL_WAKE_LOCK:保持CPU 運轉,保持屏幕高亮顯示,鍵盤燈也保持亮度

四、 控制系統休眠

Android設備的休眠和喚醒主要基于WakeLock機制。WakeLock是一種上鎖機制,只要有進程獲得了WakeLock鎖系統就不會進 入休眠。例如,在下載文件或播放音樂時,即使休眠時間到了,系統也不能進行休眠。WakeLock可以設置超時,超時后會自動解鎖。

應用使用WakeLock功能前,需要先使用new WakeLock()接口創建一個WakeLock類對象,然后調用它的acquire()方法禁止系統休眠,應用完成工作后調用release()方法來恢復休眠機制,否則系統將無法休眠,直到耗光所有電量。

WakeLock類中實現acquire()release()方法實際上是調用了PowerManagerServiceacquireWakeLock()releaseWakeLock()方法。

updatePowerStateLocked()PowerManagerService的核心函數;在執行完申請鎖,釋放鎖,用戶事件,強制喚醒/睡眠等操作都需要調用updatePowerStateLocked()來更新電源狀態

wakelock
Wakelock是android系統上特有的電源管理機制,只要有應用拿著這個鎖,系統就不能進入睡眠狀態,在上層不同的應用程序可以持有多個不同的wakelock鎖,但是反映到底層就只有三種:控制系統休眠PowerManagerService.WakeLock,控制屏幕顯示的PowerManagerService.Display和控制電源狀態改變通知的PowerManagerService.Broadcasts

PowerManagerService有acquire()加鎖release()解鎖`兩種狀態,加鎖有兩種方式:

  • 第一種是永久的鎖住,這樣的鎖除非顯式的放開,否則是不會解鎖的,所以這種鎖用起來要非常的小心(默認)。
    acquire():申請wakelock永久鎖(默認),需要手動release
  • 第二種鎖是超時鎖,這種鎖會在鎖住后一段時間解鎖。
    acquire(long timeout):申請wakelock超時鎖,timeout為設置的超時時間,超時自動release掉該wakelock。

應用程序在使用wakelock前,必須在其manifest.xml文件中注冊android.permission.WAKE_LOCK權限;

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

推薦閱讀更多精彩內容