Android 9.0 Bluetooth源碼分析(一)藍(lán)牙開啟流程

Android藍(lán)牙打開和關(guān)閉的流程基本一致,這里就從打開的流程進(jìn)行分析。

1 UI

從UI上看藍(lán)牙開關(guān)就是設(shè)置settings里那個(gè)switch開關(guān)派近,藍(lán)牙開關(guān)操作封裝在Settings/bluetooth/BluetoothEnabler.java中描沟,BluetoothEnabler便于藍(lán)牙開關(guān)管理。里面實(shí)現(xiàn)SwitchBar.OnSwitchChangeListener接口沟于,監(jiān)聽狀態(tài)改變尽纽。

@Override
    public void onSwitchChanged(Switch switchView, boolean isChecked) {
          ...........
        //在LocalBluetoothAdapter中打開藍(lán)牙
        if (mLocalAdapter != null) {
            mLocalAdapter.setBluetoothEnabled(isChecked);
        }
        mSwitch.setEnabled(false);
    }

onSwitchChanged里面調(diào)用LocalBluetoothAdapter的setBluetoothEnabled方法念逞,這是藍(lán)牙打開或者關(guān)閉的一個(gè)接口蹭越。接下來看一下LocalBluetoothAdapter:

    public void setBluetoothEnabled(boolean enabled) {
        boolean success = enabled ? mAdapter.enable() : mAdapter.disable();
           ......設(shè)置藍(lán)牙狀態(tài).....
        }

LocalBluetoothAdapter在Settings app和Framworks之間提供了一個(gè)接口去改變藍(lán)牙開關(guān)狀態(tài)昏兆。setBluetoothEnabled調(diào)用BluetoothAdapter.enable方法匙握。然后我們就進(jìn)入framework的BluetoothAdapter中繼續(xù)分析晾咪。


2 framework

public boolean enable() {
        int state = STATE_OFF;
        //已經(jīng)打開返回
        if (isEnabled() == true){
            if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
            return true;
        }
        //Use service interface to get the exact state
        if (mService != null) {
            try {
               state = mService.getState();
            } catch (RemoteException e) {Log.e(TAG, "", e);}
        }
        //當(dāng)前狀態(tài)為打開
        if (state == BluetoothAdapter.STATE_BLE_ON) {
                Log.e(TAG, "BT is in BLE_ON State");
                notifyUserAction(true);
                return true;
        }
        try {
            //通過遠(yuǎn)程服務(wù)打開藍(lán)牙
            return mManagerService.enable();
        } catch (RemoteException e) {Log.e(TAG, "", e);}
        return false;
    }

這個(gè)方法中的關(guān)鍵代碼是“return mManagerService.enable();” mManagerService 對象是由IBluetoothManager 接口代碼實(shí)現(xiàn)的丐枉,IBluetoothManager實(shí)際定義的是AIDL文件朱沃,對應(yīng)的類就是 BluetoothManagerService 類榄攀,在路徑 frameworks/base/core/java/android/bluetooth/BluetoothManagerService 下面 嗜傅。

public boolean enable() {
       if (isBluetoothDisallowed()) {
           if (DBG) {
               Slog.d(TAG, "enable(): not enabling - bluetooth disallowed");
           }
           return false;
        }
        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
            Log.w(TAG,"enable(): not allowed for non-active and non system user");
            return false;
        }
        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                         "Need BLUETOOTH ADMIN permission");//權(quán)限檢查
        if (DBG) {
            Log.d(TAG,"enable():  mBluetooth =" + mBluetooth +
                    " mBinding = " + mBinding);
        }
        synchronized(mReceiver) {
            mQuietEnableExternal = false;
            mEnableExternal = true;
            // waive WRITE_SECURE_SETTINGS permission check 
            sendEnableMsg(false); //通過handler集中處理消息
        }
        if (DBG) Log.d(TAG, "enable returning");
        return true;
}

方法中先判斷是否是系統(tǒng)app操作藍(lán)牙,檢查是否有操作藍(lán)牙的權(quán)限等檩赢,然后關(guān)鍵的代碼是“sendEnableMsg(false)”吕嘀,handler最后調(diào)用handleEnable方法處理該消息。

