【Android R】車(chē)載 Android 核心服務(wù) - CarService 解析

前言

在之前的文章從應(yīng)用工程師的角度再談車(chē)載 Android 系統(tǒng)中提到了"CarService是車(chē)載Android系統(tǒng)的核心服務(wù)之一,所有應(yīng)用都需要通過(guò)CarService來(lái)查詢(xún)、控制整車(chē)的狀態(tài)",不僅僅是車(chē)輛控制,實(shí)際上CarService幾乎就是整個(gè)車(chē)載Framework最核心的組件,這也讓CarService成了各種bug的重災(zāi)區(qū),一部分原因就是開(kāi)發(fā)同學(xué)對(duì)于CarService的運(yùn)行原理與實(shí)現(xiàn)方式理解的不夠深,那么本篇我們就來(lái)講解Android Automotive R上CarService是如何實(shí)現(xiàn)。

本文提到的CarService主要是基于原生Android Automotive,實(shí)際量產(chǎn)的各種車(chē)載Android系統(tǒng)中由于業(yè)務(wù)、技術(shù)、人員配置等各方面原因會(huì)對(duì)CarService做進(jìn)一步的拆分,所以功能上會(huì)與原生的CarService會(huì)存在差異。

CarService 概述

CarService 源碼位置:/packages/services/Car/service/

簡(jiǎn)介

通過(guò)之前的文章我們了解到,Android系統(tǒng)主要應(yīng)用于中控和副駕屏幕,原生的車(chē)載Android本質(zhì)上可以看作是Android OS + Automotive Services + Automotive APPs組成的系統(tǒng)。用戶(hù)會(huì)通過(guò)顯示在中控屏幕上App,將對(duì)車(chē)輛的操作信息通過(guò)Car API 傳遞給Framework Service,Service再通過(guò)HIDL將信息處理后傳遞給HAL ServiceHAL Service再將數(shù)據(jù)處理后傳遞給MCUMCU通過(guò)CAN bus將信息傳遞給汽車(chē)上的各個(gè)ECU(中間忽略了一些不需要理解的步驟),然后由ECU控制汽車(chē)的機(jī)械零部件進(jìn)行活動(dòng),這樣就完成了一次從Android APP到車(chē)輛機(jī)械零部件間的通信。

HIDL是一種類(lèi)似于AIDL的跨進(jìn)程通信手段,主要應(yīng)用于HAL層程序的跨進(jìn)程通信。Google在Android 8中引入并在Android 10中廢棄,Android 10 以后被AIDL取代。

以上通信過(guò)程起到承上啟下作用的Framework Service就是我們的主角 — CarService

原生Android Automotive 系統(tǒng)里CarService實(shí)現(xiàn)的功能非常多,架構(gòu)圖里描述的只是其中的冰山一角,架構(gòu)圖描述的意義只是為了引出CarService

CarService 的組成

作為 Android Automotive 的核心進(jìn)程,原生的CarService業(yè)務(wù)量非常龐大,包含了許多與汽車(chē)相關(guān)的服務(wù),主要有以下幾個(gè):

  • CarPropertyService

此類(lèi)實(shí)現(xiàn)ICarProperty的binder接口。有助于更容易地創(chuàng)建處理車(chē)輛屬性的多個(gè)Manager。

  • CarInputService

CarInputService通過(guò)車(chē)輛HAL監(jiān)控和處理輸入事件。

  • CarLocationService

此服務(wù)在車(chē)輛停放時(shí)存儲(chǔ)LocationManager中最后一個(gè)已知位置,并在車(chē)輛通電時(shí)恢復(fù)該位置。

  • CarMediaService

CarMediaService管理汽車(chē)應(yīng)用程序的當(dāng)前活動(dòng)媒體源。這與MediaSessionManager的活動(dòng)會(huì)話不同,因?yàn)橥粫r(shí)間內(nèi)車(chē)內(nèi)只能有一個(gè)活動(dòng)源。

在車(chē)內(nèi),活動(dòng)的媒體源不一定有活動(dòng)的MediaSession,例如,如果只是在瀏覽它。但是,該源仍然被視為活動(dòng)源,并且應(yīng)該是任何媒體相關(guān)UI(媒體中心、主屏幕等)中顯示的源。

  • CarPowerManagementService

汽車(chē)電源管理服務(wù)。控制電源狀態(tài)并與系統(tǒng)的其他部分交互以確保其自身狀態(tài)。

  • CarProjectionService

汽車(chē)投屏服務(wù)。

  • CarAudioService

負(fù)責(zé)與汽車(chē)音響系統(tǒng)交互的服務(wù)。

  • AppFocusService

應(yīng)用程序焦點(diǎn)服務(wù)確保一次只有一個(gè)應(yīng)用程序類(lèi)型的實(shí)例處于活動(dòng)狀態(tài)。

  • GarageModeService

車(chē)庫(kù)模式。車(chē)庫(kù)模式啟用車(chē)內(nèi)空閑時(shí)間。

  • InstrumentClusterService

負(fù)責(zé)與汽車(chē)儀表盤(pán)交互的服務(wù)。

  • CarPackageManagerService

汽車(chē)包管理服務(wù)。

  • CarUserService

汽車(chē)多用戶(hù)服務(wù)。在啟動(dòng)時(shí)管理用戶(hù)。包括:

  1. 創(chuàng)建用作驅(qū)動(dòng)程序的用戶(hù)。
  2. 創(chuàng)建用作乘客的用戶(hù)。
  3. 首次運(yùn)行時(shí)創(chuàng)建輔助管理員用戶(hù)。
  4. 切換駕駛員。
  • CarStorageMonitoringService

