深入理解Camera 硬件抽象層


和你一起終身學習,這里是程序員Android

經典好文推薦,通過閱讀本文,您將收獲以下知識點:

一、概覽
二、Camera HIDL 接口
三、Camera Provider 主程序
四、Camera HAL3 接口

一、概覽

始於谷歌的Treble開源項目,基於接口與實現的分離的設計原則,谷歌加入了Camera Provider這一抽象層,該層作為一個獨立進程存在於整個系統中,並且通過HIDL這一自定義語言成功地將Camera Hal Module從Camera Service中解耦出來,承擔起了對Camera HAL的封裝工作,縱觀整個Android系統,對於Camera Provider而言,對上是通過HIDL接口負責與Camera Service的跨進程通信,對下通過標准的HAL3接口下發針對Camera的實際操作,這儼然是一個中央樞紐般的調配中心的角色,而事實上正是如此,由此看來,對Camera Provider的梳理變得尤為重要,接下來就以我個人理解出發來簡單介紹下Camera Provider。

Camera Provider通過提供標准的HIDL接口給Camera Service進行調用,保持與Service的正常通信,其中谷歌將HIDL接口的定義直接暴露給平台廠商進行自定義實現,其中為了極大地減輕並降低開發者的工作量和開發難度,谷歌很好地封裝了其跨進程實現細節,同樣地,Camera Provider通過標准的HAL3接口,向下控制着具體的Camera HAL Module,而這個接口依然交由平台廠商負責去實現,而進程內部則通過簡單的函數調用,將HIDL接口與HAL3接口完美的銜接起來,由此構成了Provider整體架構。

image

由圖中可以看出Camera Provider進程由兩部分組成,一是運行在系統中的主程序通過提供了標准的HIDL接口保持了與Camera Service的跨進程通訊,二是為了進一步擴展其功能,通過dlopen方式加載了一系列So庫,而其中就包括了實現了Camera HAL3接口的So庫,而HAL3接口主要定義了主要用於實現圖像控制的功能,其實現主要交由平台廠商或者開發者來完成,所以Camera HAL3 So庫的實現各式各樣,在高通平台上,這里的實現就是我們本文重點需要分析的CamX-CHI框架。

在開始梳理CamX-CHI之前,不防先從上到下,以接口為主線簡單梳理下Camera Provider的各個部分:

二、Camera HIDL 接口

首先需要明確一個概念,就是HIDL是一種自定義語言,其核心是接口的定義,而谷歌為了使開發者將注意力落在接口的定義上而不是機制的實現上,主動封裝了HIDL機制的實現細節,開發者只需要通過*.hal文件定義接口,填充接口內部實際的實現即可,接下來來看下具體定義的幾個主要接口:

在這里插入圖片描述

因為HIDL機制本身是跨進程通訊的,所以Camera Service本身通過HIDL接口獲取的對象都會有Bn端和Bp端,分別代表了Binder兩端,接下來為了方便理解,我們都省略掉Bn/Bp說法,直接用具體接口類代表,忽略跨進程兩端的區別。
ICameraProvider.hal源碼如下:

package android.hardware.camera.provider@2.4;

import ICameraProviderCallback;
import android.hardware.camera.common@1.0::types;
import android.hardware.camera.device@1.0::ICameraDevice;
import android.hardware.camera.device@3.2::ICameraDevice;

interface ICameraProvider {

    setCallback(ICameraProviderCallback callback) generates (Status status);

    getVendorTags() generates (Status status, vec<VendorTagSection> ps);

    getCameraIdList() generates (Status status, vec<string> cameraDeviceNames);

    isSetTorchModeSupported() generates (Status status, bool support);

    getCameraDeviceInterface_V1_x(string cameraDeviceName) generates (Status status, android.hardware.camera.device@1.0::ICameraDevice device);

    getCameraDeviceInterface_V3_x(string cameraDeviceName) generates (Status status, android.hardware.camera.device@3.2::ICameraDevice device);
};

該文件中定義了ICameraProvider接口類,由CameraProvider繼承並實現,在Camera Provider啟動的時候被實例化,主要接口如下:

  • getCameraDeviceInterface_V3_x: 該方法主要用於Camera Service獲取ICameraDevice,通過該對象可以控制Camera 設備的諸如配置數據流、下發request等具體行為。

  • setCallback:將Camera Service 實現的ICameraProviderCallback傳入Camera Provider,一旦Provider有事件產生時便可以通過該對象通知Camera Service。