private void handleEnable(boolean quietMode) {
    mQuietEnable = quietMode;
    synchronized(mConnection) {
    //檢查服務(wù)是否已經(jīng)啟動并且已經(jīng)綁定
    if ((mBluetooth == null) && (!mBinding)) {
        //Start bind timeout and bind
        Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
        mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
        mConnection.setGetNameAddressOnly(false);
        Intent i = new Intent(IBluetooth.class.getName());
        //綁定服務(wù)贞瞒,UserHandle為多用戶考慮
        if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT)) {
            mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
        } else {
            mBinding = true;
        }
    } else if (mBluetooth != null) {
        if (mConnection.isGetNameAddressOnly()) {
            // if GetNameAddressOnly is set, we can clear this flag,
            // so the service won't be unbind
            // after name and address are saved
            mConnection.setGetNameAddressOnly(false);
            //Register callback object
            try {
                mBluetooth.registerCallback(mBluetoothCallback);
            } catch (RemoteException re) {
                Log.e(TAG, "Unable to register BluetoothCallback",re);
            }
        //Inform BluetoothAdapter instances that service is up
        sendBluetoothServiceUpCallback();
    }
    //Enable bluetooth
    try {
        if (!mQuietEnable) {
            //使能藍(lán)牙
            if (!mBluetooth.enable()) {
            Log.e(TAG,"IBluetooth.enable() returned false");
            }
        } else {
        if(!mBluetooth.enableNoAutoConnect()) {
                Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
             }
         }
    } catch (RemoteException e) {
        Log.e(TAG,"Unable to call enable()",e); 
    }
}

這里通過AIDL的方式偶房,調(diào)用Bluetooth App 中的AdapterService 。先綁定服務(wù)军浆,然后注冊Ibluetooth回調(diào)函數(shù)棕洋,之后調(diào)用enable方法方法開啟藍(lán)牙。所以之后就從Framworks 跳到 Bluetooth APP 中繼續(xù)分析乒融。

——————————————————————————————————

3 Bluetooth APP

代碼路徑“Packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService ”掰盘,看里面的 enable(boolean) 的實(shí)現(xiàn)

public synchronized boolean enable(boolean quietMode) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
         debugLog("enable() - Enable called with quiet mode status =  " + mQuietmode);
         mQuietmode = quietMode;
         //由狀態(tài)機(jī)處理開啟操作
         mAdapterStateMachine.sendMessage(AdapterState.BLE_TURN_ON);         
         return true;
     }

AdapterService.enable方法最終向狀態(tài)機(jī)AdapterState發(fā)送AdapterState.BLE_TURN_ON消息,由狀態(tài)機(jī)處理消息赞季。藍(lán)牙狀態(tài)機(jī)的代碼在Packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState 下面愧捕,AdapterState狀態(tài)機(jī)在AdapterService的onCreate方法中他通過一個(gè)靜態(tài)函數(shù)AdapterState.make初始化。

private AdapterState(AdapterService service, AdapterProperties adapterProperties) {
        super("BluetoothAdapterState:");
        addState(mOnState);
        addState(mBleOnState);
        addState(mOffState);
        addState(mPendingCommandState);
        mAdapterService = service;
        mAdapterProperties = adapterProperties; 
        setInitialState(mOffState); //初始狀態(tài)為offstate
    }

setInitialState設(shè)置了該狀態(tài)機(jī)的初始狀態(tài)是OffState申钩,AdapterState.BLE_TURN_ON消息由OffState首先處理次绘,狀態(tài)機(jī)中每個(gè)狀態(tài)通過調(diào)用public boolean processMessage方法處理每個(gè)消息。

        public boolean processMessage(Message msg) {
            AdapterService adapterService = mAdapterService;
            if (adapterService == null) {
          errorLog("Received message in OffState after cleanup: "+msg.what);
                return false;
            }
            debugLog("Current state: OFF, message: " + msg.what);
            switch(msg.what) {
                //初始狀態(tài)是OffState,由OffState處理藍(lán)牙開啟操作
               case BLE_TURN_ON:
                   //通知BluetoothmangerService藍(lán)牙正在開啟
                   notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_TURNING_ON);
                   mPendingCommandState.setBleTurningOn(true);
                   transitionTo(mPendingCommandState);
                   //發(fā)送延遲消息邮偎,檢測打開超時(shí)任務(wù)
             sendMessageDelayed(BLE_START_TIMEOUT, BLE_START_TIMEOUT_DELAY);
                   //批量啟動 profile service
                   adapterService.BleOnProcessStart();
                   break; 
               case USER_TURN_OFF:
                   //TODO: Handle case of service started
        //and stopped without enable
                   break; 
               default:
                   return false;
            }
            return true;
     }