提供存儲(chǔ)監(jiān)視數(shù)據(jù)(如I/O統(tǒng)計(jì)數(shù)據(jù))的服務(wù)。為了接收此類(lèi)數(shù)據(jù),用戶(hù)需要實(shí)現(xiàn)IIoStatsListener并根據(jù)此服務(wù)注冊(cè)自己。

  • CarBluetoothService

車(chē)載藍(lán)牙服務(wù)-維護(hù)當(dāng)前用戶(hù)的藍(lán)牙設(shè)備和配置文件連接。

  • FixedActivityService

監(jiān)控顯示器頂部的Activity,并確保在固定模式下的Activity在崩潰或因任何原因進(jìn)入后臺(tái)時(shí)重新啟動(dòng)。此組件還監(jiān)視目標(biāo)包的更新,并在更新完成后重新啟動(dòng)它。

  • CarBugreportManagerService

Bug report服務(wù)

  • CarConfigurationService

該服務(wù)將查看系統(tǒng)上的默認(rèn)JSON配置文件并解析其結(jié)果。該服務(wù)將查找映射到R.raw.car_config的JSON文件。如果此值不存在或格式不正確,則此服務(wù)不會(huì)失敗;相反,它返回各種配置的默認(rèn)值。

  • CarDiagnosticService

汽車(chē)診斷服務(wù)。工程模式會(huì)用到此服務(wù)。

  • CarDrivingStateService

推斷車(chē)輛當(dāng)前駕駛狀態(tài)的服務(wù)。它通過(guò)偵聽(tīng)CarPropertyService的相關(guān)屬性來(lái)計(jì)算駕駛狀態(tài)。

  • CarExperimentalFeatureServiceController

控制與ExperimentalCarService的綁定以及實(shí)驗(yàn)功能的接口。

  • CarFeatureController

控制汽車(chē)特性的部件。

  • CarNightService

用于處理用于將車(chē)輛設(shè)置為夜間模式的事件。

  • CarOccupantZoneService

用于實(shí)現(xiàn)CarOccupantZoneManagerAPI的服務(wù)。

  • CarTestService

允許測(cè)試/模擬車(chē)輛HAL的服務(wù)。該服務(wù)直接使用車(chē)輛HAL API,因?yàn)檐?chē)輛HAL模擬無(wú)論如何都需要直接訪問(wèn)該級(jí)別。

  • CarUxRestrictionsManagerService

用戶(hù)體驗(yàn)限制的服務(wù)。根據(jù)監(jiān)聽(tīng)到的車(chē)輛當(dāng)前駕駛狀態(tài),限制HMI顯示。

  • OccupantAwarenessService

一種服務(wù),通過(guò)HAL邊界監(jiān)聽(tīng)占用者感知檢測(cè)系統(tǒng),并通過(guò)OccupantAwarenessManager將數(shù)據(jù)暴露給Android中的系統(tǒng)客戶(hù)端。

  • SystemActivityMonitoringService

監(jiān)控AMS新Activity或Service啟動(dòng)的服務(wù)。

  • SystemStateControllerService

系統(tǒng)狀態(tài)控制服務(wù)。原生系統(tǒng)中是一個(gè)空服務(wù),并沒(méi)有實(shí)現(xiàn)。

  • CarMonitoringService

監(jiān)視應(yīng)用程序資源使用情況的服務(wù)。

  • CarTrustedDeviceService

汽車(chē)服務(wù)中啟用受信任設(shè)備功能的部分。可信設(shè)備是一項(xiàng)功能,其中遠(yuǎn)程設(shè)備注冊(cè)為可信設(shè)備,可以授權(quán)Android用戶(hù)而不是用戶(hù)輸入密碼或PIN。

  • CarUserNoticeService

向用戶(hù)顯示初始通知UI的服務(wù)。它僅在啟用設(shè)置時(shí)啟動(dòng)它,并根據(jù)用戶(hù)的請(qǐng)求通知UI自行關(guān)閉。

  • VmsBrokerService

VMS客戶(hù)端實(shí)現(xiàn),使用HAL特定消息編碼將VmsPublisher/VmsSubscriber API調(diào)用代理到車(chē)輛HAL。

  • CarWatchdogService

實(shí)現(xiàn)CarWatchdogManagerAPI的服務(wù)。CarWatchdogService作為汽車(chē)監(jiān)控中介運(yùn)行,它檢查客戶(hù)端的健康狀況,并將結(jié)果報(bào)告給汽車(chē)監(jiān)控服務(wù)器。

加粗的各個(gè)Service屬于CarService中比較重要的功能,以后都會(huì)單獨(dú)開(kāi)坑講。

往簡(jiǎn)單地說(shuō),車(chē)載Framework的功能性開(kāi)發(fā)基本就是圍繞著CarService中的這些服務(wù)做增改,如果能把所有這些服務(wù)和背后原理都理解透徹,車(chē)載Framework基本就算完事了。當(dāng)然可能也有些過(guò)于理想化了,因?yàn)檫€有android自身的Framework需要修改,比如網(wǎng)絡(luò)系統(tǒng)、藍(lán)牙協(xié)議棧等等。


以上就是Android Automotive中CarService支持的所有功能,雖然冠名了xxxService但這些服務(wù)其實(shí)并不是四大組件意義上的Service,它們沒(méi)有繼承自android.app.Service,相反它們都繼承自ICarxxxx.Stub,本質(zhì)上屬于AIDL接口的實(shí)現(xiàn)類(lèi)。到這一步也可以看出CarService本質(zhì)上只是作為這些服務(wù)的容器而存在的,本身并沒(méi)有實(shí)現(xiàn)業(yè)務(wù)邏輯上的功能。