ICameraProviderCallback.hal源碼如下:

package android.hardware.camera.provider@2.4;

import android.hardware.camera.common@1.0::types;

interface ICameraProviderCallback {

    cameraDeviceStatusChange(string cameraDeviceName, CameraDeviceStatus newStatus);

    torchModeStatusChange(string cameraDeviceName, TorchModeStatus newStatus);
};

該文件中定義了ICameraProviderCallback回調接口類,該接口由Camera Service 中的CameraProviderManager::ProviderInfo繼承並實現,在Camera Service 啟動的時候被實例化,通過調用ICameraProvider::setCallback接口注冊到Camera Provider中,其主要接口如下:

  • cameraDeviceStatusChange:將Camera 設備狀態上傳至Camera Service,狀態由CameraDeviceStatus定義

ICameraDevice.hal源碼如下:

package android.hardware.camera.device@3.2;

import android.hardware.camera.common@1.0::types;
import ICameraDeviceSession;
import ICameraDeviceCallback;

interface ICameraDevice {

    getResourceCost() generates (Status status, CameraResourceCost resourceCost);

    getCameraCharacteristics() generates (Status status, CameraMetadata cameraCharacteristics);

    setTorchMode(TorchMode mode) generates (Status status);

    open(ICameraDeviceCallback callback) generates (Status status, ICameraDeviceSession session);

    dumpState(handle fd);
 };

該文件中定義了ICameraDevice接口類,由CameraDevice::TrampolineDeviceInterface_3_2實現,其主要接口如下:

  • open:用於創建一個Camera設備,並且將Camera Service中繼承ICameraDeviceCallback並實現了相應接口的Camera3Device作為參數傳入Provider中,供Provider上傳事件或者圖像數據。

  • getCameraCharacteristics:用於獲取Camera設備的屬性。

ICameraDeviceCallback.hal源碼如下:

package android.hardware.camera.device@3.2;

import android.hardware.camera.common@1.0::types;

interface ICameraDeviceCallback {
    processCaptureResult(vec<CaptureResult> results);
    notify(vec<NotifyMsg> msgs);
};

該文件中定義了ICameraDeviceCallback接口類,由Camera Service中的Camera3Device繼承並實現,通過調用ICameraDevice::open方法注冊到Provider中,其主要接口如下:

  • processCaptureResult_3_4: 一旦有圖像數據產生會通過調用該方法將數據以及meta data上傳至Camera Service。

  • notify: 通過該方法上傳事件至Camera Service中,比如shutter事件等。

ICameraDeviceSession.hal源碼如下:

package android.hardware.camera.device@3.2;

import android.hardware.camera.common@1.0::types;

interface ICameraDeviceSession {

    constructDefaultRequestSettings(RequestTemplate type) generates (Status status, CameraMetadata requestTemplate);

    configureStreams(StreamConfiguration requestedConfiguration) generates (Status status, HalStreamConfiguration halConfiguration);

    processCaptureRequest(vec<CaptureRequest> requests, vec<BufferCache> cachesToRemove) generates (Status status, uint32_t numRequestProcessed);

    getCaptureRequestMetadataQueue() generates (fmq_sync<uint8_t> queue);

    getCaptureResultMetadataQueue() generates (fmq_sync<uint8_t> queue);

    flush() generates (Status status); close();
};

該文件中定義了ICameraDeviceSession接口類,由CameraDeviceSession::TrampolineSessionInterface_3_2繼承並實現,其主要接口如下:

  • constructDefaultRequestSettings:用於創建默認的Request配置項。

  • configureStreams_3_5:用於配置數據流,其中包括了output buffer/Surface/圖像格式大小等屬性。

  • processCaptureRequest_3_4:下發request到Provider中,一個request對應着一次圖像需求。

  • close: 關閉當前會話。

三 、Camera Provider 主程序

接下來進入到Provider內部去看看,整個進程是如何運轉的,以下圖為例進行分析:

在這里插入圖片描述