BLE_TURN_ON里面通過回調(diào)通知BluetoothMangerService罗洗,藍(lán)牙正在啟動中,然后將狀態(tài)機(jī)中下個(gè)狀態(tài)設(shè)置為PendingCommandState钢猛,最后調(diào)用adapterService .BleOnProcessStart批量啟動所需的各個(gè)profile server。

    void BleOnProcessStart() {
        debugLog("BleOnProcessStart()");
        //支持的profile service
        Class[] supportedProfileServices = Config.getSupportedProfiles();
        //Initialize data objects
        for (int i=0; i < supportedProfileServices.length;i++) {
            mProfileServicesState.put(supportedProfileServices[i].getName(),
                    BluetoothAdapter.STATE_OFF);
        }
     //初始化遠(yuǎn)程藍(lán)牙設(shè)備
        mRemoteDevices = new RemoteDevices(this);
        mAdapterProperties.init(mRemoteDevices);
        debugLog("BleOnProcessStart() - Make Bond State Machine");
        //啟動藍(lán)牙綁定狀態(tài)狀態(tài)
        mBondStateMachine = BondStateMachine.make(this, mAdapterProperties, mRemoteDevices);
        mJniCallbacks.init(mBondStateMachine,mRemoteDevices);
        //FIXME: Set static instance here???
        setAdapterService(this);
        //Start Gatt service
        setGattProfileServiceState(supportedProfileServices,BluetoothAdapter.STATE_ON);
}

BleOnProcessStart首先獲取支持的profile轩缤,支持的profile在 Config.java 中命迈。接著啟動新的狀態(tài)機(jī)BondStateMachine,最后調(diào)用setGattProfileServiceState方法火的,批量啟動支持的Profile server壶愤,包括GattService,啟動服務(wù)時(shí)會傳入?yún)?shù):ACTION_SERVICE_STATE_CHANGED和BluetoothAdapter.STATE_ON 馏鹤。

然后看packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java啟動的入口onStartCommand征椒,里面沒有什么內(nèi)容,真正的實(shí)現(xiàn)在父類中:

    public int onStartCommand(Intent intent, int flags, int startId) {
        if (DBG) log("onStartCommand()");
        if (mStartError || mAdapter == null) {
          Log.w(mName, "Stopping profile service: device does not have BT");
            doStop(intent);
            return PROFILE_SERVICE_MODE;
        }
        if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM) != PackageManager.PERMISSION_GRANTED) {
            Log.e(mName, "Permission denied!");
            return PROFILE_SERVICE_MODE;
        }
        if (intent == null) {
            Log.d(mName, "Restarting profile service...");
            return PROFILE_SERVICE_MODE;
        } else {
        String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
           if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
                int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                if(state==BluetoothAdapter.STATE_OFF) {
                 Log.d(mName, "Received stop request...Stopping profile...");
                    doStop(intent); //停止
                } else if (state == BluetoothAdapter.STATE_ON) {
                 Log.d(mName, "Received start request. Starting profile...");
                    doStart(intent); //啟動
                }}}
        return PROFILE_SERVICE_MODE;
    }