既然這些Service都是AIDL接口的實(shí)現(xiàn)類(lèi),本質(zhì)上就是AIDL的Server端,那應(yīng)用就還需要通過(guò)相應(yīng)的API SDK才能調(diào)用Server的方法,這個(gè)API SDK就是Car API。

Car API 使用方式

不同的公司或車(chē)機(jī)系統(tǒng)項(xiàng)目對(duì)于Car API的定位、實(shí)現(xiàn)并不相同,本文主要從原生Android Automotive的角度介紹。

Car API 源碼地址:packages/services/Car/car-lib/

Car API 簡(jiǎn)介

在上面的介紹中,我們提到CarService中各個(gè)服務(wù)本質(zhì)上是AIDL接口的實(shí)現(xiàn)類(lèi),屬于Server端,而對(duì)應(yīng)的Client端就需要一個(gè)IBinder對(duì)象來(lái)訪問(wèn)Server端的方法,這些IBinder對(duì)象在Car API中被封裝在一個(gè)個(gè)XXXManager類(lèi)中。

Car API與CarService中的服務(wù),名稱(chēng)上存在對(duì)應(yīng)關(guān)系,所以很好理解。例如:CarWatchdogManager對(duì)應(yīng)CarWatchdogServiceCarMediaManager對(duì)應(yīng)CarMediaService

不過(guò)也有例外:CarInfoManagerCarSensorManagerCarHvacManagerCarCabinManagerCarVendorExtensionManager都對(duì)應(yīng)CarPropertyService。但是在Android 11中這些Manager都已經(jīng)過(guò)時(shí),Google建議統(tǒng)一使用CarPropertyManager

實(shí)際項(xiàng)目中我們不一定要按照Google建議的那樣編寫(xiě)Car API,可以按實(shí)際情況實(shí)施。我個(gè)人也經(jīng)歷過(guò)某個(gè)把Car API源碼整個(gè)移除,從頭重寫(xiě)CarService的項(xiàng)目。

編譯 Car API

在使用Car API之前,我們需要先將Car API編譯成jar也就是CarLib,這樣才能讓其它的系統(tǒng)應(yīng)用使用。

編譯CarLib有三種不同指令:

1)make android.car

編譯成功后的jar存放在/out/soong/.intermediates/packages/services/Car/car-lib/android.car/android_common/javac/目錄下。

編譯出的CarLib庫(kù)包含Car API中定義的所有方法以及實(shí)現(xiàn)細(xì)節(jié)。導(dǎo)入到android studio中打開(kāi)后,如下所示:

2)make android.car-system-stubs

編譯成功后的jar存放在/out/soong/.intermediates/packages/services/Car/car-lib/android.car-system-stubs/android_common/javac/目錄下。

編譯出的CarLib庫(kù)包含CarAPI中定義的所有方法,但是不包含實(shí)現(xiàn)細(xì)節(jié),一些與實(shí)現(xiàn)細(xì)節(jié)有關(guān)的變量也會(huì)被隱藏。實(shí)際項(xiàng)目中這種模式較為常用。導(dǎo)入到android studio中打開(kāi)后,如下所示:

3)make android.car-stubs

編譯成功后的jar存放在/out/soong/.intermediates/packages/services/Car/car-lib/android.car-stubs/android_common/javac/目錄下。

編譯出的CarLib庫(kù)僅包含沒(méi)有被@SystemApi修飾方法,而且方法同樣不包含實(shí)現(xiàn)細(xì)節(jié),是最嚴(yán)格的編譯模式。此模式下編譯出的CarLib甚至已經(jīng)沒(méi)有CarDiagnosticManager這個(gè)系統(tǒng)API了。

以上三個(gè)指令也可以一起使用

上述的編譯步驟對(duì)應(yīng)的是使用Android Studio開(kāi)發(fā)的系統(tǒng)應(yīng)用。原生android automotive中的系統(tǒng)應(yīng)用則是直接在android.bp中將CarLib引入,可以參考Settings/Android.bp第81行。

使用 Car API

Car API 的使用并不復(fù)雜,大致有以下幾個(gè)步驟。

通過(guò)Car.createCar()方法可以創(chuàng)建出Car的對(duì)象。

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
    Car carApiClient = Car.createCar(context, mCarServiceConnection);
}

通過(guò)getPackageManager().hasSystemFeature(String string)判斷系統(tǒng)是否支持特定的模塊功能

Car.createCar()需要傳入ServiceConnection,并在service連接成功后,獲取想要的Manager實(shí)例,實(shí)現(xiàn)方式如下:

private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                CarHvacManager manager = (CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE);
            } catch (CarNotConnectedException e) {
                Log.e(TAG, "Car not connected in onServiceConnected");
            }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
};

構(gòu)建出Car對(duì)象后還需要調(diào)用connect()才會(huì)連接到CarService上。

carApiClient.connect();

connect()只能調(diào)用一次,如果當(dāng)前已經(jīng)處于連接狀態(tài),再次調(diào)用connect()會(huì)拋出異常,client如果沒(méi)有捕獲該異常,則會(huì)引起client端程序崩潰(血的教訓(xùn)!)。

@Deprecated
public void connect() throws IllegalStateException {
    synchronized (mLock) {
        if (mConnectionState != STATE_DISCONNECTED) {
            throw new IllegalStateException("already connected or connecting");
        }
        mConnectionState = STATE_CONNECTING;
        startCarService();
    }
}

connect()對(duì)應(yīng)的還有disconnect()

carApiClient.disconnect();

不知道你有沒(méi)有注意到,connect()被標(biāo)記為Deprecated過(guò)時(shí)的方法了。