在系統初始化的時候,系統會去運行android.hardware.camera.provider@2.4-service_64程序啟動Provider進程,並加入HW Service Manager中接受統一管理,在該過程中實例化了一個LegacyCameraProviderImpl_2_4對象,並在其構造函數中通過hw_get_module標准方法獲取HAL的camera_module_t結構體,並將其存入CameraModule對象中,之后通過調用該camera_modult_t結構體的init方法初始化HAL Module,緊接着調用其get_number_of_camera方法獲取當前HAL支持的Camera數量,最后通過調用其set_callbacks方法將LegcyCameraProviderImpl_2_4(LegcyCameraProviderImpl_2_4繼承了camera_modult_callback_t)作為參數傳入CamX-CHI中,接受來自CamX-CHI中的數據以及事件,當這一系列動作完成了之后,Camera Provider進程便一直便存在於系統中,監聽着來自Camera Service的調用。

在這里插入圖片描述

接下來以上圖為例簡單介紹下Provider中幾個重要流程:

  • Camera Service通過調用ICameraProvider的getCameraDeviceInterface_v3_x接口獲取ICameraDevice,在此過程中,Provider會去實例化一個CameraDevice對象,並且將之前存有camera_modult_t結構體的CameraModule對象傳入CameraDevice中,這樣就可以在CameraDevice內部通過CameraModule訪問到camera_module_t的相關資源,然后將CameraDevice內部類TrampolineDeviceInterface_3_2(該類繼承並實現了ICameraDevice接口)返回給Camera Service。

  • Camera Service通過之前獲取的ICameraDevice,調用其open方法來打開Camera設備,接着在Provider中會去調用CameraDevice對象的open方法,在該方法內部會去調用camera_module_t結構體的open方法,從而獲取到HAL部分的camera3_device_t結構體,緊接着Provider會實例化一個CameraDeviceSession對象,並且將剛才獲取到的camera3_device_t結構體以參數的方式傳入CameraDeviceSession中,在CameraDeviceSession的構造方法中又會調用CameraDeviceSession的initialize方法,在該方法內部又會去調用camera3_device_t結構體的ops內的initialize方法開始HAL部分的初始化工作,最后CameraDeviceSession對象被作為camera3_callback_ops的實現傳入HAL,接收來自HAL的數據或者具體事件,當一切動作都完成后,Provider會將CameraDeviceSession::TrampolineSessionInterface_3_2(該類繼承並實現了ICameraDeviceSession接口)對象通過HIDL回調的方法返回給Camera Service中。

  • Camera Service通過調用ICameraDevcieSession的configureStreams_3_5接口進行數據流的配置,在Provider中,最終會通過調用之前獲取的camera3_device_t結構體內ops的configure_streams方法下發到HAL中進行處理。

  • Camera Service通過調用ICameraDevcieSession的processCaptureRequest_3_4接口下發request請求到Provider中,在Provider中,最終依然會通過調用獲取的camera3_device_t結構體內ops中的process_capture_request方法將此次請求下發到HAL中進行處理。

從整個流程不難看出,這幾個接口最終對應的是HAL3的接口,並且Provider並沒有經過太多復雜的額外的處理。

四、Camera HAL3 接口

HAL硬件抽象層(Hardware Abstraction Layer),是谷歌開發的用於屏蔽底層硬件抽象出來的一個軟件層, 每一個平台廠商可以將不開源的代碼封裝在這一層,僅僅提供二進制文件。
該層定義了自己的一套通用標准接口,平台廠商務必按照以下規則定義自己的Module:

  • 每一個硬件模塊都通過hw_module_t來描述,具有固定的名字HMI

  • 每一個硬件模塊都必須實現hw_module_t里面的open方法,用於打開硬件設備,並返回對應的操作接口集合

  • 硬件的操作接口集合使用hw_device_t 來描述,並可以通過自定義一個更大的包含hw_device_t的結構體來拓展硬件操作集合

其中代表硬件模塊的是hw_module_t,對應的設備是通過hw_device_t來描述,這兩者的定義如下:
hw_module_t/hw_device_t源碼如下:

typedef struct hw_module_t {
    uint32_t tag; //HMI
    struct hw_module_methods_t* methods;
} hw_module_t;

typedef struct hw_module_methods_t {
    int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
} hw_module_methods_t;

typedef struct hw_device_t {
    struct hw_module_t* module;
    int (*close)(struct hw_device_t* device);
} hw_device_t;

