<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ### **概述** Android專用驅動構成了Android運行時的基石。從技術上來講,Android專用驅動也是整個Android系統的亮點,特別是Binder驅動。Binder是一種進程間通信機制(IPC),它與傳統的IPC機制對比,最大的特點是高效,因為通信數據在兩個進程之間只需要執行一次拷貝即可。Binder在Android系統里面使用得非常廣泛以及頻繁。在涉及到比較大的通信數據時,Binder通常還結合另外一個驅動Ashmem來使用。Ashmem是一個共享內存驅動,它與傳統的共享內存相比,最大的特點是它是通過文件描述符來描述的,并且可以動態地進行分塊管理。動態分塊管理的目的是可以將部分不再使用了的內存交回給系統,非常適合內存較小的移動設備使用。另外一個專用驅動Logger是一個日志驅動,它與傳統的日志系統對比,特點是日志是記錄在內核空間而非文件中,這樣就可以提高日志的讀寫速度。 主要講Logger、Binder和Ashmem三個Android專用驅動的實現原理。由于這三個驅動在Android源代碼里面用得非常廣泛和頻繁,因此理解它們的實現原理,就可以掌握Android的精華。這對以后閱讀Android系統的其它代碼,也是非常有幫助的。 * Android專用驅動概述 * Android Logger驅動系統 * Android Binder驅動系統 * Android Ashmem驅動系統 #### **Android專用驅動概述** **以Linux驅動形式實現在內核空間** * 不是為了驅動硬件設備工作 * 而是為了獲得特權管理系統 **為Android Runtime Framework服務** * 高效的日志服務 * 高效的進程間通信機制 * 以組件為目標的進程管理機制 * …… **在整個系統中廣泛和頻繁地使用** #### **Android Logger驅動系統** **日志系統的作用和特點** * 開發期間調試程序功能 * 發布期間記錄程序運行 * 頻繁和廣泛地使用 **傳統日志系統以文件為輸出** * 從用戶空間切換至內核空間 * 在內核空間執行磁盤IO操作 **Android日志系統以內核緩沖區為輸出** * 從用戶空間切換至內核空間 * 直接內存操作 **Android日志系統更高效** **以緩沖區為輸出的日志系統要解決的問題** * 不能占用過多的內存 * 次要日志不能覆蓋重要日志 * 頻繁寫入的日志不能覆蓋不頻繁寫入的日志 **以上三個問題的解決方案** * 環形緩沖區,新日志覆蓋舊日志 * 日志分類,不同類的日志寫在不同的緩沖區 **整體架構圖** ![](https://box.kancloud.cn/e60e2154623e98e9a761142571a7efd3_573x506.jpg) **日志分類** * Main,記錄應用程序類型日志,次要,文本格式 /dev/log/main * System,記錄系統類型日志,重要,文本格式 /dev/log/system * Radio,記錄無線相關日志,頻繁 ,文本格式 /dev/log/radio * Event,記錄系統事件日志,特殊用途,二進制格式 /dev/log/events **每一類日志對應一個設備文件** * Main:/dev/log/main * System: /dev/log/system * Radio:/dev/log/radio * Event:/dev/log/events **每一類日志對應一個環形緩沖區,大小256K** **備注** 在Linux Kernel 2.6.29的實現中: 1. 不區分Main和System類型的日志,它們對應的都是同一個環形緩沖區 2. Main、System和Event類型的日志對應的環形緩沖區大小為64K,Radio類型的日志對應的環形緩沖區大小256K 在Linux Kernel 3.4的實現中: 1. Main和System類型已經作區分,它們對應的是不同環形緩沖區 2. 所有環形緩沖區的大小均為256K **文本類型日志格式** ![](https://box.kancloud.cn/b28b44357f90e8cd4c03da164f3d8665_767x37.jpg) **Priority,優先級,整數** VERBOSE、DEBUG、INFO、WARN、ERROR、FATAL **Tag,標簽,字符串** 自定義 **Msg,日志內容,字符串** 自定義 **二進制類型日志格式** ![](https://box.kancloud.cn/ae10abd85641c4e4852a516c998a1a27_781x39.jpg) **Tag,標簽,整數** 自定義 **Msg,日志內容,二進制數據塊** ![](https://box.kancloud.cn/ccdbf3e81d8a2fe87e68e65cd3e31047_782x43.jpg) **Type:整數(1),長整數(2),字符串(3),列表(4)** **Value:自定義** **二進制日志格式由/system/etc/event-log-tags文件描述** ![](https://box.kancloud.cn/0e29ce550f4976bd94409fcc787bd8f8_820x39.jpg) * 日志標簽tag number對應的文本描述為tag name * 日志內容格式 ![](https://box.kancloud.cn/b70e3f053d0b4c18f24ee3527870bd68_810x35.jpg) * name:名稱 * data type:數據類型 * data unit:數據單位 **二進制日志格式描述示例** ~~~ 2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1) ~~~ * 日志2722表示日志標簽值,battery_level是其對應的文件描述 * 標簽值等于2722的日志內容由三個值組成,它們分別是level、voltage和temperature * level 、voltage和temperature的數據類型均是整數(1) * level的單位是百分比(6) * voltage和temperature的單位均為對象數量(1) **用戶空間日志庫** ![](https://box.kancloud.cn/953886ca2a98960cf38f06c6ed64dbef_679x346.jpg) * __android_log_assert、__android_log_vprint、__android_log_print 寫入類型為main的日志記錄 * __android_log_btwrite、__android_log_bwrite 寫入類型為event的日志記錄 * __android_log_buf_print 寫入任意類型的日志記錄 * __android_log_write、__android_log_buf_write 日志標簽以“RIL”開頭或者于“HTC_RIL”、“AT”、“GSM”、“STK”、“CDMA”、“PHONE”或“SMS”,那么會被認為是radio類型的日志記錄 * write_to_log * 函數指針,負責最終的日志寫入 * 開始時指向__write_to_log_init * 初始化成功后指向__write_to_log_kernel * 初始化失敗后指向__write_to_log_null **C/C++日志寫入接口** * LOGV、LOGD、LOGI、LOGW、LOGE * SLOGV、SLOGD、SLOGI、SLOGW、SLOGE * LOG_EVENT_INT、LOG_EVENT_LONG、LOG_EVENT_STRING **Java日志寫入接口** * android.util.Log * android.util.Slog * android.util.EventLog **日志讀取工具-- logcat** ~~~ adb logcat --help ~~~ ![](https://box.kancloud.cn/a7b61897f753f913181d8d8e818d6542_610x362.jpg) #### **Android Binder驅動系統** **傳統的IPC ,例如Pipe和Socket,執行一次通信需要兩次數據拷貝** ![](https://box.kancloud.cn/8b4d6eb58b49430f33dbf3890bac6f1b_474x198.jpg) **內存共享機制雖然只需要執行一次數據拷貝,但是它需要結合其它IPC來做進程同步,效率同樣不理想** **Binder是一種高效且易用的IPC機制** * 一次數據拷貝 * Client/Server通信模型 * 既可用作進程間通信,也可用作進程內通信 ![](https://box.kancloud.cn/8fdad34947e3b3b60716ea034beef2e9_784x465.jpg) **Binder驅動為每一個進程分配4M的內核緩沖區,用作數據傳輸** * 4M內核緩沖區所對應的物理頁面是按需要分配的,一開始只有一個物理頁被被映射 * 4M內核緩沖區所對應的物理頁面除了映射在內核空間之外,還會被映射在進程的用戶空間 ![](https://box.kancloud.cn/5310f756866da0d25e1b1e09dfcf6f2e_890x280.jpg) **進程間的一次數據拷貝** ![](https://box.kancloud.cn/0c8a7104ffa359c822daf24324c12584_441x229.jpg) **Client/Server通信模型** * Server進程啟動時,將在本進程內運行的Service注冊到Service Manager中,并且啟動一個Binder線程池,用來接收Client進程請求 * Client進程向Service Manager查詢所需要的Service,并且獲得一個Binder代理對象,通過該代理對象即可向Service發送請求 **Service注冊** * BBinder:Service在用戶空間的描述 * binder_node:Service在內核空間的描述 * binder_ref:Service代理在內核空間的描述(一個整數句柄值) ![](https://box.kancloud.cn/36fc4f70aba9b5ba47c59c50cd86b285_527x360.jpg) **Service代理獲取** * BpBinder:Service代理在用戶空間的描述(一個整數句柄值) ![](https://box.kancloud.cn/67bd9312adeea9e56cb55ce145937ca8_764x360.jpg) **Service Manager注冊及其代理獲得** 一個特殊Service,它的代理句柄值永遠等于0 ![](https://box.kancloud.cn/1bad4cadb748d7bf946baad884de1275_680x355.jpg) **Client和Server的通信過程** ![](https://box.kancloud.cn/a132f9101f37fcaea431846694d94638_589x385.jpg) **binder_transaction:通信數據描述** ![](https://box.kancloud.cn/667407d4a1c6bc1aa631496fe17958fb_517x329.png) ![](https://box.kancloud.cn/e1d030d0cb30ca22701961b370e5e077_610x285.png) **Binder對象(flat_binder_object)的類型** * BINDER_TYPE_BINDER * BINDER_TYPE_WEAK_BINDER * BINDER_TYPE_HANDLE * BINDER_TYPE_WEAK_HANDLE * BINDER_TYPE_FD ![](https://box.kancloud.cn/f1fdb489e766c3868119587799954c15_866x245.jpg) **BINDER_TYPE_BINDER和BINDER_TYPE_WEAK_BINDER類型的flat_binder_object傳輸發生在**: * Server進程主動向Client進程發送Service (匿名Service ) * Server進程向Service Manager進程注冊Service **BINDER_TYPE_HANDLE和BINDER_TYPE_WEAK_HANDLE類型的flat_binder_object傳輸發生在**: 一個Client向另外一個進程發送Service代理 **BINDER_TYPE_FD類型的flat_binder_object傳輸發生在**: 一個進程向另外一個進程發送文件描述符 **Binder驅動對類型BINDER_TYPE_BINDER 的Binder對象(flat_binder_object)的處理**: * 在源進程中找到對應的binder_node。如果不存在,則創建。 * 根據上述binder_node在目標進程中找到對應的binder_ref。如果不存在,則創建。 * 增加上述binder_ref的強引用計數和弱引用計數 * 構造一個類型為BINDER_TYPE_HANDLE的flat_binder_object對象。 * 將上述flat_binder_object對象發送給目標進程。 **Binder驅動對類型BINDER_TYPE_WEAK_BINDER 的Binder對象(flat_binder_object)的處理**: * 在源進程中找到對應的binder_node。如果不存在,則創建。 * 根據上述binder_node在目標進程中找到對應的binder_ref。如果不存在,則創建。 * 增加上述binder_ref的弱引用計數。 * 構造一個類型為BINDER_TYPE_WEAK_HANDLE的flat_binder_object對象。 * 將上述flat_binder_object對象發送給目標進程。 **Binder驅動對類型BINDER_TYPE_WEAK_BINDER 的Binder對象(flat_binder_object)的處理**: * 在源進程中找到對應的binder_node。如果不存在,則創建。 * 根據上述binder_node在目標進程中找到對應的binder_ref。如果不存在,則創建。 * 增加上述binder_ref的弱引用計數。 * 構造一個類型為BINDER_TYPE_WEAK_HANDLE的flat_binder_object對象。 * 將上述flat_binder_object對象發送給目標進程。 **Binder驅動對類型BINDER_TYPE_HANDLE 的Binder對象(flat_binder_object)的處理**: * 在源進程中找到對應的binder_ref。 * 如果上述binder_ref所引用的binder_node所在進程就是目標進程: * 增加上述binder_node的強引用計數和弱引用計數 * 構造一個類型為BINDER_TYPE_BINDER的flat_binder_object * 將上述flat_binder_object發送給目標進程 * 如果上述binder_ref所引用的binder_node所在進程不是目標進程: * 為目標進程創建一個binder_ref,該binder_ref與上述binder_ref引用的是同一個binder_node * 增加上述新創建的binder_ref的強引用計數和弱引用計數 * 構造一個類型為BINDER_TYPE_HANDLE的flat_binder_object * 將上述flat_binder_object發送給目標進程 **Binder驅動對類型BINDER_TYPE_WEAK_HANDLE 的Binder對象(flat_binder_object)的處理**: * 在源進程中找到對應的binder_ref。 * 如果上述binder_ref所引用的binder_node所在進程就是目標進程: * 增加上述binder_node的弱引用計數 * 構造一個類型為BINDER_TYPE_WEAK_BINDER的flat_binder_object * 將上述flat_binder_object發送給目標進程 * 如果上述binder_ref所引用的binder_node所在進程不是目標進程: * 為目標進程創建一個binder_ref,該binder_ref與上述binder_ref引用的是同一個binder_node * 增加上述新創建的binder_ref的弱引用計數 * 構造一個類型為BINDER_TYPE_WEAK_HANDLE的flat_binder_object * 將上述flat_binder_object發送給目標進程 **Binder驅動對類型BINDER_TYPE_FD 的Binder對象(flat_binder_object)的處理**: * 在源進程中找到對應的struct file結構體 * 將上述struct file結構體 保存在目標進程的打開文件列表中 * 構造一個類型為BINDER_TYPE_FD的flat_binder_object * 將上述flat_binder_object發送給目標進程 **Binder對象的引用計數** * BBinder:位于用戶空間,通過智能指針管理,有mStrong和mWeak兩個引用計數 * BpBinder:位于用戶空間,通過智能指針管理,有mStrong和mWeak兩個引用計數 * binder_node:位于內核空間,有internal_strong_refs、local_weak_refs和local_strong_refs三個引用計數,以及一個binder_ref引用列表refs * binder_ref:位于內核空間,有strong和weak兩個引用計數 **備注**: local_strong_refs和local_weak_refs分別表示該Binder實體對象的內部強引用計數和弱引用計數;而internal_strong_refs是一個外部強引用計數,用來描述有多少個Binder引用對象是通過強引用計數來引用該Binder實體對象的。內部引用計數是相對于該Binder實體對象所在的Server進程而言的,而外部引用計數是相對于引用了該Binder實體對象的Client進程而言的。當Binder驅動程序請求Server進程中的某一個Binder本地對象來執行一個操作時,它就會增加與該Binder本地對象對應的Binder實體對象的內部引用計數,避免該Binder實體對象被過早地銷毀;而當一個Client進程通過一個Binder引用對象來引用一個Binder實體對象時,Binder驅動程序就會增加它的外部引用計數,也是避免該Binder實體對象被過早地銷毀。讀者可能會覺得奇怪,為什么一個Binder實體對象的外部引用計數只有強引用計數internal_strong_refs,而沒有一個對應的弱引用計數internal_weak_refs呢?原來,Binder實體對象的Binder引用對象列表refs的大小就已經隱含了它的外部弱引用計數,因此,就不需要使用一個額外的弱引用計數internal_weak_refs來描述它的外部弱引用計數了。 **Binder對象之間的引用關系** ![](https://box.kancloud.cn/433a78d86017bc52ecc26907b2cde9dd_587x96.png)![](https://box.kancloud.cn/cbd6d402789894cf923d97d684af0420_470x546.jpg) **Binder對象引用關系小結** * BBinder被binder_node引用 * binder_node被binder_ref引用 * binder_ref被BpBinder引用 * BBinder和BpBinder運行在用戶空間 * binder_node和binder_ref運行在內核空間 * 內核空間足夠健壯,保證binder_node和binder_ref不會異常銷毀 * 用戶空間不夠健壯,BBinder和BpBinder可能會異常銷毀 * BpBinder異常銷毀不會引發致命問題,但是BBinder異常銷毀會引發致命問題 * 需要有一種方式來監控BBinder和BpBinder的異常銷毀 **Binder對象異常銷毀監控** * 所有執行Binder IPC的進程都需要打開/dev/binder文件 * 進程異常退出的時候,內核保證會釋放未正常關閉的它打開的/dev/binder文件,即調用與/dev/binder文件所關聯的release回調函數 * Binder驅動通過實現/dev/binder文件的release回調函數即可監控Binder對象的異常銷毀,進而執行清理工作 * BBinder異常銷毀的時候,不單止Binder驅動需要執行清理工作,引用了它的BpBinder所在的Client進程也需要執行清理工作 * 需要有一種BBinder死亡通知機制 * Client進程從Binder驅動獲得一個BpBinder * Client進程向Binder驅動注冊一個死亡通知,該死亡通知與上述BpBinder所引用的BBinder相關聯 * Binder驅動監控到BBinder所在進程異常退出的時候,檢查該BBinder是否注冊有死亡通知 * Binder驅動向注冊的死亡通知所關聯的BpBinder所運行在的Client進程發送通知 * Client進程執行相應的清理工作 **Binder線程池** * 在Server進程中,Client進程發送過來的Binder請求由Binder線程進行處理 * 每一個Server進程在啟動的時候都會創建一個Binder線程池,并且向里面注冊一個Binder線程 * 之后Server進程可以無限地向Binder線程池注冊新的Binder線程 * Binder驅動發現Server進程沒有空間的Binder線程時,會主動向Server進程請求注冊新的Binder線程 * Binder驅動主動請求Server進程注冊新的Binder線程的數量可以由Server進程設置,默認是16 **Binder線程調度機制** * 在Binder驅動中,每一個Server進程都有一個todo list,用來保存Client進程發送過來的請求,這些請求可以由其Binder線程池中的任意一個空閑線程處理 * 在Binder驅動中,每一個Binder線程也有一個todo list,用來保存Client進程發送過來的請求,這些請求只可以由該Binder線程處理 * Binder線程沒事做的時候,就睡眠在Binder驅動中,直至它所屬的Server進程的todo list或者它自己的todo list有新的請求為止 * 每當Binder驅動將Client進程發送過來的請求保存在Server進程的todo list時,都會喚醒其Binder線程池的空閑Binder線程池,并且讓其中一個來處理 * 每當Binder驅動將Client進程發送過來的請求保存在Binder線程的todo list時,都會將其喚醒來處理 **默認情況下, Client進程發送過來的請求都是保存在Server 進程的todo list中,然而有一種特殊情況:** * 源進程P1的線程A向目標進程發起了一個請求T1,該請求被分發給目標進程P2的線程B處理 * 源進程P1的線程A等待目標進程P2的線程B處理處理完成請求T1 * 目標進程P2的線程B在處理請求T1的過程中,需要向源進程P1發起另一個請求T2 * 源進程P1除了線程A是處于空閑等待狀態之外,還有另外一個線程C處于空閑等待狀態 **這時候請求T2應該分發給線程A處理,還是線程C處理?** * 如果分發給線程C處理,則線程A仍然是處于空閑等待狀態 * 如果分發給線程A處理,則線程 C可以處理其它新的請求 **Binder線程的事務堆棧** ![](https://box.kancloud.cn/9c54725660f70372b4c2d3f953d815b2_612x299.png) ![](https://box.kancloud.cn/02f5f1f76adca2c74fd579fb5f61efbd_516x316.png) **T1:From P1 to P2** * BC_TRANSACTION: * T1->from_parent = NULL * T1->from = Thread(A) * Thread(A)->transaction_stack = T1 * Thread(A)->proc = P1 * BR_TRANSACTION: * T1->to_parent = NULL * T1->to_thread = Thread(B) * Thread(B)-> transaction_stack = T1 **T2:From P2 to P1** * BC_TRANSACTION: * T2->form_parent = T1 * T2->from = Thread(B) * Thread(B)->transaction_stack = T2 * Thread(B)->proc = P2 * BR_TRANSACTION * T2->to_parent = T1 * T2->to_thread = Thread(A) * Thread(A)->transaction_stack = T2 **同步請求優先于異步請求** * 同一時刻,一個BBinder只能處理一個異步請求 * 第一個異步請求將被保存在目標進程的todo list中 * 第一個異步請求未被處理前,其它的異步請求將被保存在對應的 binder_node的async todo list中 * 第一個異步請求被處理之后,第二個異步請求將從binder_node的async todo list轉移至目標進程的todo list等待處理 * 依次類推…… * 此外,所有同時進行的異步請求所占用的內核緩沖區大小不超過目標進程的總內核緩沖區大小的一半 **與請求相關的三個線程優先級** * 源線程的優先級 * 目標線程的優先級 * 目標binder_node的最小優先級(注冊Service時設置) **Binder線程處理同步請求時的優先級** * 取{源線程,目標binder_node,目標線程}的最高優先級 * 保證目標線程以不低于源線程優先級或者binder_node最小優先級的優先級運行 **Binder線程處理異步請求時的優先級** * 取{目標binder_node,目標線程}的最高優先級 * 保證目標線程以不低于binder_node最小優先級的優先級運行 **Binder進程間通信機制的Java接口** * 每一個Java層的Binder本地對象(Binder)在C++層都對應有一個JavaBBinder對象,后者是從C++層的BBinder繼承下來的 * 每一個Java層的Binder代理對象(BinderProxy)在C++層都對應有一個BpBinder對象 * 于是Java層的Binder進程間通信實際上就是通過C++層的BpBinder和BBinder來進行的,與C++層的Binder進程間通信 一致 #### **Android Ashmem驅動系統** **傳統的Linux共享內存機制** * System V:shmget、shmctl、shmat、shmdt * 用一個整數ID來標志一塊共享內存 * 不能動態釋放部分共享內存 * Posix:shm_open、ftruncate、mmap、munmap * 用一個文件描述符來標一塊共享內存 * 不能動態釋放部分共享內存 **Android匿名共享內存** * open、ioctl、mmap、munmap * 用一個文件描述符來標一塊共享內存 * 能動態釋放部分共享內存 **整體架構** ![](https://box.kancloud.cn/6ead76b15b5051f0a44b2c619326e7de_666x417.jpg) **Ashmem驅動程序** * 與System V的共享內存一樣,都是基于臨時文件系統(tmpfs)來實現的,也就是每一塊共享內存都對應有一個臨時文件 * 可以通過IO控制命令ASHMEM_PIN對部分共享內存進行鎖定 * 可以通過IO控制命令ASHMEM_UNPIN對部分共享內存進行解鎖 * 沒有鎖定的部分共享內存,在系統內存緊張時會被回收 **C訪問接口** * ashmem_create_region:創建匿名共享內存 * open /dev/ashmem * mmap /dev/ashmem * ashmem_pin_region:鎖定部分匿名共享內存 * ioctl ASHMEM_PIN * ashmem_unpin_region:解鎖部分匿名共享內存 * ioctl ASHMEM_UNPIN **C++訪問接口** * MemoryHeapBase * 用來訪問一塊匿名共享內存 * Binder Service,實現IMemoryHeap接口 * 對應的Binder代理為BpMemoryHeap * MemoryBase: Binder Service * 在MemoryHeapBase的基礎上實現,用來訪問一整塊匿名共享內存的其中一部分 * Binder Service ,實現IMemory接口 * 對應的Binder代理為BpMemory **Java訪問接口** **android.os.MemoryFile** ![](https://box.kancloud.cn/8714c64a4dc45696617cf0b2599b6acd_728x129.png) ![](https://box.kancloud.cn/ead93d30736f604e68ae891301d72c23_720x228.png) **Android 2.3之后,第二個構造函數已不存在,因此MemoryFile只能作為內存文件使用** **Ashmem進程間共享原理--文件、文件結構體、文件描述符的關系** ![](https://box.kancloud.cn/4deb683e1219f69b0fcf3ea3ff188fed_731x389.jpg) **Ashmem進程間共享原理--兩個在不同進程中的文件描述符對應同一個指向設備文件/dev/ashmem的文件結構體** ![](https://box.kancloud.cn/357558620c7833fd293032bbd7cc0974_512x391.jpg) **可以通過Binder IPC在進程間進行傳遞和共享 – flat_binder_object(BINDER_TYPE_FD)** ![](https://box.kancloud.cn/9113da10f01d2118d885335782f665b9_601x447.png)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看