亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

深入剖析Android中init進程實現的C語言源碼

2020-05-23 14:16:52
字體:
來源:轉載
供稿:網友

這篇文章主要介紹了Android中init進程實現的C語言源碼,init屬性服務在安卓中屬于系統的底層Linux服務,需要的朋友可以參考下

概述

init是一個進程,確切的說,它是Linux系統中用戶空間的第一個進程。由于Android是基于Linux內核的,所以init也是Android系統中用戶空間的第一個進程。init的進程號是1。作為天字第一號進程,init有很多重要的工作:

init提供property service(屬性服務)來管理Android系統的屬性。

init負責創建系統中的關鍵進程,包括zygote。

以往的文章一上來就介紹init的源碼,但是我這里先從這兩個主要工作開始。搞清楚這兩個主要工作是如何實現的,我們再回頭來看init的源碼。

這篇文章主要是介紹init進程的屬性服務。

跟init屬性服務相關的源碼目錄如下:

 

 
  1. system/core/init/ 
  2. bionic/libc/bionic/ 
  3. system/core/libcutils/ 

屬性服務

在windows平臺上有一個叫做注冊表的東西,它可以存儲一些類似key/value的鍵值對。一般而言,系統或者某些應用程序會把自己的一些屬性存儲在注冊表中,即使系統重啟或應用程序重啟,它還能根據之前在注冊表中設置的屬性值,進行相應的初始化工作。

Android系統也提供了類似的機制,稱之為屬性服務(property service)。應用程序可以通過這個服務查詢或者設置屬性。我們可以通過如下命令,獲取手機中屬性鍵值對。

 

 
  1. adb shell getprop 

例如紅米Note手機的屬性值如下:

 

 
  1. [ro.product.device]: [lcsh92_wet_jb9] 
  2. [ro.product.locale.language]: [zh] 
  3. [ro.product.locale.region]: [CN] 
  4. [ro.product.manufacturer]: [Xiaomi] 

在system/core/init/init.c文件的main函數中,跟屬性服務的相關代碼如下:

 

 
  1. property_init(); 
  2. queue_builtin_action(property_service_init_action, "property_service_init"); 

接下來,我們分別看一下這兩處代碼的具體實現。

屬性服務初始化 創建存儲空間