從上面的定義可以看出,主要是通過hw_module_t 代表了模塊,通過其open方法用來打開一個設備,而該設備是用hw_device_t來表示,其中除了用來關閉設備的close方法外,並無其它方法,由此可見谷歌定義的HAL接口,並不能滿足絕大部分HAL模塊的需要,所以谷歌想出了一個比較好的解決方式,那便是將這兩個基本結構嵌入到更大的結構體內部,同時在更大的結構內部定義了各自模塊特有的方法,用於實現模塊的功能,這樣,一來對上保持了HAL的統一規范,二來也擴展了模塊的功能。

基於上面的方式,谷歌便針對Camera 提出了HAL3接口,其中主要包括了用於代表一系列操作主體的結構體以及具體操作函數,接下來我們分別進行詳細介紹:

1. 核心結構體解析

HAL3中主要定義了camera_module_t/camera3_device_t/camera3_stream_configuration/camera3_stream以及camera3_stream_buffer幾個主要結構體。

其中camera_module_t以及camera3_device_t代碼定義如下:

typedef struct camera_module {
    hw_module_t common;
    int (*get_number_of_cameras)(void);
    int (*get_camera_info)(int camera_id, struct camera_info *info);
    nt (*set_callbacks)(const camera_module_callbacks_t *callbacks);
    void (*get_vendor_tag_ops)(vendor_tag_ops_t* ops);
    int (*open_legacy)(const struct hw_module_t* module, const char* id, uint32_t halVersion, struct hw_device_t** device);
    int (*set_torch_mode)(const char* camera_id, bool enabled);
    int (*init)();
    int (*get_physical_camera_info)(int physical_camera_id, int (*is_stream_combination_supported)(int camera_id,const camera_stream_combination_t *streams);
    int (*is_stream_combination_supported)(int camera_id, const camera_stream_combination_t *streams);
    void (*notify_device_state_change)(uint64_t deviceState);
} camera_module_t;

typedef struct camera3_device {
    hw_device_t common;
    camera3_device_ops_t *ops; //拓展接口,Camera HAL3定義的標准接口
    void *priv;
} camera3_device_t;

由定義不難發現,camera_module_t包含了hw_module_t,主要用於表示Camera模塊,其中定義了諸如get_number_of_cameras以及set_callbacks等擴展方法,而camera3_device_t包含了hw_device_t,主要用來表示Camera設備,其中定義了camera3_device_ops操作方法集合,用來實現正常獲取圖像數據以及控制Camera的功能。

結構體camera3_stream_configuration代碼定義如下:

typedef struct camera3_stream_configuration {
    uint32_t num_streams;
    camera3_stream_t **streams;
    uint32_t operation_mode;
    const camera_metadata_t *session_parameters;
} camera3_stream_configuration_t;

該結構體主要用來代表配置的數據流列表,內部裝有上層需要進行配置的數據流的指針,內部的定義簡單介紹下:

  • num_streams: 代表了來自上層的數據流的數量,其中包括了output以及input stream。

  • streams: 是streams的指針數組,包括了至少一條output stream以及至多一條input stream。

  • operation_mode: 當前數據流的操作模式,該模式在camera3_stream_configuration_mode_t中被定義,HAL通過這個參數可以針對streams做不同的設置。

  • session_parameters: 該參數可以作為缺省參數,直接設置為NULL即可,CAMERA_DEVICE_API_VERSION_3_5以上的版本才支持。

結構體camera3_stream_t的代碼定義如下:

typedef struct camera3_stream {
    int stream_type;
    uint32_t width;
    uint32_t height;
    int format;
    uint32_t usage;
    uint32_t max_buffers;
    void *priv;
    android_dataspace_t data_space;
    int rotation;
    const char* physical_camera_id;
}camera3_stream_t;

該結構體主要用來代表具體的數據流實體,在整個的配置過程中,需要在上層進行填充,當下發到HAL中后,HAL會針對其中的各項屬性進行配置,這里便簡單介紹下其內部的各個元素的意義:

  • stream_type: 表示數據流的類型,類型在camera3_stream_type_t中被定義。

  • width:表示當前數據流中的buffer的寬度。

  • height: 表示當前數據流中buffer的高度。

  • format: 表示當前數據流中buffer的格式,該格式是在system/core/include/system/graphics.h中被定義。

  • usage:表示當前數據流的gralloc用法,其用法定義在gralloc.h中。

  • max_buffers:指定了當前數據流中可能支持的最大數據buffer數量。

  • data_space: 指定了當前數據流buffer中存儲的圖像數據的顏色空間。

  • rotation:指定了當前數據流的輸出buffer的旋轉角度,其角度的定義在camera3_stream_rotation_t中,該參數由Camera Service進行設置,必須在HAL中進行設置,該參數對於input stream並沒有效果。

  • physical_camera_id:指定了當前數據流從屬的物理camera Id。

結構體camera3_stream_buffer_t定義如下:

typedef struct camera3_stream_buffer {
    camera3_stream_t *stream;
    buffer_handle_t *buffer
    int status;
    int acquire_fence;
    int release_fence;
} camera3_stream_buffer_t;

該結構體主要用來代表具體的buffer對象,其中重要元素如下:

  • stream: 代表了從屬的數據流

  • buffer:buffer句柄

2. 核心接口函數解析

HAL3的核心接口都是在camera3_device_ops中被定義,代碼定義如下:

typedef struct camera3_device_ops {
    int (*initialize)(const struct camera3_device *, const camera3_callback_ops_t *callback_ops);
    int (*configure_streams)(const struct camera3_device *, camera3_stream_configuration_t *stream_list);
    int (*register_stream_buffers)(const struct camera3_device *,const camera3_stream_buffer_set_t *buffer_set);
    const camera_metadata_t* (*construct_default_request_settings)(const struct camera3_device *, int type);
    int (*process_capture_request)(const struct camera3_device *, camera3_capture_request_t *request);
    void (*get_metadata_vendor_tag_ops)(const struct camera3_device*, vendor_tag_query_ops_t* ops);
    void (*dump)(const struct camera3_device *, int fd);
    int (*flush)(const struct camera3_device *);
    void (*signal_stream_flush)(const struct camera3_device*, uint32_t num_streams, const camera3_stream_t* const* streams);
    int (*is_reconfiguration_required)(const struct camera3_device*, const camera_metadata_t* old_session_params, const camera_metadata_t* new_session_params);
} camera3_device_ops_t;

從代碼中可以看見,該結構體定義了一系列的函數指針,用來指向平台廠商實際的實現方法,接下來就其中幾個方法簡單介紹下:

a) initialize
該方法必須在camera_module_t中的open方法之后,其它camera3_device_ops中方法之前被調用,主要用來將上層實現的回調方法注冊到HAL中,並且根據需要在該方法中加入自定義的一些初始化操作,另外,谷歌針對該方法在性能方面也有嚴格的限制,該方法需要在5ms內返回,最長不能超過10ms。