這是因?yàn)樵赼ndroid 10 以后,Google改寫(xiě)了Car API的使用方式,Android 10以后構(gòu)建Car對(duì)象不再建議傳入ServiceConnection而是使用下面的方法:

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
    Car carApiClient = Car.createCar(context);
    CarHvacManager manager = (CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE);
}

Android 10以后Car Api的調(diào)用方式由異步方法改為了同步方法,保留了disconnect(),但是不再需要調(diào)用connect(),這樣使用起來(lái)更簡(jiǎn)單。

這種調(diào)用方式是樂(lè)觀的認(rèn)為CarService不會(huì)發(fā)生異常,與CarService的連接也不會(huì)斷開(kāi)。但是如果CarService發(fā)生異常,連接被斷開(kāi)的話,client端調(diào)用方就會(huì)直接被殺死,使用下面這種調(diào)用方式可以避免這種情況。

Car car = Car.createCar(this, workThreadHandler, 2000, new Car.CarServiceLifecycleListener() {
    @Override
    public void onLifecycleChanged(@NonNull Car car, boolean ready) {
        // ready 在Service斷開(kāi)連接時(shí)會(huì)變?yōu)閒alse
if (ready) {
            
        } else {
            // CarService 發(fā)生異常或連接被斷開(kāi)了,需要client端處理。
}
    }
});

為什么上面的那種調(diào)用方式會(huì)導(dǎo)致client端的進(jìn)程被殺死呢?這就需要我們繼續(xù)深入的探究一下Car Api是如何實(shí)現(xiàn)的。

Car API 實(shí)現(xiàn)原理

探討Car API的實(shí)現(xiàn)原理我們可以它的入口類(lèi)Car開(kāi)始。源碼位置:/packages/services/Car/car-lib/src/android/car/Car.java

createCar有三個(gè)不同的重載方法,分別如下所示:

public static Car createCar(Context context)
public static Car createCar(Context context, @Nullable Handler handler)

public static Car createCar(@NonNull Context context,
                            @Nullable Handler handler, long waitTimeoutMs,
                            @NonNull CarServiceLifecycleListener statusChangeListener) 

createCar(context)在實(shí)現(xiàn)上是直接調(diào)用了createCar(context, handler),如下所示:

public static Car createCar(Context context) {
    return createCar(context, (Handler) null);
}

createCar(context, handler)createCar(context, handler, waitTimeoutMs, statusChangeListener)之間則沒(méi)有調(diào)用關(guān)系,各自有各自的實(shí)現(xiàn)方式,但是邏輯上大致相同,并且第三種的createCar(context, handler, waitTimeoutMs, statusChangeListener)邏實(shí)現(xiàn)上要更復(fù)雜一些。所以我們直接看createCar(context, handler, waitTimeoutMs, statusChangeListener)是實(shí)現(xiàn)的就可以了。

  • createCar(context, handler, waitTimeoutMs, statusChangeListener)

Handler handler:將所有CarXXXManager事件發(fā)送到此handler。但是statusChangeListener將始終調(diào)度到主線程。傳遞null會(huì)導(dǎo)致將所有CarXXXManager回調(diào)發(fā)送到主線程。

long waitTimeoutMs:將其設(shè)置為CAR_WAIT_TIMEOUT_DO_NOT_WAIT則不等待CarService連接就緒。將此設(shè)置為CAR_WAIT_TIMEOUT_WAIT_FOREVER將阻塞調(diào)用,直到CarService連接成功為止。

設(shè)置的值大于0則為超時(shí)時(shí)間,當(dāng)存在有限的超時(shí)時(shí)間時(shí),返回的Car對(duì)象不能保證是可用的。

CarServiceLifecycleListener statusChangeListener: 監(jiān)聽(tīng)CarService是否連接就緒。


createCar在實(shí)現(xiàn)流程上可以分為三個(gè)部分:

第一步,計(jì)算出綁定CarService的最大重試次數(shù)。這個(gè)次數(shù)決定了后面,多久會(huì)顯示連接異常的日志。

public static Car createCar(@NonNull Context context,
                            @Nullable Handler handler, long waitTimeoutMs,
                            @NonNull CarServiceLifecycleListener statusChangeListener) {
  
    long maxRetryCount = 0;
    if (waitTimeoutMs > 0) {
        maxRetryCount = waitTimeoutMs / CAR_SERVICE_BINDER_POLLING_INTERVAL_MS; // 50 ms
 // 如果是正值,則至少等待一次。
if (maxRetryCount == 0) {
            maxRetryCount = 1;
        }
    }
    
   ...    
}

第二步也是最關(guān)鍵的一步,構(gòu)造出Car對(duì)象并返回給調(diào)用方,同時(shí)將狀態(tài)通過(guò)statusChangeListener回調(diào)給調(diào)用方。正常流程下到createCar()方法執(zhí)行到這里就已經(jīng)結(jié)束了。