首先,我們先來看一下property_init函數的源碼(/system/core/init/property_service.c):

 

 
  1. void property_init(void
  2. init_property_area(); 

property_init函數中只是簡單的調用了init_property_area方法,接下來我們看一下這個方法的具體實現:

 

 
  1. static int property_area_inited = 0; 
  2. static workspace pa_workspace; 
  3. static int init_property_area(void
  4. // 屬性空間是否已經初始化 
  5. if (property_area_inited) 
  6. return -1; 
  7.  
  8. if (__system_property_area_init()) 
  9. return -1; 
  10.  
  11. if (init_workspace(&pa_workspace, 0)) 
  12. return -1; 
  13.  
  14. fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); 
  15.  
  16. property_area_inited = 1; 
  17. return 0; 

從init_property_area函數,我們可以看出,函數首先判斷屬性內存區域是否已經初始化過,如果已經初始化,則返回-1。如果沒有初始化,我們接下來會發現有兩個關鍵函數__system_property_area_init和init_workspace應該是跟內存區域初始化相關。那我們分別分析一下這兩個函數具體實現。

 

 
  1. __system_property_area_init 
  2.  
  3. __system_property_area_init函數位于/bionic/libc/bionic/system_properties.c文件中,具體代碼實現如下: 
  4.  
  5. struct prop_area { 
  6. unsigned bytes_used; 
  7. unsigned volatile serial; 
  8. unsigned magic; 
  9. unsigned version; 
  10. unsigned reserved[28]; 
  11. char data[0]; 
  12. }; 
  13. typedef struct prop_area prop_area; 
  14. prop_area *__system_property_area__ = NULL; 
  15.  
  16. #define PROP_FILENAME "/dev/__properties__" 
  17. static char property_filename[PATH_MAX] = PROP_FILENAME;  
  18.  
  19. #define PA_SIZE (128 * 1024) 
  20.  
  21.  
  22. static int map_prop_area_rw() 
  23. prop_area *pa; 
  24. int fd; 
  25. int ret; 
  26.  
  27. /** 
  28. * O_RDWR ==> 讀寫 
  29. * O_CREAT ==> 若不存在,則創建 
  30. * O_NOFOLLOW ==> 如果filename是軟鏈接,則打開失敗 
  31. * O_EXCL ==> 如果使用O_CREAT是文件存在,則可返回錯誤信息 
  32. */ 
  33. fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); 
  34. if (fd < 0) { 
  35. if (errno == EACCES) { 
  36. abort(); 
  37. return -1; 
  38.  
  39. ret = fcntl(fd, F_SETFD, FD_CLOEXEC); 
  40. if (ret < 0) 
  41. goto out; 
  42.  
  43. if (ftruncate(fd, PA_SIZE) < 0) 
  44. goto out; 
  45.  
  46. pa_size = PA_SIZE; 
  47. pa_data_size = pa_size - sizeof(prop_area); 
  48. compat_mode = false
  49.  
  50. // mmap映射文件實現共享內存 
  51. pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 
  52. if (pa == MAP_FAILED) 
  53. goto out; 
  54.  
  55. /*初始化內存地址中所有值為0*/ 
  56. memset(pa, 0, pa_size); 
  57. pa->magic = PROP_AREA_MAGIC; 
  58. pa->version = PROP_AREA_VERSION; 
  59. pa->bytes_used = sizeof(prop_bt); 
  60.  
  61. __system_property_area__ = pa; 
  62.  
  63. close(fd); 
  64. return 0; 
  65.  
  66. out: 
  67. close(fd); 
  68. return -1; 
  69.  
  70. int __system_property_area_init() 
  71. return map_prop_area_rw(); 

代碼比較好理解,主要內容是利用mmap映射property_filename創建了一個共享內存區域,并將共享內存的首地址賦值給全局變量__system_property_area__。

關于mmap映射文件實現共享內存IPC通信機制,可以參考這篇文章:mmap實現IPC通信機制

init_workspace

接下來,我們來看一下init_workspace函數的源碼(/system/core/init/property_service.c):

 

 
  1. typedef struct { 
  2. void *data; 
  3. size_t size; 
  4. int fd; 
  5. }workspace; 
  6.  
  7. static int init_workspace(workspace *w, size_t size) 
  8. void *data; 
  9. int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW); 
  10. if (fd < 0) 
  11. return -1; 
  12.  
  13. w->size = size; 
  14. w->fd = fd; 
  15. return 0; 

客戶端進程訪問屬性內存區域

雖然屬性內存區域是init進程創建的,但是Android系統希望其他進程也能夠讀取這塊內存區域里的內容。為了做到這一點,init進程在屬性區域初始化過程中做了如下兩項工作:

把屬性內存區域創建在共享內存上,而共享內存是可以跨進程的。這一點,在上述代碼中是通過mmap映射/dev/__properties__文件實現的。pa_workspace變量中的fd成員也保存了映射文件的句柄。

如何讓其他進程知道這個共享內存句柄呢?Android先將文件映射句柄賦值給__system_property_area__變量,這個變量屬于bionic_lic庫中的輸出的一個變量,然后利用了gcc的constructor屬性,這個屬性指明了一個__lib_prenit函數,當bionic_lic庫被加載時,將自動調用__libc_prenit,這個函數內部完成共享內存到本地進程的映射工作。

只講原理是不行的,我們直接來看一下__lib_prenit函數代碼的相關實現:

 

 
  1. void __attribute__((constructor)) __libc_prenit(void); 
  2. void __libc_prenit(void
  3. // ... 
  4. __libc_init_common(elfdata); // 調用這個函數 
  5. // ... 
  6.  
  7.  
  8. __libc_init_common函數為: 
  9.  
  10. void __libc_init_common(uintptr_t *elfdata) 
  11. // ... 
  12. __system_properties_init(); // 初始化客戶端的屬性存儲區域 
  13.  
  14.  
  15. __system_properties_init函數有回到了我們熟悉的/bionic/libc/bionic/system_properties.c文件: 
  16.  
  17. static int get_fd_from_env(void
  18. char *env = getenv("ANDROID_PROPERTY_WORKSPACE"); 
  19.  
  20. if (! env) { 
  21. return -1; 
  22.  
  23. return atoi(env); 
  24.  
  25. static int map_prop_area() 
  26. bool formFile = true
  27. int result = -1; 
  28. int fd; 
  29. int ret; 
  30.  
  31. fd = open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); 
  32. if (fd >= 0) { 
  33. /* For old kernels that don't support O_CLOEXEC */ 
  34. ret = fcntl(fd, F_SETFD, FD_CLOEXEC); 
  35. if (ret < 0) 
  36. goto cleanup; 
  37.  
  38. if ((fd < 0) && (error == ENOENT)) { 
  39. fd = get_fd_from_env(); 
  40. fromFile = false
  41.  
  42. if (fd < 0) { 
  43. return -1; 
  44.  
  45. struct stat fd_stat; 
  46. if (fstat(fd, &fd_stat) < 0) { 
  47. goto cleanup; 
  48.  
  49. if ((fd_stat.st_uid != 0) 
  50. || (fd_stat.st_gid != 0) 
  51. || (fd_stat.st_mode & (S_IWGRP | S_IWOTH) != 0) 
  52. || (fd_stat.st_size < sizeof(prop_area))) { 
  53. goto cleanup; 
  54.  
  55. pa_size = fd_stat.st_size; 
  56. pa_data_size = pa_size - sizeof(prop_area); 
  57.  
  58. /*  
  59. * 映射init創建的屬性內存到本地進程空間,這樣本地進程就可以使用這塊共享內存了。 
  60. * 注意:映射時制定了PROT_READ屬性,所以客戶端進程只能讀屬性,不能設置屬性。 
  61. */ 
  62. prop_area *pa = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0); 
  63.  
  64. if (pa == MAP_FAILED) { 
  65. goto cleanup; 
  66.  
  67. if ((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION && pa->version != PROP_AREA_VERSION_COMPAT)) { 
  68. munmap(pa, pa_size); 
  69. goto cleanup; 
  70.  
  71. if (pa->version == PROP_AREA_VERSION_COMPAT) { 
  72. compat_mode = true
  73.  
  74. result = 0; 
  75.  
  76. __system_property_area__ = pa; 
  77. cleanup: 
  78. if (fromFile) { 
  79. close(fd); 
  80.  
  81. return result; 
  82.  
  83. int __system_properties_init() 
  84. return map_prop_area(); 

通過對源碼的閱讀,可以發現,客戶端通過mmap映射,可以讀取屬性內存的內容,但是沒有權限設置屬性。那客戶端是如何設置屬性的呢?這就涉及到下面要將的屬性服務器了。

屬性服務器的分析

init進程會啟動一個屬性服務器,而客戶端只能通過與屬性服務器的交互來設置屬性。

啟動屬性服務器

先來看一下屬性服務器的內容,它由property_service_init_action函數啟動,源碼如下(/system/core/init/init.c&&property_service.c):

 

 
  1. static int property_service_init_action(int nargs, char **args) 
  2. start_property_service(); 
  3. return 0; 
  4.  
  5. static void load_override_properties() 
  6. #ifdef ALLOW_LOCAL_PROP_OVERRIDE 
  7. char debuggable[PROP_VALUE_MAX]; 
  8. int ret; 
  9.  
  10. ret = property_get("ro.debuggable", debuggable); 
  11. if (ret && (strcmp(debuggable, "1") == 0)) { 
  12. load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); 
  13. #endif 
  14.  
  15. static void load_properties(char *data) 
  16. char *key, *value, *eol, *sol, *tmp; 
  17.  
  18. sol = data; 
  19. while ((eol = strchr(sol, '/n'))) { 
  20. key = sol; 
  21. // 賦值下一行的指針給sol 
  22. *eol ++ = 0; 
  23. sol = eol; 
  24.  
  25. value = strchr(key, '='); 
  26. if (value == 0) continue
  27. *value++ = 0; 
  28.  
  29. while (isspace(*key)) key ++; 
  30. if (*key == '#'continue
  31. tmp = value - 2; 
  32. while ((tmp > key) && isspace(*tmp)) *tmp-- = 0; 
  33.  
  34. while (isspace(*value)) value ++; 
  35. tmp = eol - 2; 
  36. while ((tmp > value) && isspace(*tmp)) *tmp-- = 0; 
  37.  
  38. property_set(key, value); 
  39.  
  40. int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) 
  41. struct sockaddr_un addr; 
  42. int fd, ret; 
  43. char *secon; 
  44.  
  45. fd = socket(PF_UNIX, type, 0); 
  46. if (fd < 0) { 
  47. ERROR("Failed to open socket '%s': %s/n", name, strerror(errno)); 
  48. return -1; 
  49.  
  50. memset(&addr, 0, sizeof(addr)); 
  51. addr.sun_family = AF_UNIX; 
  52. snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name); 
  53.  
  54. ret = unlink(addr.sun_path); 
  55. if (ret != 0 && errno != ENOENT) { 
  56. goto out_close; 
  57.  
  58. ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); 
  59. if (ret) { 
  60. goto out_unlink; 
  61. chown(addr.sun_path, uid, gid); 
  62. chmod(addr.sun_path, perm); 
  63.  
  64. return fd; 
  65.  
  66. out_unlink: 
  67. unlink(addr.sun_path); 
  68. out_close: 
  69. close(fd); 
  70. return -1; 
  71.  
  72. #define PROP_PATH_SYSTEM_BUILD "/system/build.prop" 
  73. #define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop" 
  74. #define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop" 
  75. #define PROP_PATH_FACTORY "/factory/factory.prop" 
  76.  
  77. void start_property_service(void
  78. int fd; 
  79.  
  80. load_properties_from_file(PROP_PATH_SYSTEM_BUILD); 
  81. load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); 
  82. load_override_properties(); 
  83. /*Read persistent properties after all default values have been loaded.*/ 
  84. load_persistent_properties(); 
  85.  
  86. fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); 
  87. if (fd < 0) return
  88. fcntl(fd, F_SETFD, FD_CLOEXEC); 
  89. fcntl(fd, F_SETFL, O_NONBLOCK); 
  90.  
  91. listen(fd, 8); 
  92. property_set_fd = fd; 

從上述代碼可以看到,init進程除了會預寫入指定文件(例如:system/build.prop)屬性外,還會創建一個UNIX Domain Socket,用于接受客戶端的請求,構建屬性。那這個socket請求是再哪里被處理的呢?

答案是:在init中的for循環處已經進行了相關處理。

服務端處理設置屬性請求

接收屬性設置請求的地方是在init進程中,相關代碼如下所示:

 

 
  1. int main(int argc, char **argv) 
  2. // ...省略不相關代碼 
  3.  
  4. for (;;) { 
  5. // ... 
  6. for (i = 0; i < fd_count; i ++) { 
  7. if (ufds[i].fd == get_property_set_fd()) 
  8. handle_property_set_fd(); 

從上述代碼可以看出,當屬性服務器收到客戶端請求時,init進程會調用handle_property_set_fd函數進行處理,函數位置是:system/core/init/property_service.c,我們來看一下這個函數的實現源碼:

 

 
  1. void handle_property_set_fd() 
  2. prop_msg msg; 
  3. int s; 
  4. int r; 
  5. int res; 
  6. struct ucred cr; 
  7. struct sockaddr_un addr; 
  8. socklen_t addr_size = sizeof(addr); 
  9. socklen_t cr_size = sizeof(cr); 
  10. char *source_ctx = NULL; 
  11.  
  12. // 接收TCP連接 
  13. if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { 
  14. return
  15.  
  16. // 接收客戶端請求數據 
  17. r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); 
  18. if (r != sizeof(prop_msg)) { 
  19. ERROR("sys_prop: mis-match msg size received: %d expected : %d errno: %d/n", r, sizeof(prop_msg), errno); 
  20. close(s); 
  21. return
  22.  
  23. switch(msg.cmd) { 
  24. case PROP_MSG_SETPROP: 
  25. msg.name[PROP_NAME_MAX - 1] = 0; 
  26. msg.value[PROP_VALUE_MAX - 1] = 0; 
  27.  
  28. if (memcmp(msg.name, "ctl.", 4) == 0) { 
  29. close(s); 
  30. if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) { 
  31. handle_control_message((char*) msg.name + 4, (char*) msg.value); 
  32. else { 
  33. ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d/n", msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid); 
  34. else { 
  35. if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) { 
  36. property_set((char *) msg.name, (char*) msg.value); 
  37. close(s); 
  38. break
  39. default
  40. close(s); 
  41. break

當客戶端的權限滿足要求時,init就調用property_set進行相關處理。property_set源碼實現如下:

 

 
  1. int property_set(const char *name, const char *value) 
  2. prop_info *pi; 
  3. int ret; 
  4.  
  5. size_t namelen = strlen(name); 
  6. size_t valuelen = strlen(value); 
  7.  
  8. if (! is_legal_property_name(name, namelen)) return -1; 
  9. if (valuelen >= PROP_VALUE_MAX) return -1; 
  10.  
  11. // 從屬性空間中尋找是否已經存在該屬性值 
  12. pi = (prop_info*) __system_property_find(name); 
  13. if (pi != 0) { 
  14. // ro開頭的屬性被設置后,不允許再被修改 
  15. if (! strncmp(name, "ro.", 3)) return -1; 
  16.  
  17. __system_property_update(pi, value, valuelen); 
  18. else { 
  19. ret = __system_property_add(name, namelen, value, valuelen); 
  20.  
  21. // 有一些特殊的屬性需要特殊處理,例如net.和persist.開頭的屬性 
  22. if (strncmp("net.", name, strlen("net.")) == 0) { 
  23. if (strcmp("net.change", name) == 0) { 
  24. return 0; 
  25. property_set("net.change", name); 
  26. else if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) { 
  27. write_persistent_property(name, value); 
  28. property_changed(name, value); 
  29. return 0; 

屬性服務器端的工作基本到這里就完成了。最后,我們來看一下客戶端是如何發送設置屬性的socket請求。

客戶端發送請求

客戶端設置屬性時是調用了property_set(“sys.istest”, “true”)方法。從上述分析可知,該方法實現跟服務器端的property_set方法不同,該方法一定是發送了socket請求,該方法源碼位置為:/system/core/libcutils/properties.c:

 

 
  1. int property_set(const char *key, const char *value) 
  2. return __system_property_set(key, value); 

可以看到,property_set調用了__system_property_set方法,這個方法位于:/bionic/libc/bionic/system_properties.c文件中:

 

 
  1. struct prop_msg 
  2. unsigned cmd; 
  3. char name[PROP_NAME_MAX]; 
  4. char value[PROP_VALUE_MAX]; 
  5. }; 
  6. typedef struct prop_msg prop_msg; 
  7.  
  8. static int send_prop_msg(prop_msg *msg) 
  9. struct pollfd pollfds[1]; 
  10. struct sockaddr_un addr; 
  11. socklen_t alen; 
  12. size_t namelen; 
  13. int s; 
  14. int r; 
  15. int result = -1; 
  16.  
  17. s = socket(AF_LOCAL, SOCK_STREAM, 0); 
  18. if (s < 0) { 
  19. return result; 
  20.  
  21. memset(&addr, 0, sizeof(addr)); 
  22. namelen = strlen(property_service_socket); 
  23. strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path)); 
  24. addr.sun_family = AF_LOCAL; 
  25. alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; 
  26.  
  27. if (TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) { 
  28. close(s); 
  29. return result; 
  30.  
  31. r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0)); 
  32.  
  33. close(s); 
  34. return result; 
  35.  
  36. int __system_property_set(const char *key, const char *value) 
  37. int err; 
  38. prop_msg msg; 
  39.  
  40. if (key == 0) return -1; 
  41. if (value == 0) value = ""
  42. if (strlen(key) >= PROP_NAME_MAX) return -1; 
  43. if (strlen(value) >= PROP_VALUE_MAX) return -1; 
  44.  
  45. memset(&msg, 0, sizeof(msg)); 
  46. msg.cmd = PROP_MSG_SETPROP; 
  47. strlcpy(msg.name, key, sizeof(msg.name)); 
  48. strlcpy(msg.value, value, sizeof(msg.value)); 
  49.  
  50. err = send_prop_msg(&msg); 
  51. if (err < 0) { 
  52. return err; 
  53. return 0; 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
www.亚洲人.com| 久久91精品国产91久久跳| 欧美肥臀大乳一区二区免费视频| 国产精品色午夜在线观看| 久久99精品久久久久久噜噜| 欧美日韩免费看| 国产精自产拍久久久久久| 亚洲视频电影图片偷拍一区| 日韩欧美一区二区三区| 欧美日韩成人在线观看| 久久av中文字幕| 国语自产精品视频在免费| 国产精品看片资源| 亚洲japanese制服美女| 欧美成人亚洲成人日韩成人| 亚洲尤物视频网| 欧美日韩亚洲视频| 91免费人成网站在线观看18| 国产精品亚洲片夜色在线| 国产日韩欧美自拍| 欧美色另类天堂2015| 97香蕉久久夜色精品国产| 成人福利视频在线观看| 69精品小视频| 久久久视频免费观看| 亚州国产精品久久久| 欧美高清不卡在线| 色播久久人人爽人人爽人人片视av| 色综合久久精品亚洲国产| 欧美一级片久久久久久久| 久久国产精品久久久久| 97av视频在线| 国产区亚洲区欧美区| 亚洲欧美福利视频| 久久视频免费在线播放| 茄子视频成人在线| 国产精品999999| 性欧美暴力猛交69hd| 米奇精品一区二区三区在线观看| 视频直播国产精品| 久久久久久久久久久久久久久久久久av| 亚洲人成在线观| 国产日韩欧美中文| 久久人人爽人人爽人人片亚洲| 777777777亚洲妇女| 国产精品久久久久久久久免费| 欧美视频一区二区三区…| 米奇精品一区二区三区在线观看| 国产欧美日韩免费看aⅴ视频| 91免费国产网站| 亚洲欧美日韩精品久久亚洲区| 日本19禁啪啪免费观看www| 日韩av观看网址| 亚洲午夜久久久影院| 久久精品99国产精品酒店日本| 日韩精品日韩在线观看| 亚洲一区二区三区乱码aⅴ蜜桃女| 亚洲女人天堂色在线7777| 亚洲欧美日韩天堂| 欧美性xxxx极品hd欧美风情| 欧美一级电影免费在线观看| 国产在线不卡精品| 亚洲精品影视在线观看| 国产精品无av码在线观看| 日韩风俗一区 二区| 九九视频直播综合网| 国产一区av在线| 欧美色另类天堂2015| 亚洲欧美日韩精品久久奇米色影视| 久久五月天综合| 中文字幕欧美精品日韩中文字幕| 深夜福利91大全| 亚洲国产精品电影在线观看| 亚洲人永久免费| 国产精品一区二区三区成人| 91视频国产精品| 98视频在线噜噜噜国产| 亚洲欧美日韩综合| 国产成人精品午夜| 亚洲在线观看视频| 国产日韩在线观看av| 日韩电影中文字幕一区| 91久久夜色精品国产网站| 亚洲第一中文字幕| 国产自摸综合网| 久久国产色av| 日韩精品福利在线| 一本一本久久a久久精品牛牛影视| 久久综合88中文色鬼| 日韩激情av在线免费观看| 欧美国产日韩一区二区| 日韩在线小视频| 日韩电影免费观看中文字幕| 国产精品最新在线观看| 日韩电影中文字幕av| 一级做a爰片久久毛片美女图片| 成人免费网站在线| 欧美激情高清视频| 日韩亚洲欧美中文在线| 日韩精品在线免费| 日韩精品免费综合视频在线播放| 欧美大全免费观看电视剧大泉洋| 91精品国产色综合久久不卡98口| 日韩精品免费一线在线观看| 亚洲天堂网站在线观看视频| 国产精品免费一区二区三区都可以| 夜夜嗨av一区二区三区四区| 2020欧美日韩在线视频| 亚洲国产婷婷香蕉久久久久久| 神马久久桃色视频| 亚洲精品v欧美精品v日韩精品| 久久久久久网址| 国产欧美一区二区三区视频| 亚洲精品美女久久久久| 欧美香蕉大胸在线视频观看| 国产精品欧美一区二区三区奶水| 久久精品国产91精品亚洲| 亚洲成人黄色在线观看| 亚洲一品av免费观看| 欧美不卡视频一区发布| 亚洲电影免费观看高清完整版在线观看| 久久99热精品| 国产成+人+综合+亚洲欧美丁香花| 成人精品一区二区三区电影黑人| 欧美成年人视频| 亚洲人成电影在线| 国产精品自拍小视频| 欧美极品在线视频| 在线观看欧美日韩国产| 欧美电影免费观看高清完整| 日韩免费电影在线观看| 亚洲精品91美女久久久久久久| 欧美久久精品午夜青青大伊人| 2019中文字幕在线免费观看| 亚州欧美日韩中文视频| 亚洲精品动漫久久久久| 国产亚洲人成网站在线观看| 欧美成人亚洲成人日韩成人| 欧美极品欧美精品欧美视频| 国产欧美亚洲精品| 成人情趣片在线观看免费| 不卡av电影在线观看| 久久久久99精品久久久久| 疯狂做受xxxx高潮欧美日本| 成人激情春色网| 亚洲一二在线观看| 亚洲男人天堂视频| 91麻豆桃色免费看| 国产精品久久久久久久久久小说| 亚洲精品白浆高清久久久久久| 欧美激情久久久久| 中文字幕欧美日韩| 精品视频久久久久久| 2023亚洲男人天堂| 91精品久久久久久久久| 亚洲国产精品va在线看黑人动漫| 九九热精品视频| 久久久久久久爱| 欧美孕妇孕交黑巨大网站| 欧美成人午夜激情| 欧美资源在线观看| 亚洲va久久久噜噜噜久久天堂| 亚洲成人黄色在线观看| 精品久久久精品|