因?yàn)閱臃?wù)時(shí)傳入的參數(shù)是ACTION_SERVICE_STATE_CHANGED和BluetoothAdapter.STATE_ON湃累,所以調(diào)用doStart勃救。doStart里面調(diào)用了抽象方法start(實(shí)現(xiàn)是在子類GattService里面,做了一些預(yù)處理治力,包括initializeNative蒙秒、啟動一些manager)再調(diào)notifyProfileServiceStateChanged(BluetoothAdapter.STATE_ON),此方法會調(diào)用AdapterService.onProfileServiceStateChanged方法宵统,傳遞STATE_ON消息晕讲,該消息會包裝在MESSAGE_PROFILE_SERVICE_STATE_CHANGED類型里,由AdapterService的handler方法處理

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            debugLog("handleMessage() - Message: " + msg.what);
            switch (msg.what) {
                case MESSAGE_PROFILE_SERVICE_STATE_CHANGED: 
                    debugLog("handleMessage() - MESSAGE_PROFILE_SERVICE_STATE_CHANGED");
                    processProfileServiceStateChanged((String) msg.obj, msg.arg1);
                    break;
            }
                 ........................
            }
        }
    };

processProfileServiceStateChanged處理profile 狀態(tài)改變马澈,里面

//在OffState中瓢省,isBleTurningOn設(shè)置為true
    if (isBleTurningOn) {
         if (serviceName.equals("com.android.bluetooth.gatt.GattService")) {
                debugLog("GattService is started");
                mAdapterStateMachine.sendMessage(mAdapterStateMachine.
                        obtainMessage(AdapterState.BLE_STARTED));
                return;
            }
    }

會向AdapterStateMachine狀態(tài)機(jī)發(fā)送BLE_STARTED消息,根據(jù)之前狀態(tài)機(jī)已經(jīng)由OffState切換成PendingCommandState痊班,所以消息由PendingCommandState狀態(tài)處理,看processMessage的處理

                case BLE_STARTED:
                    //Remove start timeout
                    removeMessages(BLE_START_TIMEOUT);
                    //Enable,調(diào)用底層方法勤婚,開啟藍(lán)牙
                    if (!adapterService.enableNative()) {
                        errorLog("Error while turning Bluetooth on");
                        //開啟失敗,狀態(tài)通知辩块,切換到OffState狀態(tài)
                        notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
                        transitionTo(mOffState);
                    } else {
                    //超時(shí)檢測
                    sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
                    }
                break;

通過調(diào)用adapterService.enableNative()方法蛔六,開始調(diào)用JNI方法,進(jìn)入C/C++層废亭。adapterService.enableNative對應(yīng)的cpp文件為Packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp:

static jboolean enableNative(JNIEnv* env, jobject obj) {
    ALOGV("%s:",__FUNCTION__);
jboolean result = JNI_FALSE;
 if (!sBluetoothInterface) return result;
 int ret = sBluetoothInterface->enable();
    result = (ret == BT_STATUS_SUCCESS || ret == BT_STATUS_DONE) ? JNI_TRUE : JNI_FALSE; //判斷是打開還是關(guān)閉狀態(tài)国章;
    return result;
}

通過調(diào)用“int ret = sBluetoothInterface->enable()”來驅(qū)動底層打開藍(lán)牙開關(guān)。接下來就是C里面對打開藍(lán)牙的實(shí)現(xiàn)豆村。

——————————————————————————————————

4 藍(lán)牙協(xié)議棧

對應(yīng)的代碼在/system/bt/btif/src/bluetooth.cc里面:

static int enable(void) {
  LOG_INFO("%s", __func__);
  if (!interface_ready())//狀態(tài)判斷液兽,已經(jīng)初始化,向下執(zhí)行
    return BT_STATUS_NOT_READY;
  stack_manager_get_interface()->start_up_stack_async();
  return BT_STATUS_SUCCESS;
}

enable方法會調(diào)用start_up_stack_async方法,start_up_stack_async的實(shí)現(xiàn)在stack_manager.c 文件中:

static void start_up_stack_async(void) {
    thread_post(management_thread, event_start_up_stack, NULL);
}

通過thread_post異步event_start_up_stack四啰,來啟動藍(lán)牙棧

// Synchronous function to start up the stack
static void event_start_up_stack(UNUSED_ATTR void *context) {
  if (stack_is_running) {
LOG_DEBUG("%s stack already brought up.", __func__);
    return;
  }
  ensure_stack_is_initialized();//確保宁玫,棧已經(jīng)初始化
  LOG_DEBUG("%s is bringing up the stack.", __func__);
  hack_future = future_new();
  // Include this for now to put btif config into a shutdown-able state
  //加載配置文件,配置文件位于手機(jī)系統(tǒng)目錄/data/misc/bluedroid/bt_config.xml
  module_start_up(get_module(BTIF_CONFIG_MODULE));
  bte_main_enable();//BTE部分的enable
  if (future_await(hack_future) != FUTURE_SUCCESS) {//啟動失敗
    stack_is_running = true; // So stack shutdown actually happens    
    event_shut_down_stack(NULL); //關(guān)閉藍(lán)牙棧柑晒,里面還會調(diào)用
    return;}
  stack_is_running = true;
  LOG_DEBUG("%s finished", __func__);
btif_thread_post(event_signal_stack_up, NULL); //發(fā)送藍(lán)牙棧啟動信息:
}

bte_main_enable()方法中進(jìn)行btsnoop_module和hci_module的啟動以及啟動BTU操作欧瘪,這里就不深入下去了。

如果打開藍(lán)牙成功匙赞,走到btif_thread_post(event_signal_stack_up, NULL)這里佛掖,會在event_signal_stack_up函數(shù)里面調(diào)用HAL_CBACK(bt_hal_cbacks, adapter_state_changed_cb, BT_STATE_ON) ,然后通過回調(diào)調(diào)用adapter_state_changed_cb方法涌庭,通知藍(lán)牙當(dāng)前狀態(tài)BT_STATE_ON芥被。

adapter_state_changed_cb在hardware/libhardware/include/hardware/bluetooth.h文件中被定義:

/** Bluetooth DM callback structure. */
typedef struct {
    /** set to sizeof(bt_callbacks_t) */
    size_t size;
    adapter_state_changed_callback adapter_state_changed_cb;
    ...........
} bt_callbacks_t;

如果C++層有消息傳遞到j(luò)ava層,回調(diào)接口都定義在這里坐榆,adapter_state_changed_cb對應(yīng)的是adapter_state_changed_callback 拴魄。然后通過JNI回調(diào)通知到JAVA層,上層就會收到打開藍(lán)牙的通知席镀。至此匹中,打開藍(lán)牙流程分析到這里。


參考文檔

  1. https://www.2cto.com/kf/201411/352236.html
  2. https://blog.csdn.net/xubin341719/article/details/40402637
  3. http://www.reibang.com/p/181ad20a5bff
  4. https://blog.csdn.net/shichaog/article/details/52728684
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載豪诲,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者职员。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市跛溉,隨后出現(xiàn)的幾起案子焊切,更是在濱河造成了極大的恐慌,老刑警劉巖芳室,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件专肪,死亡現(xiàn)場離奇詭異,居然都是意外死亡堪侯,警方通過查閱死者的電腦和手機(jī)嚎尤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伍宦,“玉大人芽死,你說我怎么就攤上這事〈瓮荩” “怎么了关贵?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卖毁。 經(jīng)常有香客問我揖曾,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任炭剪,我火速辦了婚禮练链,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘奴拦。我一直安慰自己媒鼓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布错妖。 她就那樣靜靜地躺著隶糕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪站玄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天濒旦,我揣著相機(jī)與錄音株旷,去河邊找鬼。 笑死尔邓,一個(gè)胖子當(dāng)著我的面吹牛晾剖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播梯嗽,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼齿尽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了灯节?” 一聲冷哼從身側(cè)響起循头,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎炎疆,沒想到半個(gè)月后卡骂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡形入,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年全跨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亿遂。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浓若,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛇数,到底是詐尸還是另有隱情挪钓,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布耳舅,位于F島的核電站诵原,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绍赛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一蔓纠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吗蚌,春花似錦腿倚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至箩言,卻和暖如春硬贯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陨收。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工饭豹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人务漩。 一個(gè)月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓拄衰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饵骨。 傳聞我的和親對象是個(gè)殘疾皇子翘悉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354