public static Car createCar(@NonNull Context context,
                            @Nullable Handler handler, long waitTimeoutMs,
                            @NonNull CarServiceLifecycleListener statusChangeListener) {
    ...
    Car car = null;
    IBinder service = null;
    boolean started = false;
    int retryCount = 0;
    
    ...
    
    boolean isMainThread = Looper.myLooper() == Looper.getMainLooper();
    while (true) {
        // 這個(gè) CAR_SERVICE_BINDER_SERVICE_NAME 是在CarService啟動(dòng)時(shí)添加的。
        service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);
        if (car == null) {
            // service可以為空,構(gòu)造方法對(duì)于空service是安全的。
car = new Car(context, ICar.Stub.asInterface(service), null, statusChangeListener,
                    handler);
        }

        if (service != null) {
            if (!started) {
                car.dispatchCarReadyToMainThread(isMainThread);
                car.startCarService();
                // 正常流程下,while (true)循環(huán)執(zhí)行到這里就結(jié)束了,后面的方法只有CarService啟動(dòng)出現(xiàn)異常時(shí)才會(huì)出現(xiàn)。 
                return car;
            }
            break;
}

        
        if (!started) {
            car.startCarService();
            started = true;
        }

        // 如果連接失敗,每隔50毫秒重試一次,嘗試達(dá)到一定的閾值后,日志上會(huì)顯示異常
retryCount++;
        if (waitTimeoutMs < 0 && retryCount >= CAR_SERVICE_BINDER_POLLING_MAX_RETRY
&& retryCount % CAR_SERVICE_BINDER_POLLING_MAX_RETRY == 0) {
            // 日志警告
Log.w(TAG_CAR, "car_service not ready, waited for car service (ms):"
                            + retryCount * CAR_SERVICE_BINDER_POLLING_INTERVAL_MS,
                    new RuntimeException());
        } else if (waitTimeoutMs >= 0 && retryCount > maxRetryCount) {
            if (waitTimeoutMs > 0) {
                Log.w(TAG_CAR, "car_service not ready, waited for car service (ms):"
                                + waitTimeoutMs,
                        new RuntimeException());
            }
            return car;
        }

        try {
            // 休眠 50 ms
Thread.sleep(CAR_SERVICE_BINDER_POLLING_INTERVAL_MS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            Log.w(TAG_CAR, "interrupted", new RuntimeException());
            return car;
        }
    }

最后一步,主要是應(yīng)對(duì)一些異常情況,正常情況不會(huì)觸發(fā)。

public static Car createCar(@NonNull Context context,
                            @Nullable Handler handler, long waitTimeoutMs,
                            @NonNull CarServiceLifecycleListener statusChangeListener) {
    
    ...
    
    // 加鎖是為了讓 mServiceConnectionListener 能在主線程中正常訪問(wèn) car 實(shí)例
synchronized (car.mLock) {
        Log.w(TAG_CAR,
                "waited for car_service (ms):"
                        + retryCount * CAR_SERVICE_BINDER_POLLING_INTERVAL_MS,
                new RuntimeException());
        // ServiceConnection 已經(jīng)處理了一切,直接返回 car 實(shí)例
if (car.mService != null) {
            return car;
        }
        // mService check in ServiceConnection prevents calling onLifecycleChanged.
 // So onLifecycleChanged should be called explicitly but do it outside lock.
car.mService = ICar.Stub.asInterface(service);
        car.mConnectionState = STATE_CONNECTED;
    }
    car.dispatchCarReadyToMainThread(isMainThread);
    return car;
}

createCar()方法中分發(fā)Car實(shí)例狀態(tài)時(shí),會(huì)調(diào)用startCarService()綁定CarService

private void startCarService() {
    Intent intent = new Intent();
    intent.setPackage(CAR_SERVICE_PACKAGE);
    intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
    boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,
            Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);
    synchronized (mLock) {
        if (!bound) {
            // 綁定失敗時(shí)的重試機(jī)制
            mConnectionRetryCount++;
            if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
                Log.w(TAG_CAR, "cannot bind to car service after max retry");
                mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
            } else {
                mEventHandler.postDelayed(mConnectionRetryRunnable,
                        CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
            }
        } else {
            // 綁定成功時(shí)要取消重試機(jī)制
            mEventHandler.removeCallbacks(mConnectionRetryRunnable);
            mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
            mConnectionRetryCount = 0;
            mServiceBound = true;
        }
    }
}

private final Runnable mConnectionRetryRunnable = new Runnable() {
    @Override
    public void run() {
        startCarService();
    }
};

private final Runnable mConnectionRetryFailedRunnable = new Runnable() {
    @Override
    public void run() {
        mServiceConnectionListener.onServiceDisconnected(new ComponentName(CAR_SERVICE_PACKAGE,
                CAR_SERVICE_CLASS));
    }
};

在綁定CarService時(shí),需要使用ServiceConnection監(jiān)聽(tīng)與CarService的連接狀態(tài),并處理service連接成功與連接斷開(kāi)的情況。

1)連接成功:由于在createCar中已經(jīng)創(chuàng)建好了mService,所以正常流程下,執(zhí)行到return就結(jié)束了,后面流程基本都是出現(xiàn)異常觸發(fā)了重連。

private final ServiceConnection mServiceConnectionListener = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                synchronized (mLock) {
                    ICar newService = ICar.Stub.asInterface(service);
                    if (newService == null) {
                        Log.wtf(TAG_CAR, "null binder service", new RuntimeException());
                        return;  // 這不應(yīng)該發(fā)生
}

                    if (mService != null && mService.asBinder().equals(newService.asBinder())) {
                        // 由于在createCar中已經(jīng)創(chuàng)建好了mService,所以正常流程下,執(zhí)行到這一步就結(jié)束了
return;
                    }

                    mConnectionState = STATE_CONNECTED;
                    mService = newService;
                }

                // 分發(fā)連接狀態(tài)
if (mStatusChangeCallback != null) {
                    mStatusChangeCallback.onLifecycleChanged(Car.this, true);
                } else if (mServiceConnectionListenerClient != null) {
                    mServiceConnectionListenerClient.onServiceConnected(name, service);
                }
            }
    ...
        };

2)連接斷開(kāi):分發(fā)Car對(duì)象的狀態(tài),此時(shí)Client不應(yīng)該再使用Car的實(shí)例。所以如果Client端調(diào)用createCar()時(shí)沒(méi)有監(jiān)聽(tīng)連接狀態(tài),Car Api會(huì)觸發(fā)finishClient(),直接殺死client端。

private final ServiceConnection mServiceConnectionListener = new ServiceConnection() {
    ...
            @Override
            public void onServiceDisconnected(ComponentName name) {
                // 重新啟動(dòng)后,CarService可以接收功能更改。
mFeatures.resetCache();
                synchronized (mLock) {
                    if (mConnectionState  == STATE_DISCONNECTED) {
                        // 當(dāng)客戶(hù)端調(diào)用在 onServiceDisconnected 調(diào)用之前斷開(kāi)連接時(shí),可能會(huì)發(fā)生這種情況。
return;
                    }
                    handleCarDisconnectLocked();
                }
                if (mStatusChangeCallback != null) {
                    mStatusChangeCallback.onLifecycleChanged(Car.this, false);
                } else if (mServiceConnectionListenerClient != null) {
                    mServiceConnectionListenerClient.onServiceDisconnected(name);
                } else {
                    // client端沒(méi)有正確處理CarService會(huì)重新啟動(dòng)的情況,因此直接殺死client端
finishClient();
                }
            }
        };

finishClient()中會(huì)根據(jù)傳入client傳入的context類(lèi)型,執(zhí)行不同的操作。

情景一:context = null,在Client端拋出異常。

情景二:context 是 Activity,結(jié)束該Activity,不會(huì)終止Client端的進(jìn)程。

情景三:context 是 Service,終止Client端的進(jìn)程。

情景四:context 不是以上的情況,終止Client端的進(jìn)程。

private void finishClient() {
    if (mContext == null) {
        throw new IllegalStateException("Car service has crashed, null Context");
    }
    if (mContext instanceof Activity) {
        Activity activity = (Activity) mContext;
        if (!activity.isFinishing()) {
            Log.w ( TAG_CAR,
                    "Car service crashed, client not handling it, finish Activity, created "
                            + "from " + mConstructionStack);
            activity.finish();
        }
        return;
    } else if (mContext instanceof Service) {
        Service service = (Service) mContext;
        killClient(service.getPackageName() + "," + service.getClass().getSimpleName());
    } else {
        killClient(/ * clientInfo= */  null);
    }
}

private void killClient(@Nullable String clientInfo) {
    Log.w ( TAG_CAR, "**Car service has crashed. Client(" + clientInfo + ") is not handling it."
                    + " Client should use Car.createCar(..., CarServiceLifecycleListener, .."
                    + ".) to handle it properly. Check pritned callstack to check where other "
                    + "version of Car.createCar() was called. Killing the client process**",
            mConstructionStack);
    Process.killProcess( Process.myPid( ));
}

正是由于finishClient()這種機(jī)制的存在,所以調(diào)用方應(yīng)該要監(jiān)聽(tīng)CarService的連接狀態(tài)。

最后我們?cè)倏匆幌?code>getCarManager()這個(gè)方法是如何實(shí)現(xiàn)的。

getCarManager()實(shí)現(xiàn)機(jī)制上利用了BinderPool的思路,使用ICar.aidlgetService()來(lái)獲取Server端的Binder對(duì)象,然后將Binder對(duì)象封裝在Manager里面,同時(shí)將Manager對(duì)象緩存在一個(gè)Map集合中,后續(xù)就可以從Map取出需要的Manager,減少I(mǎi)PC通信開(kāi)銷(xiāo)。如下所示:

@Nullable
public Object getCarManager(String serviceName) {
    CarManagerBase manager;
    synchronized (mLock) {
        if (mService == null) {
            Log.w(TAG_CAR, "getCarManager not working while car service not ready");
            return null;
        }
        manager = mServiceMap.get(serviceName);
        if (manager == null) {
            try {
                IBinder binder = mService.getCarService(serviceName);
                if (binder == null) {
                    Log.w(TAG_CAR, "getCarManager could not get binder for service:"
                            + serviceName);
                    return null;
                }
                manager = createCarManagerLocked(serviceName, binder);
                if (manager == null) {
                    Log.w(TAG_CAR, "getCarManager could not create manager for service:"
                            + serviceName);
                    return null;
                }
                mServiceMap.put(serviceName, manager);
            } catch (RemoteException e) {
                handleRemoteExceptionFromCarService(e);
            }
        }
    }
    return manager;
}

@Nullable
private CarManagerBase createCarManagerLocked(String serviceName, IBinder binder) {
    CarManagerBase manager = null;
    switch (serviceName) {
        case AUDIO_SERVICE:
            manager = new CarAudioManager(this, binder);
            break;
        case SENSOR_SERVICE:
            manager = new CarSensorManager(this, binder);
            break;
        case INFO_SERVICE:
            manager = new CarInfoManager(this, binder);
            break;
            ...
        default:
            // Experimental or non-existing
String className = null;
            try {
                className = mService.getCarManagerClassForFeature(serviceName);
            } catch (RemoteException e) {
                handleRemoteExceptionFromCarService(e);
                return null;
            }
            if (className == null) {
                Log.e(TAG_CAR, "Cannot construct CarManager for service:" + serviceName
                        + " : no class defined");
                return null;
            }
            manager = constructCarManager(className, binder);
            break;
    }
    return manager;
}

以上就Car API的實(shí)現(xiàn)過(guò)和原理了。注意在createCar()中有這樣一段代碼。

IBinder service = null;
service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);

在Client端與CarService建立連接之前,通過(guò)ServiceManager.getService()就可以直接取出IBinder對(duì)象,而不用等到與service建立連接后再?gòu)?code>onServiceConnected(ComponentName name, IBinder service)中取。

但是這樣操作的前提是使用ServiceManager.addService()添加了這個(gè)IBinder,那么是哪里添加的呢,IBinder是Server端實(shí)現(xiàn)的,那么答案就需要去CarService中尋找了。

CarService 的實(shí)現(xiàn)原理

想要說(shuō)清楚CarService實(shí)現(xiàn)方式,我們需要搞明白CarService是怎么啟動(dòng)的。

CarService 啟動(dòng)流程

CarService作為Android Automotive的核心服務(wù),它是在SystemServer中啟動(dòng)的,SystemServer會(huì)在startOtherServices()方法中讓SystemServiceManager先通過(guò)反射的形式創(chuàng)建出StartCarServiceHelperService這個(gè)對(duì)象。

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    ...
    // 僅在automotive中啟動(dòng)
    if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
        t.traceBegin("StartCarServiceHelperService");
        mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
        t.traceEnd();
    }
    ...
}

然后在SystemServiceManager中調(diào)用StartCarServiceHelperServiceonStart()方法。

CarServiceHelperServiceCarService的SystemService端的配套服務(wù)。

public SystemService startService(String className) {
    final Class<SystemService> serviceClass = loadClassFromLoader(className,
            this.getClass().getClassLoader());
    return startService(serviceClass);
}

public void startService(@NonNull final SystemService service) {
    // Register it.
mServices.add(service);
    long time = SystemClock.elapsedRealtime();
    try {
        service.onStart();
    } catch (RuntimeException ex) {
        throw new RuntimeException("Failed to start service " + service.getClass().getName()
                + ": onStart threw an exception", ex);
    }
    warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
}

最終在onStart()方法中啟動(dòng)CarService,并加載jni庫(kù)為CarService提供必要的API。

CarServiceHelperService 源碼位置:/frameworks/opt/car/services/src/com/android/internal/car/CarServiceHelperService.java

注意:CarServiceHelperService并不是android.app.Service

@Override
public void onStart() {
    ...
    Intent intent = new Intent();
    intent.setPackage("com.android.car");
    intent.setAction(ICarConstants.CAR_SERVICE_INTERFACE);
    if (!mContext.bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE,
            UserHandle.SYSTEM)) {
        Slog.wtf(TAG, "cannot start car service");
    }
    loadNativeLibrary();
}

void loadNativeLibrary() {
    System.loadLibrary("car-framework-service-jni");
}

通過(guò)以上的步驟CarService就完成了啟動(dòng),CarService的啟動(dòng)時(shí)序如下所示:

CarService 初始化

CarService進(jìn)入啟動(dòng)時(shí)序后,會(huì)onCreate()方法中進(jìn)行一系列的自身的初始化操作,步驟如下:

1)通過(guò)HIDL接口獲取到HAL層的IHwBinder對(duì)象-IVehicle,與AIDL的用法類(lèi)似,必須持有IHwBinder對(duì)象我們才可以與Vehicle HAL層進(jìn)行通信。有關(guān)HIDL、VechicleHAL以后都會(huì)單獨(dú)介紹。

2)創(chuàng)建ICarImpl對(duì)象,并調(diào)用init方法,它就是ICar.aidl接口的實(shí)現(xiàn)類(lèi),我們需要通過(guò)它才能拿到其他的Service的IBinder對(duì)象。

3)將ICar.aidl的實(shí)現(xiàn)類(lèi)添加到ServiceManager中。這就解答了我們?cè)贑ar API中疑問(wèn)。

4)設(shè)定SystemProperty,將CarService設(shè)定為創(chuàng)建完成狀態(tài),只有包含CarService在內(nèi)的所有的核心Service都完成初始化,才能結(jié)束開(kāi)機(jī)動(dòng)畫(huà)并發(fā)送開(kāi)機(jī)廣播。

@Override
public void onCreate() {
    Log.i(CarLog.TAG_SERVICE, "Service onCreate");
    mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */ );
    mVehicle = getVehicle();
    EventLog.writeEvent(EventLogTags.CAR_SERVICE_CREATE, mVehicle == null ? 0 : 1);

    if (mVehicle == null) {
        throw new IllegalStateException("Vehicle HAL service is not available.");
    }
    try {
        mVehicleInterfaceName = mVehicle.interfaceDescriptor();
    } catch (RemoteException e) {
        throw new IllegalStateException("Unable to get Vehicle HAL interface descriptor", e);
    }

    Log.i(CarLog.TAG_SERVICE, "Connected to " + mVehicleInterfaceName);
    EventLog.writeEvent(EventLogTags.CAR_SERVICE_CONNECTED, mVehicleInterfaceName);

    mICarImpl = new ICarImpl(this,
            mVehicle,
            SystemInterface.Builder.defaultSystemInterface(this).build(),
            mCanBusErrorNotifier,
            mVehicleInterfaceName);
    mICarImpl.init();
    // 處理 HIDL 連接
linkToDeath(mVehicle, mVehicleDeathRecipient);

    ServiceManager.addService("car_service", mICarImpl);
    SystemProperties.set("boot.car_service_created", "1");
    super.onCreate();
}

@Nullable
private static IVehicle getVehicle() {
    final String instanceName = SystemProperties.get("ro.vehicle.hal", "default");

    try {
        return android.hardware.automotive.vehicle.V2_0.IVehicle.getService(instanceName);
    } catch (RemoteException e) {
        Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle/" + instanceName + " service", e);
    } catch (NoSuchElementException e) {
        Log.e(CarLog.TAG_SERVICE, "IVehicle/" + instanceName + " service not registered yet");
    }
    return null;
}

接著我們?cè)賮?lái)看ICarImpl的實(shí)現(xiàn),如下所示:

1)創(chuàng)建各個(gè)核心服務(wù)對(duì)象。

2)把服務(wù)對(duì)象緩存到CarLocalServices中,這里主要是為了方便Service之間的相互訪問(wèn)。

ICarImpl的源碼位置:/packages/services/Car/service/src/com/android/car/ICar

ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,
         CanBusErrorNotifier errorNotifier, String vehicleInterfaceName,
         @Nullable CarUserService carUserService,
         @Nullable CarWatchdogService carWatchdogService) {
    ...
    // 創(chuàng)建 核心服務(wù)對(duì)象
mCarPowerManagementService = new CarPowerManagementService(mContext, mHal.getPowerHal(),
            systemInterface, mCarUserService);
    ...

    // 將重要的服務(wù)緩存到 CarLocalServices
CarLocalServices.addService(CarPowerManagementService.class, mCarPowerManagementService);
    CarLocalServices.addService(CarPropertyService.class, mCarPropertyService);
    CarLocalServices.addService(CarUserService.class, mCarUserService);
    CarLocalServices.addService(CarTrustedDeviceService.class, mCarTrustedDeviceService);
    CarLocalServices.addService(CarUserNoticeService.class, mCarUserNoticeService);
    CarLocalServices.addService(SystemInterface.class, mSystemInterface);
    CarLocalServices.addService(CarDrivingStateService.class, mCarDrivingStateService);
    CarLocalServices.addService(PerUserCarServiceHelper.class, mPerUserCarServiceHelper);
    CarLocalServices.addService(FixedActivityService.class, mFixedActivityService);
    CarLocalServices.addService(VmsBrokerService.class, mVmsBrokerService);
    CarLocalServices.addService(CarOccupantZoneService.class, mCarOccupantZoneService);
    CarLocalServices.addService(AppFocusService.class, mAppFocusService);

    // 將創(chuàng)建的服務(wù)對(duì)象依次添加到一個(gè)list中保存起來(lái)
List<CarServiceBase> allServices = new ArrayList<>();
    allServices.add(mFeatureController);
    allServices.add(mCarUserService);
    ...
    allServices.add(mCarWatchdogService);
    // Always put mCarExperimentalFeatureServiceController in last.
addServiceIfNonNull(allServices, mCarExperimentalFeatureServiceController);
    mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);

}

3)將服務(wù)對(duì)象放置一個(gè)list中。這樣init方法中就可以以循環(huán)的形式直接調(diào)用服務(wù)對(duì)象的init,而不需要一個(gè)個(gè)調(diào)用。VechicleHAL的程序也會(huì)在這里完成初始化。

@MainThread
void init() {
    mBootTiming = new TimingsTraceLog(VHAL_TIMING_TAG, Trace.TRACE_TAG_HAL);
    traceBegin("VehicleHal.init");
    // 初始化 Vechicle HAL
    mHal.init();
    
    traceEnd();
    traceBegin("CarService.initAllServices");
    // 初始化所有服務(wù)
    for (CarServiceBase service : mAllServices) {
        service.init();
    }
    traceEnd();
}

4)最后實(shí)現(xiàn)ICar.aidl中定義的各個(gè)接口就可以了,如下所示:

@Override
public IBinder getCarService(String serviceName) {
    if (!mFeatureController.isFeatureEnabled(serviceName)) {
        Log.w(CarLog.TAG_SERVICE, "getCarService for disabled service:" + serviceName);
        return null;
    }
    switch (serviceName) {
        case Car.AUDIO_SERVICE:
            return mCarAudioService;
        case Car.APP_FOCUS_SERVICE:
            return mAppFocusService;
        case Car.PACKAGE_SERVICE:
            return mCarPackageManagerService;
       ...
        default:
            IBinder service = null;
            if (mCarExperimentalFeatureServiceController != null) {
                service = mCarExperimentalFeatureServiceController.getCarService(serviceName);
            }
            if (service == null) {
                Log.w(CarLog.TAG_SERVICE, "getCarService for unknown service:"
                        + serviceName);
            }
            return service;
    }
}

總結(jié)一下CarService的啟動(dòng)時(shí)序如下所示:

總結(jié)

本篇講解了CarService的總體結(jié)構(gòu),以及Car API 的實(shí)現(xiàn)原理,CarService中實(shí)現(xiàn)的功能非常龐大,就像文章中反復(fù)在強(qiáng)調(diào)的那樣,在CarService實(shí)現(xiàn)的功能幾乎就是覆蓋整個(gè)車(chē)載Framework的核心。

然而現(xiàn)實(shí)中為了保證各個(gè)核心服務(wù)的穩(wěn)定性,同時(shí)降低CarService協(xié)同開(kāi)發(fā)的難度,一般會(huì)選擇將一些重要的服務(wù)拆分單獨(dú)作為一個(gè)獨(dú)立的Service運(yùn)行在獨(dú)立的進(jìn)程中,導(dǎo)致有的車(chē)機(jī)系統(tǒng)中CarService只實(shí)現(xiàn)了CarPropertyService的功能。

文中提到的其他核心Service,會(huì)在以后的時(shí)間里逐個(gè)介紹,感謝你的閱讀,希望對(duì)你有所幫助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,224評(píng)論 6 529
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 97,916評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,014評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,466評(píng)論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,245評(píng)論 6 405
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,795評(píng)論 1 320
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,869評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,010評(píng)論 0 285
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,524評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,487評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,634評(píng)論 1 366
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,173評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,884評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,282評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,541評(píng)論 1 281
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,236評(píng)論 3 388
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,623評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容