b) configure_streams
該方法在完成initialize方法之后,在調用process_capture_request方法之前被調用,主要用於重設當前正在運行的Pipeline以及設置新的輸入輸出流,其中它會將stream_list中的新的數據流替換之前配置的數據流。在調用該方法之前必須確保沒有新的request下發並且當前request的動作已經完成,否則會引起無法預測的錯誤。一旦HAL調用了該方法,則必須在內部配置好滿足當前數據流配置的幀率,確保這個流程的運行的順暢性。
其中包含了兩個參數,分別是camera3_device以及stream_list(camera3_stream_configuration_t ),其中第二個參數是上層傳入的數據流配置列表,該列表中必須包含至少一個output stream,同時至多包含一個input stream。
另外,谷歌針對該方法有着嚴格的性能要求,平台廠商在實現該方法的時候,需要在500ms內返回,最長不能超過1000ms。

c) construct_default_request_settings
該方法主要用於構建一系列默認的Camera Usecase的capture 設置項,通過camera_metadata_t來進行描述,其中返回值是一個camera_metadata_t指針,其指向的內存地址是由HAL來進行維護的,同樣地,該方法需要在1ms內返回,最長不能超過5ms。

d) process_capture_request
該方法用於下發單次新的capture request到HAL中, 上層必須保證該方法的調用都是在一個線程中完成,而且該方法是異步的,同時其結果並不是通過返回值給到上層,而是通過HAL調用另一個接口process_capture_result()來將結果返回給上層的,在使用的過程中,通過in-flight機制,保證短時間內下發足夠多的request,從而滿足幀率要求。

該方法的性能依然受到谷歌的嚴格要求,規定其需要在一幀圖像處理完的時長內返回,最長不超過4幀圖像處理完成的時長,比如當前預覽幀率是30幀,則該方法的操作耗時最長不能超過120ms,否則便會引起明顯的幀抖動,從而影響用戶體驗。

e) dump
該方法用於打印當前Camera設備的狀態,一般是由上層通過dumpsys工具輸出debug dump信息或者主動抓取bugreport的時候被調用,該方法必須是非阻塞實現,同時需要保證在1ms內返回,最長不能超過10ms。

f) flush
當上層需要執行新的configure_streams的時候,需要調用該方法去盡可能快地清除掉當前已經在處理中的或者即將處理的任務,為配置數據流提供一個相對穩定的環境,其具體工作如下:

  • 所有的還在流轉的request會盡可能快的返回

  • 並未開始進行流轉的request會直接返回,並攜帶錯誤信息

  • 任何可以打斷的硬件操作會立即被停止

  • 任何無法進行打斷的硬件操作會在當前狀態下進行休眠

flush會在所有的buffer都得以釋放,所有request都成功返回后才真正返回,該方法需要在100ms內返回,最長不能超過1000ms。

上面的一系列方法是上層直接對下控制Camera Hal,而一旦Camera Hal產生了數據或者事件的時候,可以通過camera3_callback_ops中定義的回調方法將數據或者事件返回至上層,該結構體定義如下:

typedef struct camera3_callback_ops {

    void (*process_capture_result)(const struct camera3_callback_ops *, const camera3_capture_result_t *result);
    void (*notify)(const struct camera3_callback_ops *, const camera3_notify_msg_t *msg);

    camera3_buffer_request_status_t (*request_stream_buffers)(
        const struct camera3_callback_ops *,
        uint32_t num_buffer_reqs,
        const camera3_buffer_request_t *buffer_reqs,
        /*out*/uint32_t *num_returned_buf_reqs,
        /*out*/camera3_stream_buffer_ret_t *returned_buf_reqs);

    void (*return_stream_buffers)( const struct camera3_callback_ops *, uint32_t num_buffers, const camera3_stream_buffer_t* const* buffers);
} camera3_callback_ops_t;

其中常用的回調方法主要有兩個:用於返回數據的process_capture_result以及用於返回事件的notify,接下來分別介紹下:

a) process_capture_result
該方法用於返回HAL部分產生的metadata和image buffers,它與request是多對一的關系,同一個request,可能會對應到多個result,比如可以通過調用一次該方法用於返回metadata以及低分辨率的圖像數據,再調用一次該方法用於返回jpeg格式的拍照數據,而這兩次調用時對應於同一個process_capture_request動作。

同一個Request的Metadata以及Image Buffers的先后順序無關緊要,但是同一個數據流的不同Request之間的Result必須嚴格按照Request的下發先后順序進行依次返回的,如若不然,會導致圖像數據顯示出現順序錯亂的情況。

該方法是非阻塞的,而且並且必須要在5ms內返回。

b) notify
該方法用於異步返回HAL事件到上層,必須非阻塞實現,而且要在5ms內返回。

谷歌為了將系統框架和平台廠商的自定義部分相分離,在Android上推出了Treble項目,該項目直接將平台廠商的實現部分放入vendor分區中進行管理,進而與system分區保持隔離,這樣便可以在相互獨立的空間中進行各自的迭代升級,而互不干擾,而在相機框架體系中,便將Camera HAL Module從Camera Service中解耦出來,放入獨立進程Camera Provider中進行管理,而為了更好的進行跨進程訪問,谷歌針對Provider提出了HIDL機制用於Camera Servic對於Camera Provier的訪問,而HIDL接口的實現是在Camera Provider中實現,針對Camera HAL Module的控制又是通過谷歌制定的Camera HAL3接口來完成,所以由此看來,Provider的職責也比較簡單,通過HIDL機制保持與Camera Service的通信,通過HAL3接口控制着Camera HAL Module。

原文鏈接:https://blog.csdn.net/u012596975/article/details/107137523

相關文章友情推薦 

1. Android開發干貨分享

至此,本篇已結束。轉載網絡的文章,小編覺得很優秀,歡迎點擊閱讀原文,支持原創作者,如有侵權,懇請聯系小編刪除,歡迎您的建議與指正。同時期待您的關注,感謝您的閱讀,謝謝!

點個在看,方便您使用時快速查看!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM