国产无遮挡裸体免费直播视频,久久精品国产蜜臀av,动漫在线视频一区二区,欧亚日韩一区二区三区,久艹在线 免费视频,国产精品美女网站免费,正在播放 97超级视频在线观看,斗破苍穹年番在线观看免费,51最新乱码中文字幕

Android系統(tǒng)進程間通信(IPC)機制Binder中的Server啟動過程源代碼分析

 更新時間:2016年08月29日 12:04:51   作者:羅升陽  
本文主要介紹Android IPC機制Binder中的Server啟動過程源代碼,這里對Binder 中Server 啟動過程中的源碼做了詳細的介紹,有研究Android源碼 Binder 通信的小伙伴可以參考下

        在前面一篇文章Android系統(tǒng)進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路中,介紹了在Android系統(tǒng)中Binder進程間通信機制中的Server角色是如何獲得Service Manager遠程接口的,即defaultServiceManager函數(shù)的實現(xiàn)。Server獲得了Service Manager遠程接口之后,就要把自己的Service添加到Service Manager中去,然后把自己啟動起來,等待Client的請求。本文將通過分析源代碼了解Server的啟動過程是怎么樣的。

        本文通過一個具體的例子來說明Binder機制中Server的啟動過程。我們知道,在Android系統(tǒng)中,提供了多媒體播放的功能,這個功能是以服務的形式來提供的。這里,我們就通過分析MediaPlayerService的實現(xiàn)來了解Media Server的啟動過程。

        首先,看一下MediaPlayerService的類圖,以便我們理解下面要描述的內(nèi)容。

        我們將要介紹的主角MediaPlayerService繼承于BnMediaPlayerService類,熟悉Binder機制的同學應該知道BnMediaPlayerService是一個Binder Native類,用來處理Client請求的。BnMediaPlayerService繼承于BnInterface<IMediaPlayerService>類,BnInterface是一個模板類,它定義在frameworks/base/include/binder/IInterface.h文件中:

template<typename INTERFACE> 
class BnInterface : public INTERFACE, public BBinder 
{ 
public: 
 virtual sp<IInterface>  queryLocalInterface(const String16& _descriptor); 
 virtual const String16&  getInterfaceDescriptor() const; 
 
protected: 
 virtual IBinder*   onAsBinder(); 
}; 

       這里可以看出,BnMediaPlayerService實際是繼承了IMediaPlayerService和BBinder類。IMediaPlayerService和BBinder類又分別繼承了IInterface和IBinder類,IInterface和IBinder類又同時繼承了RefBase類。

       實際上,BnMediaPlayerService并不是直接接收到Client處發(fā)送過來的請求,而是使用了IPCThreadState接收Client處發(fā)送過來的請求,而IPCThreadState又借助了ProcessState類來與Binder驅(qū)動程序交互。有關(guān)IPCThreadState和ProcessState的關(guān)系,可以參考上一篇文章Android系統(tǒng)進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路,接下來也會有相應的描述。IPCThreadState接收到了Client處的請求后,就會調(diào)用BBinder類的transact函數(shù),并傳入相關(guān)參數(shù),BBinder類的transact函數(shù)最終調(diào)用BnMediaPlayerService類的onTransact函數(shù),于是,就開始真正地處理Client的請求了。

      了解了MediaPlayerService類結(jié)構(gòu)之后,就要開始進入到本文的主題了。

      首先,看看MediaPlayerService是如何啟動的。啟動MediaPlayerService的代碼位于frameworks/base/media/mediaserver/main_mediaserver.cpp文件中:

int main(int argc, char** argv) 
{ 
 sp<ProcessState> proc(ProcessState::self()); 
 sp<IServiceManager> sm = defaultServiceManager(); 
 LOGI("ServiceManager: %p", sm.get()); 
 AudioFlinger::instantiate(); 
 MediaPlayerService::instantiate(); 
 CameraService::instantiate(); 
 AudioPolicyService::instantiate(); 
 ProcessState::self()->startThreadPool(); 
 IPCThreadState::self()->joinThreadPool(); 
} 

       這里我們不關(guān)注AudioFlinger和CameraService相關(guān)的代碼。
       先看下面這句代碼:

                       sp<ProcessState> proc(ProcessState::self());  

       這句代碼的作用是通過ProcessState::self()調(diào)用創(chuàng)建一個ProcessState實例。ProcessState::self()是ProcessState類的一個靜態(tài)成員變量,定義在frameworks/base/libs/binder/ProcessState.cpp文件中:

sp<ProcessState> ProcessState::self() 
{ 
 if (gProcess != NULL) return gProcess; 
  
 AutoMutex _l(gProcessMutex); 
 if (gProcess == NULL) gProcess = new ProcessState; 
 return gProcess; 
} 

       這里可以看出,這個函數(shù)作用是返回一個全局唯一的ProcessState實例gProcess。全局唯一實例變量gProcess定義在frameworks/base/libs/binder/Static.cpp文件中:

                        Mutex gProcessMutex; 
                        sp<ProcessState> gProcess;  

       再來看ProcessState的構(gòu)造函數(shù):

ProcessState::ProcessState() 
 : mDriverFD(open_driver()) 
 , mVMStart(MAP_FAILED) 
 , mManagesContexts(false) 
 , mBinderContextCheckFunc(NULL) 
 , mBinderContextUserData(NULL) 
 , mThreadPoolStarted(false) 
 , mThreadPoolSeq(1) 
{ 
 if (mDriverFD >= 0) { 
  // XXX Ideally, there should be a specific define for whether we 
  // have mmap (or whether we could possibly have the kernel module 
  // availabla). 
#if !defined(HAVE_WIN32_IPC) 
  // mmap the binder, providing a chunk of virtual address space to receive transactions. 
  mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); 
  if (mVMStart == MAP_FAILED) { 
   // *sigh* 
   LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); 
   close(mDriverFD); 
   mDriverFD = -1; 
  } 
#else 
  mDriverFD = -1; 
#endif 
 } 
 if (mDriverFD < 0) { 
  // Need to run without the driver, starting our own thread pool. 
 } 
} 

        這個函數(shù)有兩個關(guān)鍵地方,一是通過open_driver函數(shù)打開Binder設備文件/dev/binder,并將打開設備文件描述符保存在成員變量mDriverFD中;二是通過mmap來把設備文件/dev/binder映射到內(nèi)存中。

        先看open_driver函數(shù)的實現(xiàn),這個函數(shù)同樣位于frameworks/base/libs/binder/ProcessState.cpp文件中:

static int open_driver() 
{ 
 if (gSingleProcess) { 
  return -1; 
 } 
 
 int fd = open("/dev/binder", O_RDWR); 
 if (fd >= 0) { 
  fcntl(fd, F_SETFD, FD_CLOEXEC); 
  int vers; 
#if defined(HAVE_ANDROID_OS) 
  status_t result = ioctl(fd, BINDER_VERSION, &vers); 
#else 
  status_t result = -1; 
  errno = EPERM; 
#endif 
  if (result == -1) { 
   LOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); 
   close(fd); 
   fd = -1; 
  } 
  if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { 
   LOGE("Binder driver protocol does not match user space protocol!"); 
   close(fd); 
   fd = -1; 
  } 
#if defined(HAVE_ANDROID_OS) 
  size_t maxThreads = 15; 
  result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); 
  if (result == -1) { 
   LOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); 
  } 
#endif 
   
 } else { 
  LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); 
 } 
 return fd; 
} 

        這個函數(shù)的作用主要是通過open文件操作函數(shù)來打開/dev/binder設備文件,然后再調(diào)用ioctl文件控制函數(shù)來分別執(zhí)行BINDER_VERSION和BINDER_SET_MAX_THREADS兩個命令來和Binder驅(qū)動程序進行交互,前者用于獲得當前Binder驅(qū)動程序的版本號,后者用于通知Binder驅(qū)動程序,MediaPlayerService最多可同時啟動15個線程來處理Client端的請求。

        open在Binder驅(qū)動程序中的具體實現(xiàn),請參考前面一篇文章淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路,這里不再重復描述。打開/dev/binder設備文件后,Binder驅(qū)動程序就為MediaPlayerService進程創(chuàng)建了一個struct binder_proc結(jié)構(gòu)體實例來維護MediaPlayerService進程上下文相關(guān)信息。

        我們來看一下ioctl文件操作函數(shù)執(zhí)行BINDER_VERSION命令的過程:

                        status_t result = ioctl(fd, BINDER_VERSION, &vers);  

        這個函數(shù)調(diào)用最終進入到Binder驅(qū)動程序的binder_ioctl函數(shù)中,我們只關(guān)注BINDER_VERSION相關(guān)的部分邏輯:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
{ 
 int ret; 
 struct binder_proc *proc = filp->private_data; 
 struct binder_thread *thread; 
 unsigned int size = _IOC_SIZE(cmd); 
 void __user *ubuf = (void __user *)arg; 
 
 /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ 
 
 ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); 
 if (ret) 
  return ret; 
 
 mutex_lock(&binder_lock); 
 thread = binder_get_thread(proc); 
 if (thread == NULL) { 
  ret = -ENOMEM; 
  goto err; 
 } 
 
 switch (cmd) { 
 ...... 
 case BINDER_VERSION: 
  if (size != sizeof(struct binder_version)) { 
   ret = -EINVAL; 
   goto err; 
  } 
  if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->protocol_version)) { 
   ret = -EINVAL; 
   goto err; 
  } 
  break; 
 ...... 
 } 
 ret = 0; 
err: 
  ...... 
 return ret; 
} 

        很簡單,只是將BINDER_CURRENT_PROTOCOL_VERSION寫入到傳入的參數(shù)arg指向的用戶緩沖區(qū)中去就返回了。BINDER_CURRENT_PROTOCOL_VERSION是一個宏,定義在kernel/common/drivers/staging/android/binder.h文件中:

                     /* This is the current protocol version. */ 
             #define BINDER_CURRENT_PROTOCOL_VERSION 7  

       這里為什么要把ubuf轉(zhuǎn)換成struct binder_version之后,再通過其protocol_version成員變量再來寫入呢,轉(zhuǎn)了一圈,最終內(nèi)容還是寫入到ubuf中。我們看一下struct binder_version的定義就會明白,同樣是在kernel/common/drivers/staging/android/binder.h文件中:

/* Use with BINDER_VERSION, driver fills in fields. */ 
struct binder_version { 
 /* driver protocol version -- increment with incompatible change */ 
 signed long protocol_version; 
}; 

         從注釋中可以看出來,這里是考慮到兼容性,因為以后很有可能不是用signed long來表示版本號。

        這里有一個重要的地方要注意的是,由于這里是打開設備文件/dev/binder之后,第一次進入到binder_ioctl函數(shù),因此,這里調(diào)用binder_get_thread的時候,就會為當前線程創(chuàng)建一個struct binder_thread結(jié)構(gòu)體變量來維護線程上下文信息,具體可以參考淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路一文。

        接著我們再來看一下ioctl文件操作函數(shù)執(zhí)行BINDER_SET_MAX_THREADS命令的過程:

                   result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);  

        這個函數(shù)調(diào)用最終進入到Binder驅(qū)動程序的binder_ioctl函數(shù)中,我們只關(guān)注BINDER_SET_MAX_THREADS相關(guān)的部分邏輯:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
{ 
 int ret; 
 struct binder_proc *proc = filp->private_data; 
 struct binder_thread *thread; 
 unsigned int size = _IOC_SIZE(cmd); 
 void __user *ubuf = (void __user *)arg; 
 
 /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ 
 
 ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); 
 if (ret) 
  return ret; 
 
 mutex_lock(&binder_lock); 
 thread = binder_get_thread(proc); 
 if (thread == NULL) { 
  ret = -ENOMEM; 
  goto err; 
 } 
 
 switch (cmd) { 
 ...... 
 case BINDER_SET_MAX_THREADS: 
  if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { 
   ret = -EINVAL; 
   goto err; 
  } 
  break; 
 ...... 
 } 
 ret = 0; 
err: 
 ...... 
 return ret; 
} 

        這里實現(xiàn)也是非常簡單,只是簡單地把用戶傳進來的參數(shù)保存在proc->max_threads中就完畢了。注意,這里再調(diào)用binder_get_thread函數(shù)的時候,就可以在proc->threads中找到當前線程對應的struct binder_thread結(jié)構(gòu)了,因為前面已經(jīng)創(chuàng)建好并保存在proc->threads紅黑樹中。

        回到ProcessState的構(gòu)造函數(shù)中,這里還通過mmap函數(shù)來把設備文件/dev/binder映射到內(nèi)存中,這個函數(shù)在淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路一文也已經(jīng)有詳細介紹,這里不再重復描述。宏BINDER_VM_SIZE就定義在ProcessState.cpp文件中:

             #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))  

        mmap函數(shù)調(diào)用完成之后,Binder驅(qū)動程序就為當前進程預留了BINDER_VM_SIZE大小的內(nèi)存空間了。

        這樣,ProcessState全局唯一變量gProcess就創(chuàng)建完畢了,回到frameworks/base/media/mediaserver/main_mediaserver.cpp文件中的main函數(shù),下一步是調(diào)用defaultServiceManager函數(shù)來獲得Service Manager的遠程接口,這個已經(jīng)在上一篇文章淺談Android系統(tǒng)進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路有詳細描述,讀者可以回過頭去參考一下。

        再接下來,就進入到MediaPlayerService::instantiate函數(shù)把MediaPlayerService添加到Service Manger中去了。這個函數(shù)定義在frameworks/base/media/libmediaplayerservice/MediaPlayerService.cpp文件中:

void MediaPlayerService::instantiate() { 
 defaultServiceManager()->addService( 
   String16("media.player"), new MediaPlayerService()); 
} 

        我們重點看一下IServiceManger::addService的過程,這有助于我們加深對Binder機制的理解。

        在上一篇文章淺談Android系統(tǒng)進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路中說到,defaultServiceManager返回的實際是一個BpServiceManger類實例,因此,我們看一下BpServiceManger::addService的實現(xiàn),這個函數(shù)實現(xiàn)在frameworks/base/libs/binder/IServiceManager.cpp文件中:

class BpServiceManager : public BpInterface<IServiceManager> 
{ 
public: 
 BpServiceManager(const sp<IBinder>& impl) 
  : BpInterface<IServiceManager>(impl) 
 { 
 } 
 
 ...... 
 
 virtual status_t addService(const String16& name, const sp<IBinder>& service) 
 { 
  Parcel data, reply; 
  data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); 
  data.writeString16(name); 
  data.writeStrongBinder(service); 
  status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); 
  return err == NO_ERROR ? reply.readExceptionCode() 
 } 
 
 ...... 
 
}; 

         這里的Parcel類是用來于序列化進程間通信數(shù)據(jù)用的。
         先來看這一句的調(diào)用:

           data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());  

         IServiceManager::getInterfaceDescriptor()返回來的是一個字符串,即"android.os.IServiceManager",具體可以參考IServiceManger的實現(xiàn)。我們看一下Parcel::writeInterfaceToken的實現(xiàn),位于frameworks/base/libs/binder/Parcel.cpp文件中:

// Write RPC headers. (previously just the interface token) 
status_t Parcel::writeInterfaceToken(const String16& interface) 
{ 
 writeInt32(IPCThreadState::self()->getStrictModePolicy() | 
    STRICT_MODE_PENALTY_GATHER); 
 // currently the interface identification token is just its name as a string 
 return writeString16(interface); 
} 

         它的作用是寫入一個整數(shù)和一個字符串到Parcel中去。

         再來看下面的調(diào)用:

                    data.writeString16(name);  

        這里又是寫入一個字符串到Parcel中去,這里的name即是上面?zhèn)鬟M來的“media.player”字符串。
        往下看:

               data.writeStrongBinder(service);  

        這里定入一個Binder對象到Parcel去。我們重點看一下這個函數(shù)的實現(xiàn),因為它涉及到進程間傳輸Binder實體的問題,比較復雜,需要重點關(guān)注,同時,也是理解Binder機制的一個重點所在。注意,這里的service參數(shù)是一個MediaPlayerService對象。

status_t Parcel::writeStrongBinder(const sp<IBinder>& val) 
{ 
 return flatten_binder(ProcessState::self(), val, this); 
} 

        看到flatten_binder函數(shù),是不是似曾相識的感覺?我們在前面一篇文章淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路中,曾經(jīng)提到在Binder驅(qū)動程序中,使用struct flat_binder_object來表示傳輸中的一個binder對象,它的定義如下所示:

/* 
 * This is the flattened representation of a Binder object for transfer 
 * between processes. The 'offsets' supplied as part of a binder transaction 
 * contains offsets into the data where these structures occur. The Binder 
 * driver takes care of re-writing the structure type and data as it moves 
 * between processes. 
 */ 
struct flat_binder_object { 
 /* 8 bytes for large_flat_header. */ 
 unsigned long  type; 
 unsigned long  flags; 
 
 /* 8 bytes of data. */ 
 union { 
  void  *binder; /* local object */ 
  signed long handle;  /* remote object */ 
 }; 
 
 /* extra data associated with local object */ 
 void   *cookie; 
}; 

        各個成員變量的含義請參考資料Android Binder設計與實現(xiàn)。
        我們進入到flatten_binder函數(shù)看看:

status_t flatten_binder(const sp<ProcessState>& proc, 
 const sp<IBinder>& binder, Parcel* out) 
{ 
 flat_binder_object obj; 
  
 obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; 
 if (binder != NULL) { 
  IBinder *local = binder->localBinder(); 
  if (!local) { 
   BpBinder *proxy = binder->remoteBinder(); 
   if (proxy == NULL) { 
    LOGE("null proxy"); 
   } 
   const int32_t handle = proxy ? proxy->handle() : 0; 
   obj.type = BINDER_TYPE_HANDLE; 
   obj.handle = handle; 
   obj.cookie = NULL; 
  } else { 
   obj.type = BINDER_TYPE_BINDER; 
   obj.binder = local->getWeakRefs(); 
   obj.cookie = local; 
  } 
 } else { 
  obj.type = BINDER_TYPE_BINDER; 
  obj.binder = NULL; 
  obj.cookie = NULL; 
 } 
  
 return finish_flatten_binder(binder, obj, out); 
} 

        首先是初始化flat_binder_object的flags域: 

               obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;  

        0x7f表示處理本Binder實體請求數(shù)據(jù)包的線程的最低優(yōu)先級,F(xiàn)LAT_BINDER_FLAG_ACCEPTS_FDS表示這個Binder實體可以接受文件描述符,Binder實體在收到文件描述符時,就會在本進程中打開這個文件。

       傳進來的binder即為MediaPlayerService::instantiate函數(shù)中new出來的MediaPlayerService實例,因此,不為空。又由于MediaPlayerService繼承自BBinder類,它是一個本地Binder實體,因此binder->localBinder返回一個BBinder指針,而且肯定不為空,于是執(zhí)行下面語句:

obj.type = BINDER_TYPE_BINDER; 
obj.binder = local->getWeakRefs(); 
obj.cookie = local; 

        設置了flat_binder_obj的其他成員變量,注意,指向這個Binder實體地址的指針local保存在flat_binder_obj的成員變量cookie中。

        函數(shù)調(diào)用finish_flatten_binder來將這個flat_binder_obj寫入到Parcel中去:

inline static status_t finish_flatten_binder( 
 const sp<IBinder>& binder, const flat_binder_object& flat, Parcel* out) 
{ 
 return out->writeObject(flat, false); 
} 

       Parcel::writeObject的實現(xiàn)如下:

status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) 
{ 
 const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; 
 const bool enoughObjects = mObjectsSize < mObjectsCapacity; 
 if (enoughData && enoughObjects) { 
restart_write: 
  *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val; 
   
  // Need to write meta-data? 
  if (nullMetaData || val.binder != NULL) { 
   mObjects[mObjectsSize] = mDataPos; 
   acquire_object(ProcessState::self(), val, this); 
   mObjectsSize++; 
  } 
   
  // remember if it's a file descriptor 
  if (val.type == BINDER_TYPE_FD) { 
   mHasFds = mFdsKnown = true; 
  } 
 
  return finishWrite(sizeof(flat_binder_object)); 
 } 
 
 if (!enoughData) { 
  const status_t err = growData(sizeof(val)); 
  if (err != NO_ERROR) return err; 
 } 
 if (!enoughObjects) { 
  size_t newSize = ((mObjectsSize+2)*3)/2; 
  size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t)); 
  if (objects == NULL) return NO_MEMORY; 
  mObjects = objects; 
  mObjectsCapacity = newSize; 
 } 
  
 goto restart_write; 
} 

        這里除了把flat_binder_obj寫到Parcel里面之內(nèi),還要記錄這個flat_binder_obj在Parcel里面的偏移位置:

                    mObjects[mObjectsSize] = mDataPos;  

       這里因為,如果進程間傳輸?shù)臄?shù)據(jù)間帶有Binder對象的時候,Binder驅(qū)動程序需要作進一步的處理,以維護各個Binder實體的一致性,下面我們將會看到Binder驅(qū)動程序是怎么處理這些Binder對象的。

       再回到BpServiceManager::addService函數(shù)中,調(diào)用下面語句:

      status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);  

       回到淺談Android系統(tǒng)進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路一文中的類圖中去看一下,這里的remote成員函數(shù)來自于BpRefBase類,它返回一個BpBinder指針。因此,我們繼續(xù)進入到BpBinder::transact函數(shù)中去看看:

status_t BpBinder::transact( 
 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) 
{ 
 // Once a binder has died, it will never come back to life. 
 if (mAlive) { 
  status_t status = IPCThreadState::self()->transact( 
   mHandle, code, data, reply, flags); 
  if (status == DEAD_OBJECT) mAlive = 0; 
  return status; 
 } 
 
 return DEAD_OBJECT; 
} 

       這里又調(diào)用了IPCThreadState::transact進執(zhí)行實際的操作。注意,這里的mHandle為0,code為ADD_SERVICE_TRANSACTION。ADD_SERVICE_TRANSACTION是上面以參數(shù)形式傳進來的,那mHandle為什么是0呢?因為這里表示的是Service Manager遠程接口,它的句柄值一定是0,具體請參考淺談Android系統(tǒng)進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路一文。

       再進入到IPCThreadState::transact函數(shù),看看做了些什么事情:

status_t IPCThreadState::transact(int32_t handle, 
         uint32_t code, const Parcel& data, 
         Parcel* reply, uint32_t flags) 
{ 
 status_t err = data.errorCheck(); 
 
 flags |= TF_ACCEPT_FDS; 
 
 IF_LOG_TRANSACTIONS() { 
  TextOutput::Bundle _b(alog); 
  alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " 
   << handle << " / code " << TypeCode(code) << ": " 
   << indent << data << dedent << endl; 
 } 
  
 if (err == NO_ERROR) { 
  LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), 
   (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY"); 
  err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); 
 } 
  
 if (err != NO_ERROR) { 
  if (reply) reply->setError(err); 
  return (mLastError = err); 
 } 
  
 if ((flags & TF_ONE_WAY) == 0) { 
  #if 0 
  if (code == 4) { // relayout 
   LOGI(">>>>>> CALLING transaction 4"); 
  } else { 
   LOGI(">>>>>> CALLING transaction %d", code); 
  } 
  #endif 
  if (reply) { 
   err = waitForResponse(reply); 
  } else { 
   Parcel fakeReply; 
   err = waitForResponse(&fakeReply); 
  } 
  #if 0 
  if (code == 4) { // relayout 
   LOGI("<<<<<< RETURNING transaction 4"); 
  } else { 
   LOGI("<<<<<< RETURNING transaction %d", code); 
  } 
  #endif 
   
  IF_LOG_TRANSACTIONS() { 
   TextOutput::Bundle _b(alog); 
   alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand " 
    << handle << ": "; 
   if (reply) alog << indent << *reply << dedent << endl; 
   else alog << "(none requested)" << endl; 
  } 
 } else { 
  err = waitForResponse(NULL, NULL); 
 } 
  
 return err; 
} 

        IPCThreadState::transact函數(shù)的參數(shù)flags是一個默認值為0的參數(shù),上面沒有傳相應的實參進來,因此,這里就為0。

        函數(shù)首先調(diào)用writeTransactionData函數(shù)準備好一個struct binder_transaction_data結(jié)構(gòu)體變量,這個是等一下要傳輸給Binder驅(qū)動程序的。struct binder_transaction_data的定義我們在淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路一文中有詳細描述,讀者不妨回過去讀一下。這里為了方便描述,將struct binder_transaction_data的定義再次列出來:

struct binder_transaction_data { 
 /* The first two are only used for bcTRANSACTION and brTRANSACTION, 
  * identifying the target and contents of the transaction. 
  */ 
 union { 
  size_t handle; /* target descriptor of command transaction */ 
  void *ptr; /* target descriptor of return transaction */ 
 } target; 
 void  *cookie; /* target object cookie */ 
 unsigned int code;  /* transaction command */ 
 
 /* General information about the transaction. */ 
 unsigned int flags; 
 pid_t  sender_pid; 
 uid_t  sender_euid; 
 size_t  data_size; /* number of bytes of data */ 
 size_t  offsets_size; /* number of bytes of offsets */ 
 
 /* If this transaction is inline, the data immediately 
  * follows here; otherwise, it ends with a pointer to 
  * the data buffer. 
  */ 
 union { 
  struct { 
   /* transaction data */ 
   const void *buffer; 
   /* offsets from buffer to flat_binder_object structs */ 
   const void *offsets; 
  } ptr; 
  uint8_t buf[8]; 
 } data; 
}; 
  

         writeTransactionData函數(shù)的實現(xiàn)如下:

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, 
 int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) 
{ 
 binder_transaction_data tr; 
 
 tr.target.handle = handle; 
 tr.code = code; 
 tr.flags = binderFlags; 
  
 const status_t err = data.errorCheck(); 
 if (err == NO_ERROR) { 
  tr.data_size = data.ipcDataSize(); 
  tr.data.ptr.buffer = data.ipcData(); 
  tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t); 
  tr.data.ptr.offsets = data.ipcObjects(); 
 } else if (statusBuffer) { 
  tr.flags |= TF_STATUS_CODE; 
  *statusBuffer = err; 
  tr.data_size = sizeof(status_t); 
  tr.data.ptr.buffer = statusBuffer; 
  tr.offsets_size = 0; 
  tr.data.ptr.offsets = NULL; 
 } else { 
  return (mLastError = err); 
 } 
  
 mOut.writeInt32(cmd); 
 mOut.write(&tr, sizeof(tr)); 
  
 return NO_ERROR; 
} 


  注意,這里的cmd為BC_TRANSACTION。 這個函數(shù)很簡單,在這個場景下,就是執(zhí)行下面語句來初始化本地變量tr:

tr.data_size = data.ipcDataSize(); 
tr.data.ptr.buffer = data.ipcData(); 
tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t); 
tr.data.ptr.offsets = data.ipcObjects(); 

       回憶一下上面的內(nèi)容,寫入到tr.data.ptr.buffer的內(nèi)容相當于下面的內(nèi)容:

writeInt32(IPCThreadState::self()->getStrictModePolicy() | 
    STRICT_MODE_PENALTY_GATHER); 
writeString16("android.os.IServiceManager"); 
writeString16("media.player"); 
writeStrongBinder(new MediaPlayerService()); 

      其中包含了一個Binder實體MediaPlayerService,因此需要設置tr.offsets_size就為1,tr.data.ptr.offsets就指向了這個MediaPlayerService的地址在tr.data.ptr.buffer中的偏移量。最后,將tr的內(nèi)容保存在IPCThreadState的成員變量mOut中。

       回到IPCThreadState::transact函數(shù)中,接下去看,(flags & TF_ONE_WAY) == 0為true,并且reply不為空,所以最終進入到waitForResponse(reply)這條路徑來。我們看一下waitForResponse函數(shù)的實現(xiàn):

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) 
{ 
 int32_t cmd; 
 int32_t err; 
 
 while (1) { 
  if ((err=talkWithDriver()) < NO_ERROR) break; 
  err = mIn.errorCheck(); 
  if (err < NO_ERROR) break; 
  if (mIn.dataAvail() == 0) continue; 
   
  cmd = mIn.readInt32(); 
   
  IF_LOG_COMMANDS() { 
   alog << "Processing waitForResponse Command: " 
    << getReturnString(cmd) << endl; 
  } 
 
  switch (cmd) { 
  case BR_TRANSACTION_COMPLETE: 
   if (!reply && !acquireResult) goto finish; 
   break; 
   
  case BR_DEAD_REPLY: 
   err = DEAD_OBJECT; 
   goto finish; 
 
  case BR_FAILED_REPLY: 
   err = FAILED_TRANSACTION; 
   goto finish; 
   
  case BR_ACQUIRE_RESULT: 
   { 
    LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); 
    const int32_t result = mIn.readInt32(); 
    if (!acquireResult) continue; 
    *acquireResult = result ? NO_ERROR : INVALID_OPERATION; 
   } 
   goto finish; 
   
  case BR_REPLY: 
   { 
    binder_transaction_data tr; 
    err = mIn.read(&tr, sizeof(tr)); 
    LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY"); 
    if (err != NO_ERROR) goto finish; 
 
    if (reply) { 
     if ((tr.flags & TF_STATUS_CODE) == 0) { 
      reply->ipcSetDataReference( 
       reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), 
       tr.data_size, 
       reinterpret_cast<const size_t*>(tr.data.ptr.offsets), 
       tr.offsets_size/sizeof(size_t), 
       freeBuffer, this); 
     } else { 
      err = *static_cast<const status_t*>(tr.data.ptr.buffer); 
      freeBuffer(NULL, 
       reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), 
       tr.data_size, 
       reinterpret_cast<const size_t*>(tr.data.ptr.offsets), 
       tr.offsets_size/sizeof(size_t), this); 
     } 
    } else { 
     freeBuffer(NULL, 
      reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), 
      tr.data_size, 
      reinterpret_cast<const size_t*>(tr.data.ptr.offsets), 
      tr.offsets_size/sizeof(size_t), this); 
     continue; 
    } 
   } 
   goto finish; 
 
  default: 
   err = executeCommand(cmd); 
   if (err != NO_ERROR) goto finish; 
   break; 
  } 
 } 
 
finish: 
 if (err != NO_ERROR) { 
  if (acquireResult) *acquireResult = err; 
  if (reply) reply->setError(err); 
  mLastError = err; 
 } 
  
 return err; 
} 

        這個函數(shù)雖然很長,但是主要調(diào)用了talkWithDriver函數(shù)來與Binder驅(qū)動程序進行交互:

status_t IPCThreadState::talkWithDriver(bool doReceive) 
{ 
 LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened"); 
  
 binder_write_read bwr; 
  
 // Is the read buffer empty? 
 const bool needRead = mIn.dataPosition() >= mIn.dataSize(); 
  
 // We don't want to write anything if we are still reading 
 // from data left in the input buffer and the caller 
 // has requested to read the next data. 
 const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; 
  
 bwr.write_size = outAvail; 
 bwr.write_buffer = (long unsigned int)mOut.data(); 
 
 // This is what we'll read. 
 if (doReceive && needRead) { 
  bwr.read_size = mIn.dataCapacity(); 
  bwr.read_buffer = (long unsigned int)mIn.data(); 
 } else { 
  bwr.read_size = 0; 
 } 
  
 IF_LOG_COMMANDS() { 
  TextOutput::Bundle _b(alog); 
  if (outAvail != 0) { 
   alog << "Sending commands to driver: " << indent; 
   const void* cmds = (const void*)bwr.write_buffer; 
   const void* end = ((const uint8_t*)cmds)+bwr.write_size; 
   alog << HexDump(cmds, bwr.write_size) << endl; 
   while (cmds < end) cmds = printCommand(alog, cmds); 
   alog << dedent; 
  } 
  alog << "Size of receive buffer: " << bwr.read_size 
   << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; 
 } 
  
 // Return immediately if there is nothing to do. 
 if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; 
  
 bwr.write_consumed = 0; 
 bwr.read_consumed = 0; 
 status_t err; 
 do { 
  IF_LOG_COMMANDS() { 
   alog << "About to read/write, write size = " << mOut.dataSize() << endl; 
  } 
#if defined(HAVE_ANDROID_OS) 
  if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) 
   err = NO_ERROR; 
  else 
   err = -errno; 
#else 
  err = INVALID_OPERATION; 
#endif 
  IF_LOG_COMMANDS() { 
   alog << "Finished read/write, write size = " << mOut.dataSize() << endl; 
  } 
 } while (err == -EINTR); 
  
 IF_LOG_COMMANDS() { 
  alog << "Our err: " << (void*)err << ", write consumed: " 
   << bwr.write_consumed << " (of " << mOut.dataSize() 
   << "), read consumed: " << bwr.read_consumed << endl; 
 } 
 
 if (err >= NO_ERROR) { 
  if (bwr.write_consumed > 0) { 
   if (bwr.write_consumed < (ssize_t)mOut.dataSize()) 
    mOut.remove(0, bwr.write_consumed); 
   else 
    mOut.setDataSize(0); 
  } 
  if (bwr.read_consumed > 0) { 
   mIn.setDataSize(bwr.read_consumed); 
   mIn.setDataPosition(0); 
  } 
  IF_LOG_COMMANDS() { 
   TextOutput::Bundle _b(alog); 
   alog << "Remaining data size: " << mOut.dataSize() << endl; 
   alog << "Received commands from driver: " << indent; 
   const void* cmds = mIn.data(); 
   const void* end = mIn.data() + mIn.dataSize(); 
   alog << HexDump(cmds, mIn.dataSize()) << endl; 
   while (cmds < end) cmds = printReturnCommand(alog, cmds); 
   alog << dedent; 
  } 
  return NO_ERROR; 
 } 
  
 return err; 
} 

        這里doReceive和needRead均為1,有興趣的讀者可以自已分析一下。因此,這里告訴Binder驅(qū)動程序,先執(zhí)行write操作,再執(zhí)行read操作,下面我們將會看到。

        最后,通過ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)進行到Binder驅(qū)動程序的binder_ioctl函數(shù),我們只關(guān)注cmd為BINDER_WRITE_READ的邏輯:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
{ 
 int ret; 
 struct binder_proc *proc = filp->private_data; 
 struct binder_thread *thread; 
 unsigned int size = _IOC_SIZE(cmd); 
 void __user *ubuf = (void __user *)arg; 
 
 /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ 
 
 ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); 
 if (ret) 
  return ret; 
 
 mutex_lock(&binder_lock); 
 thread = binder_get_thread(proc); 
 if (thread == NULL) { 
  ret = -ENOMEM; 
  goto err; 
 } 
 
 switch (cmd) { 
 case BINDER_WRITE_READ: { 
  struct binder_write_read bwr; 
  if (size != sizeof(struct binder_write_read)) { 
   ret = -EINVAL; 
   goto err; 
  } 
  if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { 
   ret = -EFAULT; 
   goto err; 
  } 
  if (binder_debug_mask & BINDER_DEBUG_READ_WRITE) 
   printk(KERN_INFO "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n", 
   proc->pid, thread->pid, bwr.write_size, bwr.write_buffer, bwr.read_size, bwr.read_buffer); 
  if (bwr.write_size > 0) { 
   ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed); 
   if (ret < 0) { 
    bwr.read_consumed = 0; 
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) 
     ret = -EFAULT; 
    goto err; 
   } 
  } 
  if (bwr.read_size > 0) { 
   ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); 
   if (!list_empty(&proc->todo)) 
    wake_up_interruptible(&proc->wait); 
   if (ret < 0) { 
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) 
     ret = -EFAULT; 
    goto err; 
   } 
  } 
  if (binder_debug_mask & BINDER_DEBUG_READ_WRITE) 
   printk(KERN_INFO "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n", 
   proc->pid, thread->pid, bwr.write_consumed, bwr.write_size, bwr.read_consumed, bwr.read_size); 
  if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { 
   ret = -EFAULT; 
   goto err; 
  } 
  break; 
 } 
 ...... 
 } 
 ret = 0; 
err: 
 ...... 
 return ret; 
} 

         函數(shù)首先是將用戶傳進來的參數(shù)拷貝到本地變量struct binder_write_read bwr中去。這里bwr.write_size > 0為true,因此,進入到binder_thread_write函數(shù)中,我們只關(guān)注BC_TRANSACTION部分的邏輯:

binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, 
     void __user *buffer, int size, signed long *consumed) 
{ 
 uint32_t cmd; 
 void __user *ptr = buffer + *consumed; 
 void __user *end = buffer + size; 
 
 while (ptr < end && thread->return_error == BR_OK) { 
  if (get_user(cmd, (uint32_t __user *)ptr)) 
   return -EFAULT; 
  ptr += sizeof(uint32_t); 
  if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { 
   binder_stats.bc[_IOC_NR(cmd)]++; 
   proc->stats.bc[_IOC_NR(cmd)]++; 
   thread->stats.bc[_IOC_NR(cmd)]++; 
  } 
  switch (cmd) { 
   ..... 
  case BC_TRANSACTION: 
  case BC_REPLY: { 
   struct binder_transaction_data tr; 
 
   if (copy_from_user(&tr, ptr, sizeof(tr))) 
    return -EFAULT; 
   ptr += sizeof(tr); 
   binder_transaction(proc, thread, &tr, cmd == BC_REPLY); 
   break; 
  } 
  ...... 
  } 
  *consumed = ptr - buffer; 
 } 
 return 0; 
} 

         首先將用戶傳進來的transact參數(shù)拷貝在本地變量struct binder_transaction_data tr中去,接著調(diào)用binder_transaction函數(shù)進一步處理,這里我們忽略掉無關(guān)代碼:

static void 
binder_transaction(struct binder_proc *proc, struct binder_thread *thread, 
struct binder_transaction_data *tr, int reply) 
{ 
 struct binder_transaction *t; 
 struct binder_work *tcomplete; 
 size_t *offp, *off_end; 
 struct binder_proc *target_proc; 
 struct binder_thread *target_thread = NULL; 
 struct binder_node *target_node = NULL; 
 struct list_head *target_list; 
 wait_queue_head_t *target_wait; 
 struct binder_transaction *in_reply_to = NULL; 
 struct binder_transaction_log_entry *e; 
 uint32_t return_error; 
 
  ...... 
 
 if (reply) { 
   ...... 
 } else { 
  if (tr->target.handle) { 
   ...... 
  } else { 
   target_node = binder_context_mgr_node; 
   if (target_node == NULL) { 
    return_error = BR_DEAD_REPLY; 
    goto err_no_context_mgr_node; 
   } 
  } 
  ...... 
  target_proc = target_node->proc; 
  if (target_proc == NULL) { 
   return_error = BR_DEAD_REPLY; 
   goto err_dead_binder; 
  } 
  ...... 
 } 
 if (target_thread) { 
  ...... 
 } else { 
  target_list = &target_proc->todo; 
  target_wait = &target_proc->wait; 
 } 
  
 ...... 
 
 /* TODO: reuse incoming transaction for reply */ 
 t = kzalloc(sizeof(*t), GFP_KERNEL); 
 if (t == NULL) { 
  return_error = BR_FAILED_REPLY; 
  goto err_alloc_t_failed; 
 } 
 ...... 
 
 tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); 
 if (tcomplete == NULL) { 
  return_error = BR_FAILED_REPLY; 
  goto err_alloc_tcomplete_failed; 
 } 
  
 ...... 
 
 if (!reply && !(tr->flags & TF_ONE_WAY)) 
  t->from = thread; 
 else 
  t->from = NULL; 
 t->sender_euid = proc->tsk->cred->euid; 
 t->to_proc = target_proc; 
 t->to_thread = target_thread; 
 t->code = tr->code; 
 t->flags = tr->flags; 
 t->priority = task_nice(current); 
 t->buffer = binder_alloc_buf(target_proc, tr->data_size, 
  tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); 
 if (t->buffer == NULL) { 
  return_error = BR_FAILED_REPLY; 
  goto err_binder_alloc_buf_failed; 
 } 
 t->buffer->allow_user_free = 0; 
 t->buffer->debug_id = t->debug_id; 
 t->buffer->transaction = t; 
 t->buffer->target_node = target_node; 
 if (target_node) 
  binder_inc_node(target_node, 1, 0, NULL); 
 
 offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); 
 
 if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) { 
  ...... 
  return_error = BR_FAILED_REPLY; 
  goto err_copy_data_failed; 
 } 
 if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) { 
  ...... 
  return_error = BR_FAILED_REPLY; 
  goto err_copy_data_failed; 
 } 
 ...... 
 
 off_end = (void *)offp + tr->offsets_size; 
 for (; offp < off_end; offp++) { 
  struct flat_binder_object *fp; 
  ...... 
  fp = (struct flat_binder_object *)(t->buffer->data + *offp); 
  switch (fp->type) { 
  case BINDER_TYPE_BINDER: 
  case BINDER_TYPE_WEAK_BINDER: { 
   struct binder_ref *ref; 
   struct binder_node *node = binder_get_node(proc, fp->binder); 
   if (node == NULL) { 
    node = binder_new_node(proc, fp->binder, fp->cookie); 
    if (node == NULL) { 
     return_error = BR_FAILED_REPLY; 
     goto err_binder_new_node_failed; 
    } 
    node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; 
    node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); 
   } 
   if (fp->cookie != node->cookie) { 
    ...... 
    goto err_binder_get_ref_for_node_failed; 
   } 
   ref = binder_get_ref_for_node(target_proc, node); 
   if (ref == NULL) { 
    return_error = BR_FAILED_REPLY; 
    goto err_binder_get_ref_for_node_failed; 
   } 
   if (fp->type == BINDER_TYPE_BINDER) 
    fp->type = BINDER_TYPE_HANDLE; 
   else 
    fp->type = BINDER_TYPE_WEAK_HANDLE; 
   fp->handle = ref->desc; 
   binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo); 
   ...... 
        
  } break; 
  ...... 
  } 
 } 
 
 if (reply) { 
  ...... 
 } else if (!(t->flags & TF_ONE_WAY)) { 
  BUG_ON(t->buffer->async_transaction != 0); 
  t->need_reply = 1; 
  t->from_parent = thread->transaction_stack; 
  thread->transaction_stack = t; 
 } else { 
  ...... 
 } 
 t->work.type = BINDER_WORK_TRANSACTION; 
 list_add_tail(&t->work.entry, target_list); 
 tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; 
 list_add_tail(&tcomplete->entry, &thread->todo); 
 if (target_wait) 
  wake_up_interruptible(target_wait); 
 return; 
 ...... 
} 

       注意,這里傳進來的參數(shù)reply為0,tr->target.handle也為0。因此,target_proc、target_thread、target_node、target_list和target_wait的值分別為:

target_node = binder_context_mgr_node; 
target_proc = target_node->proc; 
target_list = &target_proc->todo; 
target_wait = &target_proc->wait; 

       接著,分配了一個待處理事務t和一個待完成工作項tcomplete,并執(zhí)行初始化工作:

/* TODO: reuse incoming transaction for reply */ 
t = kzalloc(sizeof(*t), GFP_KERNEL); 
if (t == NULL) { 
 return_error = BR_FAILED_REPLY; 
 goto err_alloc_t_failed; 
} 
...... 
 
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); 
if (tcomplete == NULL) { 
 return_error = BR_FAILED_REPLY; 
 goto err_alloc_tcomplete_failed; 
} 
 
...... 
 
if (!reply && !(tr->flags & TF_ONE_WAY)) 
 t->from = thread; 
else 
 t->from = NULL; 
t->sender_euid = proc->tsk->cred->euid; 
t->to_proc = target_proc; 
t->to_thread = target_thread; 
t->code = tr->code; 
t->flags = tr->flags; 
t->priority = task_nice(current); 
t->buffer = binder_alloc_buf(target_proc, tr->data_size, 
 tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); 
if (t->buffer == NULL) { 
 return_error = BR_FAILED_REPLY; 
 goto err_binder_alloc_buf_failed; 
} 
t->buffer->allow_user_free = 0; 
t->buffer->debug_id = t->debug_id; 
t->buffer->transaction = t; 
t->buffer->target_node = target_node; 
if (target_node) 
 binder_inc_node(target_node, 1, 0, NULL); 
 
offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); 
 
if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) { 
 ...... 
 return_error = BR_FAILED_REPLY; 
 goto err_copy_data_failed; 
} 
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) { 
 ...... 
 return_error = BR_FAILED_REPLY; 
 goto err_copy_data_failed; 
} 

         注意,這里的事務t是要交給target_proc處理的,在這個場景之下,就是Service Manager了。因此,下面的語句:

t->buffer = binder_alloc_buf(target_proc, tr->data_size, 
  tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); 

         就是在Service Manager的進程空間中分配一塊內(nèi)存來保存用戶傳進入的參數(shù)了:

if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) { 
 ...... 
 return_error = BR_FAILED_REPLY; 
 goto err_copy_data_failed; 
} 
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) { 
 ...... 
 return_error = BR_FAILED_REPLY; 
 goto err_copy_data_failed; 
} 

         由于現(xiàn)在target_node要被使用了,增加它的引用計數(shù):

if (target_node) 
  binder_inc_node(target_node, 1, 0, NULL); 

        接下去的for循環(huán),就是用來處理傳輸數(shù)據(jù)中的Binder對象了。在我們的場景中,有一個類型為BINDER_TYPE_BINDER的Binder實體MediaPlayerService:

 switch (fp->type) { 
 case BINDER_TYPE_BINDER: 
 case BINDER_TYPE_WEAK_BINDER: { 
struct binder_ref *ref; 
struct binder_node *node = binder_get_node(proc, fp->binder); 
if (node == NULL) { 
 node = binder_new_node(proc, fp->binder, fp->cookie); 
 if (node == NULL) { 
  return_error = BR_FAILED_REPLY; 
  goto err_binder_new_node_failed; 
 } 
 node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; 
 node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); 
} 
if (fp->cookie != node->cookie) { 
 ...... 
 goto err_binder_get_ref_for_node_failed; 
} 
ref = binder_get_ref_for_node(target_proc, node); 
if (ref == NULL) { 
 return_error = BR_FAILED_REPLY; 
 goto err_binder_get_ref_for_node_failed; 
} 
if (fp->type == BINDER_TYPE_BINDER) 
 fp->type = BINDER_TYPE_HANDLE; 
else 
 fp->type = BINDER_TYPE_WEAK_HANDLE; 
fp->handle = ref->desc; 
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo); 
...... 
       
} break; 

        由于是第一次在Binder驅(qū)動程序中傳輸這個MediaPlayerService,調(diào)用binder_get_node函數(shù)查詢這個Binder實體時,會返回空,于是binder_new_node在proc中新建一個,下次就可以直接使用了。

        現(xiàn)在,由于要把這個Binder實體MediaPlayerService交給target_proc,也就是Service Manager來管理,也就是說Service Manager要引用這個MediaPlayerService了,于是通過binder_get_ref_for_node為MediaPlayerService創(chuàng)建一個引用,并且通過binder_inc_ref來增加這個引用計數(shù),防止這個引用還在使用過程當中就被銷毀。注意,到了這里的時候,t->buffer中的flat_binder_obj的type已經(jīng)改為BINDER_TYPE_HANDLE,handle已經(jīng)改為ref->desc,跟原來不一樣了,因為這個flat_binder_obj是最終是要傳給Service Manager的,而Service Manager只能夠通過句柄值來引用這個Binder實體。

        最后,把待處理事務加入到target_list列表中去:

                 list_add_tail(&t->work.entry, target_list);  

        并且把待完成工作項加入到本線程的todo等待執(zhí)行列表中去:

                    list_add_tail(&tcomplete->entry, &thread->todo);  

        現(xiàn)在目標進程有事情可做了,于是喚醒它:

             if (target_wait)  
                          wake_up_interruptible(target_wait);   

       這里就是要喚醒Service Manager進程了。回憶一下前面這篇文章,此時, Service Manager正在binder_t淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路hread_read函數(shù)中調(diào)用wait_event_interruptible進入休眠狀態(tài)。

       這里我們先忽略一下Service Manager被喚醒之后的場景,繼續(xù)MedaPlayerService的啟動過程,然后再回來。

       回到binder_ioctl函數(shù),bwr.read_size > 0為true,于是進入binder_thread_read函數(shù):

static int 
binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, 
     void __user *buffer, int size, signed long *consumed, int non_block) 
{ 
 void __user *ptr = buffer + *consumed; 
 void __user *end = buffer + size; 
 
 int ret = 0; 
 int wait_for_proc_work; 
 
 if (*consumed == 0) { 
  if (put_user(BR_NOOP, (uint32_t __user *)ptr)) 
   return -EFAULT; 
  ptr += sizeof(uint32_t); 
 } 
 
retry: 
 wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); 
  
 ....... 
 
 if (wait_for_proc_work) { 
  ....... 
 } else { 
  if (non_block) { 
   if (!binder_has_thread_work(thread)) 
    ret = -EAGAIN; 
  } else 
   ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread)); 
 } 
  
 ...... 
 
 while (1) { 
  uint32_t cmd; 
  struct binder_transaction_data tr; 
  struct binder_work *w; 
  struct binder_transaction *t = NULL; 
 
  if (!list_empty(&thread->todo)) 
   w = list_first_entry(&thread->todo, struct binder_work, entry); 
  else if (!list_empty(&proc->todo) && wait_for_proc_work) 
   w = list_first_entry(&proc->todo, struct binder_work, entry); 
  else { 
   if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */ 
    goto retry; 
   break; 
  } 
 
  if (end - ptr < sizeof(tr) + 4) 
   break; 
 
  switch (w->type) { 
  ...... 
  case BINDER_WORK_TRANSACTION_COMPLETE: { 
   cmd = BR_TRANSACTION_COMPLETE; 
   if (put_user(cmd, (uint32_t __user *)ptr)) 
    return -EFAULT; 
   ptr += sizeof(uint32_t); 
 
   binder_stat_br(proc, thread, cmd); 
   if (binder_debug_mask & BINDER_DEBUG_TRANSACTION_COMPLETE) 
    printk(KERN_INFO "binder: %d:%d BR_TRANSACTION_COMPLETE\n", 
    proc->pid, thread->pid); 
 
   list_del(&w->entry); 
   kfree(w); 
   binder_stats.obj_deleted[BINDER_STAT_TRANSACTION_COMPLETE]++; 
            } break; 
  ...... 
  } 
 
  if (!t) 
   continue; 
 
  ...... 
 } 
 
done: 
 ...... 
 return 0; 
} 

        這里,thread->transaction_stack和thread->todo均不為空,于是wait_for_proc_work為false,由于binder_has_thread_work的時候,返回true,這里因為thread->todo不為空,因此,線程雖然調(diào)用了wait_event_interruptible,但是不會睡眠,于是繼續(xù)往下執(zhí)行。

        由于thread->todo不為空,執(zhí)行下列語句:

if (!list_empty(&thread->todo)) 
  w = list_first_entry(&thread->todo, struct binder_work, entry); 

        w->type為BINDER_WORK_TRANSACTION_COMPLETE,這是在上面的binder_transaction函數(shù)設置的,于是執(zhí)行:

 switch (w->type) { 
 ...... 
 case BINDER_WORK_TRANSACTION_COMPLETE: { 
cmd = BR_TRANSACTION_COMPLETE; 
if (put_user(cmd, (uint32_t __user *)ptr)) 
 return -EFAULT; 
ptr += sizeof(uint32_t); 
 
  ...... 
list_del(&w->entry); 
kfree(w); 
   
} break; 
...... 
 } 

        這里就將w從thread->todo刪除了。由于這里t為空,重新執(zhí)行while循環(huán),這時由于已經(jīng)沒有事情可做了,最后就返回到binder_ioctl函數(shù)中。注間,這里一共往用戶傳進來的緩沖區(qū)buffer寫入了兩個整數(shù),分別是BR_NOOP和BR_TRANSACTION_COMPLETE。

        binder_ioctl函數(shù)返回到用戶空間之前,把數(shù)據(jù)消耗情況拷貝回用戶空間中:

if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { 
 ret = -EFAULT; 
 goto err; 
} 

        最后返回到IPCThreadState::talkWithDriver函數(shù)中,執(zhí)行下面語句: 

 if (err >= NO_ERROR) { 
  if (bwr.write_consumed > 0) { 
   if (bwr.write_consumed < (ssize_t)mOut.dataSize()) 
    mOut.remove(0, bwr.write_consumed); 
   else 
    mOut.setDataSize(0); 
  } 
  if (bwr.read_consumed > 0) { 
<pre code_snippet_id="134056" snippet_file_name="blog_20131230_54_6706870" name="code" class="cpp">   mIn.setDataSize(bwr.read_consumed); 
   mIn.setDataPosition(0);</pre>  }  ......  return NO_ERROR; } 

        首先是把mOut的數(shù)據(jù)清空:
                          mOut.setDataSize(0);  

       然后設置已經(jīng)讀取的內(nèi)容的大小:

                          mIn.setDataSize(bwr.read_consumed);  
                          mIn.setDataPosition(0);
  

        然后返回到IPCThreadState::waitForResponse函數(shù)中。在IPCThreadState::waitForResponse函數(shù),先是從mIn讀出一個整數(shù),這個便是BR_NOOP了,這是一個空操作,什么也不做。然后繼續(xù)進入IPCThreadState::talkWithDriver函數(shù)中。

        這時候,下面語句執(zhí)行后:

                       const bool needRead = mIn.dataPosition() >= mIn.dataSize();  

        needRead為false,因為在mIn中,尚有一個整數(shù)BR_TRANSACTION_COMPLETE未讀出。

       這時候,下面語句執(zhí)行后:

                       const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;  

        outAvail等于0。因此,最后bwr.write_size和bwr.read_size均為0,IPCThreadState::talkWithDriver函數(shù)什么也不做,直接返回到IPCThreadState::waitForResponse函數(shù)中。在IPCThreadState::waitForResponse函數(shù),又繼續(xù)從mIn讀出一個整數(shù),這個便是BR_TRANSACTION_COMPLETE:

switch (cmd) { 
case BR_TRANSACTION_COMPLETE: 
  if (!reply && !acquireResult) goto finish; 
  break; 
...... 
} 

        reply不為NULL,因此,IPCThreadState::waitForResponse的循環(huán)沒有結(jié)束,繼續(xù)執(zhí)行,又進入到IPCThreadState::talkWithDrive中。

        這次,needRead就為true了,而outAvail仍為0,所以bwr.read_size不為0,bwr.write_size為0。于是通過:

                       ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)  

        進入到Binder驅(qū)動程序中的binder_ioctl函數(shù)中。由于bwr.write_size為0,bwr.read_size不為0,這次直接就進入到binder_thread_read函數(shù)中。這時候,thread->transaction_stack不等于0,但是thread->todo為空,于是線程就通過:
[cpp] view plain copy 在CODE上查看代碼片派生到我的代碼片
wait_event_interruptible(thread->wait, binder_has_thread_work(thread));  

        進入睡眠狀態(tài),等待Service Manager來喚醒了。

        現(xiàn)在,我們可以回到Service Manager被喚醒的過程了。我們接著前面淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路這篇文章的最后,繼續(xù)描述。此時, Service Manager正在binder_thread_read函數(shù)中調(diào)用wait_event_interruptible_exclusive進入休眠狀態(tài)。上面被MediaPlayerService啟動后進程喚醒后,繼續(xù)執(zhí)行binder_thread_read函數(shù):

static int 
binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, 
     void __user *buffer, int size, signed long *consumed, int non_block) 
{ 
 void __user *ptr = buffer + *consumed; 
 void __user *end = buffer + size; 
 
 int ret = 0; 
 int wait_for_proc_work; 
 
 if (*consumed == 0) { 
  if (put_user(BR_NOOP, (uint32_t __user *)ptr)) 
   return -EFAULT; 
  ptr += sizeof(uint32_t); 
 } 
 
retry: 
 wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); 
 
 ...... 
 
 if (wait_for_proc_work) { 
  ...... 
  if (non_block) { 
   if (!binder_has_proc_work(proc, thread)) 
    ret = -EAGAIN; 
  } else 
   ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread)); 
 } else { 
  ...... 
 } 
  
 ...... 
 
 while (1) { 
  uint32_t cmd; 
  struct binder_transaction_data tr; 
  struct binder_work *w; 
  struct binder_transaction *t = NULL; 
 
  if (!list_empty(&thread->todo)) 
   w = list_first_entry(&thread->todo, struct binder_work, entry); 
  else if (!list_empty(&proc->todo) && wait_for_proc_work) 
   w = list_first_entry(&proc->todo, struct binder_work, entry); 
  else { 
   if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */ 
    goto retry; 
   break; 
  } 
 
  if (end - ptr < sizeof(tr) + 4) 
   break; 
 
  switch (w->type) { 
  case BINDER_WORK_TRANSACTION: { 
   t = container_of(w, struct binder_transaction, work); 
          } break; 
  ...... 
  } 
 
  if (!t) 
   continue; 
 
  BUG_ON(t->buffer == NULL); 
  if (t->buffer->target_node) { 
   struct binder_node *target_node = t->buffer->target_node; 
   tr.target.ptr = target_node->ptr; 
   tr.cookie = target_node->cookie; 
   ...... 
   cmd = BR_TRANSACTION; 
  } else { 
   ...... 
  } 
  tr.code = t->code; 
  tr.flags = t->flags; 
  tr.sender_euid = t->sender_euid; 
 
  if (t->from) { 
   struct task_struct *sender = t->from->proc->tsk; 
   tr.sender_pid = task_tgid_nr_ns(sender, current->nsproxy->pid_ns); 
  } else { 
   tr.sender_pid = 0; 
  } 
 
  tr.data_size = t->buffer->data_size; 
  tr.offsets_size = t->buffer->offsets_size; 
  tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset; 
  tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *)); 
 
  if (put_user(cmd, (uint32_t __user *)ptr)) 
   return -EFAULT; 
  ptr += sizeof(uint32_t); 
  if (copy_to_user(ptr, &tr, sizeof(tr))) 
   return -EFAULT; 
  ptr += sizeof(tr); 
 
  ...... 
 
  list_del(&t->work.entry); 
  t->buffer->allow_user_free = 1; 
  if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { 
   t->to_parent = thread->transaction_stack; 
   t->to_thread = thread; 
   thread->transaction_stack = t; 
  } else { 
   t->buffer->transaction = NULL; 
   kfree(t); 
   binder_stats.obj_deleted[BINDER_STAT_TRANSACTION]++; 
  } 
  break; 
 } 
 
done: 
 
 ...... 
 return 0; 
} 

        Service Manager被喚醒之后,就進入while循環(huán)開始處理事務了。這里wait_for_proc_work等于1,并且proc->todo不為空,所以從proc->todo列表中得到第一個工作項:

                   w = list_first_entry(&proc->todo, struct binder_work, entry);  

        從上面的描述中,我們知道,這個工作項的類型為BINDER_WORK_TRANSACTION,于是通過下面語句得到事務項:

                    t = container_of(w, struct binder_transaction, work);  

       接著就是把事務項t中的數(shù)據(jù)拷貝到本地局部變量struct binder_transaction_data tr中去了: 

if (t->buffer->target_node) { 
 struct binder_node *target_node = t->buffer->target_node; 
 tr.target.ptr = target_node->ptr; 
 tr.cookie = target_node->cookie; 
 ...... 
 cmd = BR_TRANSACTION; 
} else { 
 ...... 
} 
tr.code = t->code; 
tr.flags = t->flags; 
tr.sender_euid = t->sender_euid; 
 
if (t->from) { 
 struct task_struct *sender = t->from->proc->tsk; 
 tr.sender_pid = task_tgid_nr_ns(sender, current->nsproxy->pid_ns); 
} else { 
 tr.sender_pid = 0; 
} 
 
tr.data_size = t->buffer->data_size; 
tr.offsets_size = t->buffer->offsets_size; 
tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset; 
tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *)); 

        這里有一個非常重要的地方,是Binder進程間通信機制的精髓所在:

tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset; 
tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *)); 


        t->buffer->data所指向的地址是內(nèi)核空間的,現(xiàn)在要把數(shù)據(jù)返回給Service Manager進程的用戶空間,而Service Manager進程的用戶空間是不能訪問內(nèi)核空間的數(shù)據(jù)的,所以這里要作一下處理。怎么處理呢?我們在學面向?qū)ο笳Z言的時候,對象的拷貝有深拷貝和淺拷貝之分,深拷貝是把另外分配一塊新內(nèi)存,然后把原始對象的內(nèi)容搬過去,淺拷貝是并沒有為新對象分配一塊新空間,而只是分配一個引用,而個引用指向原始對象。Binder機制用的是類似淺拷貝的方法,通過在用戶空間分配一個虛擬地址,然后讓這個用戶空間虛擬地址與 t->buffer->data這個內(nèi)核空間虛擬地址指向同一個物理地址,這樣就可以實現(xiàn)淺拷貝了。怎么樣用戶空間和內(nèi)核空間的虛擬地址同時指向同一個物理地址呢?請參考前面一篇文章淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路,那里有詳細描述。這里只要將t->buffer->data加上一個偏移值proc->user_buffer_offset就可以得到t->buffer->data對應的用戶空間虛擬地址了。調(diào)整了tr.data.ptr.buffer的值之后,不要忘記也要一起調(diào)整tr.data.ptr.offsets的值。
 

       接著就是把tr的內(nèi)容拷貝到用戶傳進來的緩沖區(qū)去了,指針ptr指向這個用戶緩沖區(qū)的地址:

if (put_user(cmd, (uint32_t __user *)ptr)) 
 return -EFAULT; 
ptr += sizeof(uint32_t); 
if (copy_to_user(ptr, &tr, sizeof(tr))) 
 return -EFAULT; 
ptr += sizeof(tr); 

         這里可以看出,這里只是對作tr.data.ptr.bufferr和tr.data.ptr.offsets的內(nèi)容作了淺拷貝。

         最后,由于已經(jīng)處理了這個事務,要把它從todo列表中刪除:

list_del(&t->work.entry); 
t->buffer->allow_user_free = 1; 
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { 
 t->to_parent = thread->transaction_stack; 
 t->to_thread = thread; 
 thread->transaction_stack = t; 
} else { 
 t->buffer->transaction = NULL; 
 kfree(t); 
 binder_stats.obj_deleted[BINDER_STAT_TRANSACTION]++; 
} 

         注意,這里的cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)為true,表明這個事務雖然在驅(qū)動程序中已經(jīng)處理完了,但是它仍然要等待Service Manager完成之后,給驅(qū)動程序一個確認,也就是需要等待回復,于是把當前事務t放在thread->transaction_stack隊列的頭部:

t->to_parent = thread->transaction_stack; 
t->to_thread = thread; 
thread->transaction_stack = t; 

         如果cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)為false,那就不需要等待回復了,直接把事務t刪掉。

         這個while最后通過一個break跳了出來,最后返回到binder_ioctl函數(shù)中:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
{ 
 int ret; 
 struct binder_proc *proc = filp->private_data; 
 struct binder_thread *thread; 
 unsigned int size = _IOC_SIZE(cmd); 
 void __user *ubuf = (void __user *)arg; 
 
 ...... 
 
 switch (cmd) { 
 case BINDER_WRITE_READ: { 
  struct binder_write_read bwr; 
  if (size != sizeof(struct binder_write_read)) { 
   ret = -EINVAL; 
   goto err; 
  } 
  if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { 
   ret = -EFAULT; 
   goto err; 
  } 
  ...... 
  if (bwr.read_size > 0) { 
   ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); 
   if (!list_empty(&proc->todo)) 
    wake_up_interruptible(&proc->wait); 
   if (ret < 0) { 
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) 
     ret = -EFAULT; 
    goto err; 
   } 
  } 
  ...... 
  if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { 
   ret = -EFAULT; 
   goto err; 
  } 
  break; 
  } 
 ...... 
 default: 
  ret = -EINVAL; 
  goto err; 
 } 
 ret = 0; 
err: 
 ...... 
 return ret; 
} 

         從binder_thread_read返回來后,再看看proc->todo是否還有事務等待處理,如果是,就把睡眠在proc->wait隊列的線程喚醒來處理。最后,把本地變量struct binder_write_read bwr的內(nèi)容拷貝回到用戶傳進來的緩沖區(qū)中,就返回了。

        這里就是返回到frameworks/base/cmds/servicemanager/binder.c文件中的binder_loop函數(shù)了:

void binder_loop(struct binder_state *bs, binder_handler func) 
{ 
 int res; 
 struct binder_write_read bwr; 
 unsigned readbuf[32]; 
 
 bwr.write_size = 0; 
 bwr.write_consumed = 0; 
 bwr.write_buffer = 0; 
  
 readbuf[0] = BC_ENTER_LOOPER; 
 binder_write(bs, readbuf, sizeof(unsigned)); 
 
 for (;;) { 
  bwr.read_size = sizeof(readbuf); 
  bwr.read_consumed = 0; 
  bwr.read_buffer = (unsigned) readbuf; 
 
  res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); 
 
  if (res < 0) { 
   LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno)); 
   break; 
  } 
 
  res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func); 
  if (res == 0) { 
   LOGE("binder_loop: unexpected reply?!\n"); 
   break; 
  } 
  if (res < 0) { 
   LOGE("binder_loop: io error %d %s\n", res, strerror(errno)); 
   break; 
  } 
 } 
} 

       返回來的數(shù)據(jù)都放在readbuf中,接著調(diào)用binder_parse進行解析:

int binder_parse(struct binder_state *bs, struct binder_io *bio, 
     uint32_t *ptr, uint32_t size, binder_handler func) 
{ 
 int r = 1; 
 uint32_t *end = ptr + (size / 4); 
 
 while (ptr < end) { 
  uint32_t cmd = *ptr++; 
  ...... 
  case BR_TRANSACTION: { 
   struct binder_txn *txn = (void *) ptr; 
   if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) { 
    LOGE("parse: txn too small!\n"); 
    return -1; 
   } 
   binder_dump_txn(txn); 
   if (func) { 
    unsigned rdata[256/4]; 
    struct binder_io msg; 
    struct binder_io reply; 
    int res; 
 
    bio_init(&reply, rdata, sizeof(rdata), 4); 
    bio_init_from_txn(&msg, txn); 
    res = func(bs, txn, &msg, &reply); 
    binder_send_reply(bs, &reply, txn->data, res); 
   } 
   ptr += sizeof(*txn) / sizeof(uint32_t); 
   break; 
        } 
  ...... 
  default: 
   LOGE("parse: OOPS %d\n", cmd); 
   return -1; 
  } 
 } 
 
 return r; 
} 

        首先把從Binder驅(qū)動程序讀出來的數(shù)據(jù)轉(zhuǎn)換為一個struct binder_txn結(jié)構(gòu)體,保存在txn本地變量中,struct binder_txn定義在frameworks/base/cmds/servicemanager/binder.h文件中:

struct binder_txn 
{ 
 void *target; 
 void *cookie; 
 uint32_t code; 
 uint32_t flags; 
 
 uint32_t sender_pid; 
 uint32_t sender_euid; 
 
 uint32_t data_size; 
 uint32_t offs_size; 
 void *data; 
 void *offs; 
}; 

       函數(shù)中還用到了另外一個數(shù)據(jù)結(jié)構(gòu)struct binder_io,也是定義在frameworks/base/cmds/servicemanager/binder.h文件中:

struct binder_io 
{ 
 char *data;   /* pointer to read/write from */ 
 uint32_t *offs;  /* array of offsets */ 
 uint32_t data_avail; /* bytes available in data buffer */ 
 uint32_t offs_avail; /* entries available in offsets array */ 
 
 char *data0;   /* start of data buffer */ 
 uint32_t *offs0;  /* start of offsets buffer */ 
 uint32_t flags; 
 uint32_t unused; 
}; 

       接著往下看,函數(shù)調(diào)bio_init來初始化reply變量:

void bio_init(struct binder_io *bio, void *data, 
    uint32_t maxdata, uint32_t maxoffs) 
{ 
 uint32_t n = maxoffs * sizeof(uint32_t); 
 
 if (n > maxdata) { 
  bio->flags = BIO_F_OVERFLOW; 
  bio->data_avail = 0; 
  bio->offs_avail = 0; 
  return; 
 } 
 
 bio->data = bio->data0 = data + n; 
 bio->offs = bio->offs0 = data; 
 bio->data_avail = maxdata - n; 
 bio->offs_avail = maxoffs; 
 bio->flags = 0; 
} 

       接著又調(diào)用bio_init_from_txn來初始化msg變量:

void bio_init_from_txn(struct binder_io *bio, struct binder_txn *txn) 
{ 
 bio->data = bio->data0 = txn->data; 
 bio->offs = bio->offs0 = txn->offs; 
 bio->data_avail = txn->data_size; 
 bio->offs_avail = txn->offs_size / 4; 
 bio->flags = BIO_F_SHARED; 
} 

      最后,真正進行處理的函數(shù)是從參數(shù)中傳進來的函數(shù)指針func,這里就是定義在frameworks/base/cmds/servicemanager/service_manager.c文件中的svcmgr_handler函數(shù):

int svcmgr_handler(struct binder_state *bs, 
     struct binder_txn *txn, 
     struct binder_io *msg, 
     struct binder_io *reply) 
{ 
 struct svcinfo *si; 
 uint16_t *s; 
 unsigned len; 
 void *ptr; 
 uint32_t strict_policy; 
 
 if (txn->target != svcmgr_handle) 
  return -1; 
 
 // Equivalent to Parcel::enforceInterface(), reading the RPC 
 // header with the strict mode policy mask and the interface name. 
 // Note that we ignore the strict_policy and don't propagate it 
 // further (since we do no outbound RPCs anyway). 
 strict_policy = bio_get_uint32(msg); 
 s = bio_get_string16(msg, &len); 
 if ((len != (sizeof(svcmgr_id) / 2)) || 
  memcmp(svcmgr_id, s, sizeof(svcmgr_id))) { 
   fprintf(stderr,"invalid id %s\n", str8(s)); 
   return -1; 
 } 
 
 switch(txn->code) { 
 ...... 
 case SVC_MGR_ADD_SERVICE: 
  s = bio_get_string16(msg, &len); 
  ptr = bio_get_ref(msg); 
  if (do_add_service(bs, s, len, ptr, txn->sender_euid)) 
   return -1; 
  break; 
 ...... 
 } 
 
 bio_put_uint32(reply, 0); 
 return 0; 
} 

         回憶一下,在BpServiceManager::addService時,傳給Binder驅(qū)動程序的參數(shù)為:

writeInt32(IPCThreadState::self()->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); 
writeString16("android.os.IServiceManager"); 
writeString16("media.player"); 
writeStrongBinder(new MediaPlayerService()); 

         這里的語句:

strict_policy = bio_get_uint32(msg); 
s = bio_get_string16(msg, &len); 
s = bio_get_string16(msg, &len); 
ptr = bio_get_ref(msg); 

         就是依次把它們讀取出來了,這里,我們只要看一下bio_get_ref的實現(xiàn)。先看一個數(shù)據(jù)結(jié)構(gòu)struct binder_obj的定義:

struct binder_object 
{ 
 uint32_t type; 
 uint32_t flags; 
 void *pointer; 
 void *cookie; 
}; 

        這個結(jié)構(gòu)體其實就是對應struct flat_binder_obj的。
        接著看bio_get_ref實現(xiàn):

void *bio_get_ref(struct binder_io *bio) 
{ 
 struct binder_object *obj; 
 
 obj = _bio_get_obj(bio); 
 if (!obj) 
  return 0; 
 
 if (obj->type == BINDER_TYPE_HANDLE) 
  return obj->pointer; 
 
 return 0; 
} 

       _bio_get_obj這個函數(shù)就不跟進去看了,它的作用就是從binder_io中取得第一個還沒取獲取過的binder_object。在這個場景下,就是我們最開始傳過來代表MediaPlayerService的flat_binder_obj了,這個原始的flat_binder_obj的type為BINDER_TYPE_BINDER,binder為指向MediaPlayerService的弱引用的地址。在前面我們說過,在Binder驅(qū)動驅(qū)動程序里面,會把這個flat_binder_obj的type改為BINDER_TYPE_HANDLE,handle改為一個句柄值。這里的handle值就等于obj->pointer的值。

        回到svcmgr_handler函數(shù),調(diào)用do_add_service進一步處理:


int do_add_service(struct binder_state *bs, 
     uint16_t *s, unsigned len, 
     void *ptr, unsigned uid) 
{ 
 struct svcinfo *si; 
// LOGI("add_service('%s',%p) uid=%d\n", str8(s), ptr, uid); 
 
 if (!ptr || (len == 0) || (len > 127)) 
  return -1; 
 
 if (!svc_can_register(uid, s)) { 
  LOGE("add_service('%s',%p) uid=%d - PERMISSION DENIED\n", 
    str8(s), ptr, uid); 
  return -1; 
 } 
 
 si = find_svc(s, len); 
 if (si) { 
  if (si->ptr) { 
   LOGE("add_service('%s',%p) uid=%d - ALREADY REGISTERED\n", 
     str8(s), ptr, uid); 
   return -1; 
  } 
  si->ptr = ptr; 
 } else { 
  si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); 
  if (!si) { 
   LOGE("add_service('%s',%p) uid=%d - OUT OF MEMORY\n", 
     str8(s), ptr, uid); 
   return -1; 
  } 
  si->ptr = ptr; 
  si->len = len; 
  memcpy(si->name, s, (len + 1) * sizeof(uint16_t)); 
  si->name[len] = '\0'; 
  si->death.func = svcinfo_death; 
  si->death.ptr = si; 
  si->next = svclist; 
  svclist = si; 
 } 
 
 binder_acquire(bs, ptr); 
 binder_link_to_death(bs, ptr, &si->death); 
 return 0; 
} 

        這個函數(shù)的實現(xiàn)很簡單,就是把MediaPlayerService這個Binder實體的引用寫到一個struct svcinfo結(jié)構(gòu)體中,主要是它的名稱和句柄值,然后插入到鏈接svclist的頭部去。這樣,Client來向Service Manager查詢服務接口時,只要給定服務名稱,Service Manger就可以返回相應的句柄值了。

        這個函數(shù)執(zhí)行完成后,返回到svcmgr_handler函數(shù),函數(shù)的最后,將一個錯誤碼0寫到reply變量中去,表示一切正常:

             bio_put_uint32(reply, 0);  

       svcmgr_handler函數(shù)執(zhí)行完成后,返回到binder_parse函數(shù),執(zhí)行下面語句:

               binder_send_reply(bs, &reply, txn->data, res);  

       我們看一下binder_send_reply的實現(xiàn),從函數(shù)名就可以猜到它要做什么了,告訴Binder驅(qū)動程序,它完成了Binder驅(qū)動程序交給它的任務了。

void binder_send_reply(struct binder_state *bs, 
      struct binder_io *reply, 
      void *buffer_to_free, 
      int status) 
{ 
 struct { 
  uint32_t cmd_free; 
  void *buffer; 
  uint32_t cmd_reply; 
  struct binder_txn txn; 
 } __attribute__((packed)) data; 
 
 data.cmd_free = BC_FREE_BUFFER; 
 data.buffer = buffer_to_free; 
 data.cmd_reply = BC_REPLY; 
 data.txn.target = 0; 
 data.txn.cookie = 0; 
 data.txn.code = 0; 
 if (status) { 
  data.txn.flags = TF_STATUS_CODE; 
  data.txn.data_size = sizeof(int); 
  data.txn.offs_size = 0; 
  data.txn.data = &status; 
  data.txn.offs = 0; 
 } else { 
  data.txn.flags = 0; 
  data.txn.data_size = reply->data - reply->data0; 
  data.txn.offs_size = ((char*) reply->offs) - ((char*) reply->offs0); 
  data.txn.data = reply->data0; 
  data.txn.offs = reply->offs0; 
 } 
 binder_write(bs, &data, sizeof(data)); 
} 

       從這里可以看出,binder_send_reply告訴Binder驅(qū)動程序執(zhí)行BC_FREE_BUFFER和BC_REPLY命令,前者釋放之前在binder_transaction分配的空間,地址為buffer_to_free,buffer_to_free這個地址是Binder驅(qū)動程序把自己在內(nèi)核空間用的地址轉(zhuǎn)換成用戶空間地址再傳給Service Manager的,所以Binder驅(qū)動程序拿到這個地址后,知道怎么樣釋放這個空間;后者告訴MediaPlayerService,它的addService操作已經(jīng)完成了,錯誤碼是0,保存在data.txn.data中。

       再來看binder_write函數(shù):

int binder_write(struct binder_state *bs, void *data, unsigned len) 
{ 
 struct binder_write_read bwr; 
 int res; 
 bwr.write_size = len; 
 bwr.write_consumed = 0; 
 bwr.write_buffer = (unsigned) data; 
 bwr.read_size = 0; 
 bwr.read_consumed = 0; 
 bwr.read_buffer = 0; 
 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); 
 if (res < 0) { 
  fprintf(stderr,"binder_write: ioctl failed (%s)\n", 
    strerror(errno)); 
 } 
 return res; 
} 

       這里可以看出,只有寫操作,沒有讀操作,即read_size為0。

       這里又是一個ioctl的BINDER_WRITE_READ操作。直入到驅(qū)動程序的binder_ioctl函數(shù)后,執(zhí)行BINDER_WRITE_READ命令,這里就不累述了。

       最后,從binder_ioctl執(zhí)行到binder_thread_write函數(shù),我們首先看第一個命令BC_FREE_BUFFER:

int 
binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, 
     void __user *buffer, int size, signed long *consumed) 
{ 
 uint32_t cmd; 
 void __user *ptr = buffer + *consumed; 
 void __user *end = buffer + size; 
 
 while (ptr < end && thread->return_error == BR_OK) { 
  if (get_user(cmd, (uint32_t __user *)ptr)) 
   return -EFAULT; 
  ptr += sizeof(uint32_t); 
  if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { 
   binder_stats.bc[_IOC_NR(cmd)]++; 
   proc->stats.bc[_IOC_NR(cmd)]++; 
   thread->stats.bc[_IOC_NR(cmd)]++; 
  } 
  switch (cmd) { 
  ...... 
  case BC_FREE_BUFFER: { 
   void __user *data_ptr; 
   struct binder_buffer *buffer; 
 
   if (get_user(data_ptr, (void * __user *)ptr)) 
    return -EFAULT; 
   ptr += sizeof(void *); 
 
   buffer = binder_buffer_lookup(proc, data_ptr); 
   if (buffer == NULL) { 
    binder_user_error("binder: %d:%d " 
     "BC_FREE_BUFFER u%p no match\n", 
     proc->pid, thread->pid, data_ptr); 
    break; 
   } 
   if (!buffer->allow_user_free) { 
    binder_user_error("binder: %d:%d " 
     "BC_FREE_BUFFER u%p matched " 
     "unreturned buffer\n", 
     proc->pid, thread->pid, data_ptr); 
    break; 
   } 
   if (binder_debug_mask & BINDER_DEBUG_FREE_BUFFER) 
    printk(KERN_INFO "binder: %d:%d BC_FREE_BUFFER u%p found buffer %d for %s transaction\n", 
    proc->pid, thread->pid, data_ptr, buffer->debug_id, 
    buffer->transaction ? "active" : "finished"); 
 
   if (buffer->transaction) { 
    buffer->transaction->buffer = NULL; 
    buffer->transaction = NULL; 
   } 
   if (buffer->async_transaction && buffer->target_node) { 
    BUG_ON(!buffer->target_node->has_async_transaction); 
    if (list_empty(&buffer->target_node->async_todo)) 
     buffer->target_node->has_async_transaction = 0; 
    else 
     list_move_tail(buffer->target_node->async_todo.next, &thread->todo); 
   } 
   binder_transaction_buffer_release(proc, buffer, NULL); 
   binder_free_buf(proc, buffer); 
   break; 
        } 
 
  ...... 
  *consumed = ptr - buffer; 
 } 
 return 0; 
} 

       首先通過看這個語句:

get_user(data_ptr, (void * __user *)ptr) 

       這個是獲得要刪除的Buffer的用戶空間地址,接著通過下面這個語句來找到這個地址對應的struct binder_buffer信息:

            buffer = binder_buffer_lookup(proc, data_ptr);  

       因為這個空間是前面在binder_transaction里面分配的,所以這里一定能找到。

       最后,就可以釋放這塊內(nèi)存了:

            binder_transaction_buffer_release(proc, buffer, NULL);  
            binder_free_buf(proc, buffer);  

       再來看另外一個命令BC_REPLY:

int 
binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, 
     void __user *buffer, int size, signed long *consumed) 
{ 
 uint32_t cmd; 
 void __user *ptr = buffer + *consumed; 
 void __user *end = buffer + size; 
 
 while (ptr < end && thread->return_error == BR_OK) { 
  if (get_user(cmd, (uint32_t __user *)ptr)) 
   return -EFAULT; 
  ptr += sizeof(uint32_t); 
  if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { 
   binder_stats.bc[_IOC_NR(cmd)]++; 
   proc->stats.bc[_IOC_NR(cmd)]++; 
   thread->stats.bc[_IOC_NR(cmd)]++; 
  } 
  switch (cmd) { 
  ...... 
  case BC_TRANSACTION: 
  case BC_REPLY: { 
   struct binder_transaction_data tr; 
 
   if (copy_from_user(&tr, ptr, sizeof(tr))) 
    return -EFAULT; 
   ptr += sizeof(tr); 
   binder_transaction(proc, thread, &tr, cmd == BC_REPLY); 
   break; 
      } 
 
  ...... 
  *consumed = ptr - buffer; 
 } 
 return 0; 
} 

       又再次進入到binder_transaction函數(shù):

static void 
binder_transaction(struct binder_proc *proc, struct binder_thread *thread, 
struct binder_transaction_data *tr, int reply) 
{ 
 struct binder_transaction *t; 
 struct binder_work *tcomplete; 
 size_t *offp, *off_end; 
 struct binder_proc *target_proc; 
 struct binder_thread *target_thread = NULL; 
 struct binder_node *target_node = NULL; 
 struct list_head *target_list; 
 wait_queue_head_t *target_wait; 
 struct binder_transaction *in_reply_to = NULL; 
 struct binder_transaction_log_entry *e; 
 uint32_t return_error; 
 
 ...... 
 
 if (reply) { 
  in_reply_to = thread->transaction_stack; 
  if (in_reply_to == NULL) { 
   ...... 
   return_error = BR_FAILED_REPLY; 
   goto err_empty_call_stack; 
  } 
  binder_set_nice(in_reply_to->saved_priority); 
  if (in_reply_to->to_thread != thread) { 
   ....... 
   goto err_bad_call_stack; 
  } 
  thread->transaction_stack = in_reply_to->to_parent; 
  target_thread = in_reply_to->from; 
  if (target_thread == NULL) { 
   return_error = BR_DEAD_REPLY; 
   goto err_dead_binder; 
  } 
  if (target_thread->transaction_stack != in_reply_to) { 
   ...... 
   return_error = BR_FAILED_REPLY; 
   in_reply_to = NULL; 
   target_thread = NULL; 
   goto err_dead_binder; 
  } 
  target_proc = target_thread->proc; 
 } else { 
  ...... 
 } 
 if (target_thread) { 
  e->to_thread = target_thread->pid; 
  target_list = &target_thread->todo; 
  target_wait = &target_thread->wait; 
 } else { 
  ...... 
 } 
 
 
 /* TODO: reuse incoming transaction for reply */ 
 t = kzalloc(sizeof(*t), GFP_KERNEL); 
 if (t == NULL) { 
  return_error = BR_FAILED_REPLY; 
  goto err_alloc_t_failed; 
 } 
  
 
 tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); 
 if (tcomplete == NULL) { 
  return_error = BR_FAILED_REPLY; 
  goto err_alloc_tcomplete_failed; 
 } 
 
 if (!reply && !(tr->flags & TF_ONE_WAY)) 
  t->from = thread; 
 else 
  t->from = NULL; 
 t->sender_euid = proc->tsk->cred->euid; 
 t->to_proc = target_proc; 
 t->to_thread = target_thread; 
 t->code = tr->code; 
 t->flags = tr->flags; 
 t->priority = task_nice(current); 
 t->buffer = binder_alloc_buf(target_proc, tr->data_size, 
  tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); 
 if (t->buffer == NULL) { 
  return_error = BR_FAILED_REPLY; 
  goto err_binder_alloc_buf_failed; 
 } 
 t->buffer->allow_user_free = 0; 
 t->buffer->debug_id = t->debug_id; 
 t->buffer->transaction = t; 
 t->buffer->target_node = target_node; 
 if (target_node) 
  binder_inc_node(target_node, 1, 0, NULL); 
 
 offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); 
 
 if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) { 
  binder_user_error("binder: %d:%d got transaction with invalid " 
   "data ptr\n", proc->pid, thread->pid); 
  return_error = BR_FAILED_REPLY; 
  goto err_copy_data_failed; 
 } 
 if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) { 
  binder_user_error("binder: %d:%d got transaction with invalid " 
   "offsets ptr\n", proc->pid, thread->pid); 
  return_error = BR_FAILED_REPLY; 
  goto err_copy_data_failed; 
 } 
  
 ...... 
 
 if (reply) { 
  BUG_ON(t->buffer->async_transaction != 0); 
  binder_pop_transaction(target_thread, in_reply_to); 
 } else if (!(t->flags & TF_ONE_WAY)) { 
  ...... 
 } else { 
  ...... 
 } 
 t->work.type = BINDER_WORK_TRANSACTION; 
 list_add_tail(&t->work.entry, target_list); 
 tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; 
 list_add_tail(&tcomplete->entry, &thread->todo); 
 if (target_wait) 
  wake_up_interruptible(target_wait); 
 return; 
 ...... 
} 

       注意,這里的reply為1,我們忽略掉其它無關(guān)代碼。

       前面Service Manager正在binder_thread_read函數(shù)中被MediaPlayerService啟動后進程喚醒后,在最后會把當前處理完的事務放在thread->transaction_stack中:

if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { 
 t->to_parent = thread->transaction_stack; 
 t->to_thread = thread; 
 thread->transaction_stack = t; 
} 

       所以,這里,首先是把它這個binder_transaction取回來,并且放在本地變量in_reply_to中:

                      in_reply_to = thread->transaction_stack;  

       接著就可以通過in_reply_to得到最終發(fā)出這個事務請求的線程和進程:

                      target_thread = in_reply_to->from; 
                      target_proc = target_thread->proc;  

        然后得到target_list和target_wait:

                    target_list = &target_thread->todo;  
                    target_wait = &target_thread->wait;  

       下面這一段代碼:

/* TODO: reuse incoming transaction for reply */ 
t = kzalloc(sizeof(*t), GFP_KERNEL); 
if (t == NULL) { 
 return_error = BR_FAILED_REPLY; 
 goto err_alloc_t_failed; 
} 
 
 
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); 
if (tcomplete == NULL) { 
 return_error = BR_FAILED_REPLY; 
 goto err_alloc_tcomplete_failed; 
} 
 
if (!reply && !(tr->flags & TF_ONE_WAY)) 
 t->from = thread; 
else 
 t->from = NULL; 
t->sender_euid = proc->tsk->cred->euid; 
t->to_proc = target_proc; 
t->to_thread = target_thread; 
t->code = tr->code; 
t->flags = tr->flags; 
t->priority = task_nice(current); 
t->buffer = binder_alloc_buf(target_proc, tr->data_size, 
 tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); 
if (t->buffer == NULL) { 
 return_error = BR_FAILED_REPLY; 
 goto err_binder_alloc_buf_failed; 
} 
t->buffer->allow_user_free = 0; 
t->buffer->debug_id = t->debug_id; 
t->buffer->transaction = t; 
t->buffer->target_node = target_node; 
if (target_node) 
 binder_inc_node(target_node, 1, 0, NULL); 
 
offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); 
 
if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) { 
 binder_user_error("binder: %d:%d got transaction with invalid " 
  "data ptr\n", proc->pid, thread->pid); 
 return_error = BR_FAILED_REPLY; 
 goto err_copy_data_failed; 
} 
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) { 
 binder_user_error("binder: %d:%d got transaction with invalid " 
  "offsets ptr\n", proc->pid, thread->pid); 
 return_error = BR_FAILED_REPLY; 
 goto err_copy_data_failed; 
} 

          我們在前面已經(jīng)分析過了,這里不再重復。但是有一點要注意的是,這里target_node為NULL,因此,t->buffer->target_node也為NULL。

          函數(shù)本來有一個for循環(huán),用來處理數(shù)據(jù)中的Binder對象,這里由于沒有Binder對象,所以就略過了。到了下面這句代碼:

                   binder_pop_transaction(target_thread, in_reply_to);  

          我們看看做了什么事情: 

static void 
binder_pop_transaction( 
 struct binder_thread *target_thread, struct binder_transaction *t) 
{ 
 if (target_thread) { 
  BUG_ON(target_thread->transaction_stack != t); 
  BUG_ON(target_thread->transaction_stack->from != target_thread); 
  target_thread->transaction_stack = 
   target_thread->transaction_stack->from_parent; 
  t->from = NULL; 
 } 
 t->need_reply = 0; 
 if (t->buffer) 
  t->buffer->transaction = NULL; 
 kfree(t); 
 binder_stats.obj_deleted[BINDER_STAT_TRANSACTION]++; 
} 

        由于到了這里,已經(jīng)不需要in_reply_to這個transaction了,就把它刪掉。

        回到binder_transaction函數(shù):   

t->work.type = BINDER_WORK_TRANSACTION; 
list_add_tail(&t->work.entry, target_list); 
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; 
list_add_tail(&tcomplete->entry, &thread->todo); 

和前面一樣,分別把t和tcomplete分別放在target_list和thread->todo隊列中,這里的target_list指的就是最初調(diào)用IServiceManager::addService的MediaPlayerService的Server主線程的的thread->todo隊列了,而thread->todo指的是Service Manager中用來回復IServiceManager::addService請求的線程。

        最后,喚醒等待在target_wait隊列上的線程了,就是最初調(diào)用IServiceManager::addService的MediaPlayerService的Server主線程了,它最后在binder_thread_read函數(shù)中睡眠在thread->wait上,就是這里的target_wait了:

                  if (target_wait)  
                              wake_up_interruptible(target_wait);  

        這樣,Service Manger回復調(diào)用IServiceManager::addService請求就算完成了,重新回到frameworks/base/cmds/servicemanager/binder.c文件中的binder_loop函數(shù)等待下一個Client請求的到來。事實上,Service Manger回到binder_loop函數(shù)再次執(zhí)行ioctl函數(shù)時候,又會再次進入到binder_thread_read函數(shù)。這時個會發(fā)現(xiàn)thread->todo不為空,這是因為剛才我們調(diào)用了:

                 list_add_tail(&tcomplete->entry, &thread->todo);  

         把一個工作項tcompelete放在了在thread->todo中,這個tcompelete的type為BINDER_WORK_TRANSACTION_COMPLETE,因此,Binder驅(qū)動程序會執(zhí)行下面操作:

switch (w->type) { 
case BINDER_WORK_TRANSACTION_COMPLETE: { 
 cmd = BR_TRANSACTION_COMPLETE; 
 if (put_user(cmd, (uint32_t __user *)ptr)) 
  return -EFAULT; 
 ptr += sizeof(uint32_t); 
 
 list_del(&w->entry); 
 kfree(w); 
  
 } break; 
 ...... 
} 


        binder_loop函數(shù)執(zhí)行完這個ioctl調(diào)用后,才會在下一次調(diào)用ioctl進入到Binder驅(qū)動程序進入休眠狀態(tài),等待下一次Client的請求。

        上面講到調(diào)用IServiceManager::addService的MediaPlayerService的Server主線程被喚醒了,于是,重新執(zhí)行binder_thread_read函數(shù):

static int 
binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, 
     void __user *buffer, int size, signed long *consumed, int non_block) 
{ 
 void __user *ptr = buffer + *consumed; 
 void __user *end = buffer + size; 
 
 int ret = 0; 
 int wait_for_proc_work; 
 
 if (*consumed == 0) { 
  if (put_user(BR_NOOP, (uint32_t __user *)ptr)) 
   return -EFAULT; 
  ptr += sizeof(uint32_t); 
 } 
 
retry: 
 wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); 
 
 ...... 
 
 if (wait_for_proc_work) { 
  ...... 
 } else { 
  if (non_block) { 
   if (!binder_has_thread_work(thread)) 
    ret = -EAGAIN; 
  } else 
   ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread)); 
 } 
  
 ...... 
 
 while (1) { 
  uint32_t cmd; 
  struct binder_transaction_data tr; 
  struct binder_work *w; 
  struct binder_transaction *t = NULL; 
 
  if (!list_empty(&thread->todo)) 
   w = list_first_entry(&thread->todo, struct binder_work, entry); 
  else if (!list_empty(&proc->todo) && wait_for_proc_work) 
   w = list_first_entry(&proc->todo, struct binder_work, entry); 
  else { 
   if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */ 
    goto retry; 
   break; 
  } 
 
  ...... 
 
  switch (w->type) { 
  case BINDER_WORK_TRANSACTION: { 
   t = container_of(w, struct binder_transaction, work); 
          } break; 
  ...... 
  } 
 
  if (!t) 
   continue; 
 
  BUG_ON(t->buffer == NULL); 
  if (t->buffer->target_node) { 
   ...... 
  } else { 
   tr.target.ptr = NULL; 
   tr.cookie = NULL; 
   cmd = BR_REPLY; 
  } 
  tr.code = t->code; 
  tr.flags = t->flags; 
  tr.sender_euid = t->sender_euid; 
 
  if (t->from) { 
   ...... 
  } else { 
   tr.sender_pid = 0; 
  } 
 
  tr.data_size = t->buffer->data_size; 
  tr.offsets_size = t->buffer->offsets_size; 
  tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset; 
  tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *)); 
 
  if (put_user(cmd, (uint32_t __user *)ptr)) 
   return -EFAULT; 
  ptr += sizeof(uint32_t); 
  if (copy_to_user(ptr, &tr, sizeof(tr))) 
   return -EFAULT; 
  ptr += sizeof(tr); 
 
  ...... 
 
  list_del(&t->work.entry); 
  t->buffer->allow_user_free = 1; 
  if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { 
   ...... 
  } else { 
   t->buffer->transaction = NULL; 
   kfree(t); 
   binder_stats.obj_deleted[BINDER_STAT_TRANSACTION]++; 
  } 
  break; 
 } 
 
done: 
 ...... 
 return 0; 
} 

         在while循環(huán)中,從thread->todo得到w,w->type為BINDER_WORK_TRANSACTION,于是,得到t。從上面可以知道,Service Manager反回了一個0回來,寫在t->buffer->data里面,現(xiàn)在把t->buffer->data加上proc->user_buffer_offset,得到用戶空間地址,保存在tr.data.ptr.buffer里面,這樣用戶空間就可以訪問這個返回碼了。由于cmd不等于BR_TRANSACTION,這時就可以把t刪除掉了,因為以后都不需要用了。

         執(zhí)行完這個函數(shù)后,就返回到binder_ioctl函數(shù),執(zhí)行下面語句,把數(shù)據(jù)返回給用戶空間:

if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { 
 ret = -EFAULT; 
 goto err; 
} 

         接著返回到用戶空間IPCThreadState::talkWithDriver函數(shù),最后返回到IPCThreadState::waitForResponse函數(shù),最終執(zhí)行到下面語句:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) 
{ 
 int32_t cmd; 
 int32_t err; 
 
 while (1) { 
  if ((err=talkWithDriver()) < NO_ERROR) break; 
   
  ...... 
 
  cmd = mIn.readInt32(); 
 
  ...... 
 
  switch (cmd) { 
  ...... 
  case BR_REPLY: 
   { 
    binder_transaction_data tr; 
    err = mIn.read(&tr, sizeof(tr)); 
    LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY"); 
    if (err != NO_ERROR) goto finish; 
 
    if (reply) { 
     if ((tr.flags & TF_STATUS_CODE) == 0) { 
      reply->ipcSetDataReference( 
       reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), 
       tr.data_size, 
       reinterpret_cast<const size_t*>(tr.data.ptr.offsets), 
       tr.offsets_size/sizeof(size_t), 
       freeBuffer, this); 
     } else { 
      ...... 
     } 
    } else { 
     ...... 
    } 
   } 
   goto finish; 
 
  ...... 
  } 
 } 
 
finish: 
 ...... 
 return err; 
} 

        注意,這里的tr.flags等于0,這個是在上面的binder_send_reply函數(shù)里設置的。最終把結(jié)果保存在reply了:

reply->ipcSetDataReference( 
  reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), 
  tr.data_size, 
  reinterpret_cast<const size_t*>(tr.data.ptr.offsets), 
  tr.offsets_size/sizeof(size_t), 
  freeBuffer, this); 

       這個函數(shù)我們就不看了,有興趣的讀者可以研究一下。

       從這里層層返回,最后回到MediaPlayerService::instantiate函數(shù)中。

       至此,IServiceManager::addService終于執(zhí)行完畢了。這個過程非常復雜,但是如果我們能夠深刻地理解這一過程,將能很好地理解Binder機制的設計思想和實現(xiàn)過程。這里,對IServiceManager::addService過程中MediaPlayerService、ServiceManager和BinderDriver之間的交互作一個小結(jié):

        回到frameworks/base/media/mediaserver/main_mediaserver.cpp文件中的main函數(shù),接下去還要執(zhí)行下面兩個函數(shù):

                 ProcessState::self()->startThreadPool(); 
                 IPCThreadState::self()->joinThreadPool();  

        首先看ProcessState::startThreadPool函數(shù)的實現(xiàn):

void ProcessState::startThreadPool() 
{ 
 AutoMutex _l(mLock); 
 if (!mThreadPoolStarted) { 
  mThreadPoolStarted = true; 
  spawnPooledThread(true); 
 } 
} 

       這里調(diào)用spwanPooledThread:

void ProcessState::spawnPooledThread(bool isMain) 
{ 
 if (mThreadPoolStarted) { 
  int32_t s = android_atomic_add(1, &mThreadPoolSeq); 
  char buf[32]; 
  sprintf(buf, "Binder Thread #%d", s); 
  LOGV("Spawning new pooled thread, name=%s\n", buf); 
  sp<Thread> t = new PoolThread(isMain); 
  t->run(buf); 
 } 
} 

       這里主要是創(chuàng)建一個線程,PoolThread繼續(xù)Thread類,Thread類定義在frameworks/base/libs/utils/Threads.cpp文件中,其run函數(shù)最終調(diào)用子類的threadLoop函數(shù),這里即為PoolThread::threadLoop函數(shù):

virtual bool threadLoop() 
{ 
 IPCThreadState::self()->joinThreadPool(mIsMain); 
 return false; 
} 

       這里和frameworks/base/media/mediaserver/main_mediaserver.cpp文件中的main函數(shù)一樣,最終都是調(diào)用了IPCThreadState::joinThreadPool函數(shù),它們的區(qū)別是,一個參數(shù)是true,一個是默認值false。我們來看一下這個函數(shù)的實現(xiàn):

void IPCThreadState::joinThreadPool(bool isMain) 
{ 
 LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); 
 
 mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); 
 
 ...... 
 
 status_t result; 
 do { 
  int32_t cmd; 
 
  ....... 
 
  // now get the next command to be processed, waiting if necessary 
  result = talkWithDriver(); 
  if (result >= NO_ERROR) { 
   size_t IN = mIn.dataAvail(); 
   if (IN < sizeof(int32_t)) continue; 
   cmd = mIn.readInt32(); 
   ...... 
   } 
 
   result = executeCommand(cmd); 
  } 
 
  ...... 
 } while (result != -ECONNREFUSED && result != -EBADF); 
 
 ....... 
 
 mOut.writeInt32(BC_EXIT_LOOPER); 
 talkWithDriver(false); 
} 

        這個函數(shù)最終是在一個無窮循環(huán)中,通過調(diào)用talkWithDriver函數(shù)來和Binder驅(qū)動程序進行交互,實際上就是調(diào)用talkWithDriver來等待Client的請求,然后再調(diào)用executeCommand來處理請求,而在executeCommand函數(shù)中,最終會調(diào)用BBinder::transact來真正處理Client的請求:

status_t IPCThreadState::executeCommand(int32_t cmd) 
{ 
 BBinder* obj; 
 RefBase::weakref_type* refs; 
 status_t result = NO_ERROR; 
 
 switch (cmd) { 
 ...... 
 
 case BR_TRANSACTION: 
  { 
   binder_transaction_data tr; 
   result = mIn.read(&tr, sizeof(tr)); 
    
   ...... 
 
   Parcel reply; 
    
   ...... 
 
   if (tr.target.ptr) { 
    sp<BBinder> b((BBinder*)tr.cookie); 
    const status_t error = b->transact(tr.code, buffer, &reply, tr.flags); 
    if (error < NO_ERROR) reply.setError(error); 
 
   } else { 
    const status_t error = the_context_object->transact(tr.code, buffer, &reply, tr.flags); 
    if (error < NO_ERROR) reply.setError(error); 
   } 
 
   ...... 
  } 
  break; 
 
 ....... 
 } 
 
 if (result != NO_ERROR) { 
  mLastError = result; 
 } 
 
 return result; 
} 

        接下來再看一下BBinder::transact的實現(xiàn):

status_t BBinder::transact( 
 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) 
{ 
 data.setDataPosition(0); 
 
 status_t err = NO_ERROR; 
 switch (code) { 
  case PING_TRANSACTION: 
   reply->writeInt32(pingBinder()); 
   break; 
  default: 
   err = onTransact(code, data, reply, flags); 
   break; 
 } 
 
 if (reply != NULL) { 
  reply->setDataPosition(0); 
 } 
 
 return err; 
} 

       最終會調(diào)用onTransact函數(shù)來處理。在這個場景中,BnMediaPlayerService繼承了BBinder類,并且重載了onTransact函數(shù),因此,這里實際上是調(diào)用了BnMediaPlayerService::onTransact函數(shù),這個函數(shù)定義在frameworks/base/libs/media/libmedia/IMediaPlayerService.cpp文件中:

status_t BnMediaPlayerService::onTransact( 
 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) 
{ 
 switch(code) { 
  case CREATE_URL: { 
   ...... 
       } break; 
  case CREATE_FD: { 
   ...... 
      } break; 
  case DECODE_URL: { 
   ...... 
       } break; 
  case DECODE_FD: { 
   ...... 
      } break; 
  case CREATE_MEDIA_RECORDER: { 
   ...... 
         } break; 
  case CREATE_METADATA_RETRIEVER: { 
   ...... 
          } break; 
  case GET_OMX: { 
   ...... 
      } break; 
  default: 
   return BBinder::onTransact(code, data, reply, flags); 
 } 
} 

       至此,我們就以MediaPlayerService為例,完整地介紹了Android系統(tǒng)進程間通信Binder機制中的Server啟動過程。Server啟動起來之后,就會在一個無窮循環(huán)中等待Client的請求了。在下一篇文章中,我們將介紹Client如何通過Service Manager遠程接口來獲得Server遠程接口,進而調(diào)用Server遠程接口來使用Server提供的服務,敬請關(guān)注。

相關(guān)文章

  • Android自定義評分控件的完整實例

    Android自定義評分控件的完整實例

    在Android開發(fā)中,我們經(jīng)常會用到對商家或者商品的評價,運用星星進行打分,下面這篇文章主要給大家介紹了關(guān)于Android自定義評分控件的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2022-05-05
  • 取消Android Studio項目與SVN關(guān)聯(lián)的方法

    取消Android Studio項目與SVN關(guān)聯(lián)的方法

    今天小編就為大家分享一篇關(guān)于取消Android Studio項目與SVN關(guān)聯(lián)的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • Android設計模式之代理模式Proxy淺顯易懂的詳細說明

    Android設計模式之代理模式Proxy淺顯易懂的詳細說明

    Android設計模式之代理模式也是平時比較常用的設計模式之一,代理模式其實就是提供了一個新的對象,實現(xiàn)了對真實對象的操作,或成為真實對象的替身
    2018-03-03
  • Android基于OpenCV實現(xiàn)Harris角點檢測

    Android基于OpenCV實現(xiàn)Harris角點檢測

    角點就是極值點,即在某方面屬性特別突出的點。當然,你可以自己定義角點的屬性(設置特定熵值進行角點檢測)。角點可以是兩條線的交叉處,也可以是位于相鄰的兩個主要方向不同的事物上的點。本文介紹如何基于OpenCV實現(xiàn)Harris角點檢測
    2021-06-06
  • 從"Show?tabs"了解Android?Input系統(tǒng)

    從"Show?tabs"了解Android?Input系統(tǒng)

    這篇文章主要介紹了從"Show?tabs"了解Android?Input系統(tǒng)的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Android原生嵌入React Native詳解

    Android原生嵌入React Native詳解

    這篇文章主要為大家詳細介紹了Android原生嵌入React Native的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Android中如何安全地打印日志詳解

    Android中如何安全地打印日志詳解

    日志是我們在日常開發(fā)中必不可少的一部分,下面這篇文章主要給大家介紹了關(guān)于Android中如何安全地打印日志的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們一起來看看吧。
    2017-12-12
  • android獲取手機唯一標識的方法

    android獲取手機唯一標識的方法

    這篇文章主要介紹了獲取安卓的手機或者平板的唯一標識的方法,需要的朋友可以參考下
    2014-02-02
  • Android自定義控件屬性詳細介紹

    Android自定義控件屬性詳細介紹

    這篇文章主要介紹了Android自定義控件屬性詳細介紹的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Android程序打包為APK的方法詳解

    Android程序打包為APK的方法詳解

    這篇文章主要介紹了Android程序打包為APK的相關(guān)知識,非常不錯,具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2019-05-05

最新評論

av视屏免费在线播放| 在线播放一区二区三区Av无码| 精品亚洲国产中文自在线| 2021最新热播中文字幕| 伊拉克及约旦宣布关闭领空| 久久一区二区三区人妻欧美| 97青青青手机在线视频| 99婷婷在线观看视频| 好了av中文字幕在线| 93精品视频在线观看| 91九色porny国产在线| 天天日天天操天天摸天天舔| 91免费观看国产免费| 高潮喷水在线视频观看| 美女骚逼日出水来了| 91chinese在线视频| 天天摸天天日天天操| 亚洲公开视频在线观看| 十八禁在线观看地址免费| 蜜臀av久久久久久久| 99的爱精品免费视频| 亚洲欧美激情国产综合久久久| 天天操天天爽天天干| 久久一区二区三区人妻欧美| 亚洲成人激情av在线| 精品91自产拍在线观看一区| 青青青视频手机在线观看| av中文字幕电影在线看| 色吉吉影音天天干天天操| 成人色综合中文字幕| 91香蕉成人app下载| 老司机在线精品福利视频| 夜夜骑夜夜操夜夜奸| 日韩人妻丝袜中文字幕| 色天天天天射天天舔| 一级黄色av在线观看| 成人av免费不卡在线观看| 视频 国产 精品 熟女 | 51国产成人精品视频| 亚洲最大免费在线观看| 亚洲精品无码色午夜福利理论片| 国产精品一区二区久久久av| 亚洲熟色妇av日韩熟色妇在线| 亚洲熟女久久久36d| 五月天色婷婷在线观看视频免费| 成人av久久精品一区二区| 成人av在线资源网站| 绝色少妇高潮3在线观看| 日本女人一级免费片| 在线观看免费av网址大全| 美女操逼免费短视频下载链接| 99热碰碰热精品a中文| 日韩无码国产精品强奸乱伦| 精品一区二区三区三区88| 日本午夜福利免费视频| 激情啪啪啪啪一区二区三区| 亚洲图库另类图片区| 少妇人妻100系列| 亚洲国产精品久久久久蜜桃| 狠狠躁夜夜躁人人爽天天天天97| 夜色撩人久久7777| 亚洲狠狠婷婷综合久久app| 欧美日韩一区二区电影在线观看 | 黄色视频在线观看高清无码 | 久久午夜夜伦痒痒想咳嗽P| 亚洲高清国产自产av| 1区2区3区4区视频在线观看| 99热久久这里只有精品| 亚洲欧美清纯唯美另类| 影音先锋女人av噜噜色| 视频 一区二区在线观看| 亚洲熟妇无码一区二区三区| 99精品国产自在现线观看| 中国老熟女偷拍第一页| 精品美女在线观看视频在线观看| 国产精品免费不卡av| 欧美亚洲一二三区蜜臀| 欧美精品资源在线观看| 欧美日本aⅴ免费视频| 亚洲综合乱码一区二区| 姐姐的朋友2在线观看中文字幕 | 亚洲图片偷拍自拍区| 国产成人一区二区三区电影网站| 日韩一区二区电国产精品| 日韩亚洲高清在线观看| 亚洲 色图 偷拍 欧美| 大屁股肉感人妻中文字幕在线| 老熟妇xxxhd老熟女| avjpm亚洲伊人久久| 国产日韩精品一二三区久久久| 啪啪啪操人视频在线播放| 二区中出在线观看老师| 欧美日韩激情啪啪啪| 不戴胸罩引我诱的隔壁的人妻| 97超碰国语国产97超碰| 婷婷激情四射在线观看视频| 午夜激情久久不卡一区二区| 亚洲中文字幕乱码区| 日韩av中文在线免费观看| 99re6热在线精品| 黄色av网站免费在线| 十八禁在线观看地址免费| 天天日天天鲁天天操| 超污视频在线观看污污污 | 熟女人妻在线观看视频| 日韩av免费观看一区| 亚洲 欧美 精品 激情 偷拍| 人妻3p真实偷拍一二区| 中文字幕,亚洲人妻| 精品一线二线三线日本| 色花堂在线av中文字幕九九| 亚洲另类在线免费观看| 在线观看av观看av| 91久久国产成人免费网站| 亚洲成人线上免费视频观看| 和邻居少妇愉情中文字幕| 亚洲丝袜老师诱惑在线观看| jul—619中文字幕在线| 美女大bxxxx内射| 亚洲丝袜老师诱惑在线观看| 日本脱亚入欧是指什么| 精品91高清在线观看| 国产不卡av在线免费| 2022国产精品视频| 国产视频网站一区二区三区 | 18禁网站一区二区三区四区| 欧美乱妇无乱码一区二区| 日本中文字幕一二区视频| av天堂中文免费在线| 风流唐伯虎电视剧在线观看 | 青青草亚洲国产精品视频| 在线视频免费观看网| 51国产偷自视频在线播放| 精品一区二区三区在线观看| 五十路熟女av天堂| 亚洲精品色在线观看视频| 哥哥姐姐综合激情小说| 在线观看欧美黄片一区二区三区| 大香蕉大香蕉在线有码 av| 欧美一区二区三区四区性视频| 综合色区亚洲熟妇shxstz| 国产精品一区二区三区蜜臀av | 青青草在观免费国产精品| 一个色综合男人天堂| 性感美女高潮视频久久久| 欧美另类z0z变态| 亚洲特黄aaaa片| 亚洲在线免费h观看网站| 久久综合老鸭窝色综合久久| 天天做天天干天天舔| 夜夜嗨av一区二区三区中文字幕| 国产大学生援交正在播放| 521精品视频在线观看| 国产精品亚洲а∨天堂免| 传媒在线播放国产精品一区| av高潮迭起在线观看| 91麻豆精品91久久久久同性| 国产亚洲精品欧洲在线观看| 亚洲视频乱码在线观看| 孕妇奶水仑乱A级毛片免费看| 扒开让我视频在线观看| 粉嫩欧美美人妻小视频| 婷婷久久久久深爱网| 精品美女在线观看视频在线观看| 欧洲欧美日韩国产在线| 第一福利视频在线观看| 午夜精品福利一区二区三区p| 久精品人妻一区二区三区| 免费成人av中文字幕| 91久久精品色伊人6882| 欧美日本国产自视大全| 欧美第一页在线免费观看视频| 国内精品在线播放第一页| 日本脱亚入欧是指什么| 一本一本久久a久久精品综合不卡| 啊用力插好舒服视频| 亚洲免费在线视频网站| 玩弄人妻熟妇性色av少妇| 在线观看的黄色免费网站| 骚逼被大屌狂草视频免费看| 天堂资源网av中文字幕| 天堂av狠狠操蜜桃| 天天躁夜夜躁日日躁a麻豆| 亚洲在线一区二区欧美| 桃色视频在线观看一区二区 | 五十路在线观看完整版| 亚洲av黄色在线网站| 日比视频老公慢点好舒服啊| 黄工厂精品视频在线观看 | 521精品视频在线观看| 国产精彩福利精品视频| 日本女人一级免费片| 国产剧情演绎系列丝袜高跟| 红桃av成人在线观看| 亚洲一区二区三区av网站| 天天日夜夜干天天操| 天堂女人av一区二区| 国产精品一二三不卡带免费视频 | 大陆胖女人与丈夫操b国语高清| 开心 色 六月 婷婷| 亚洲日本一区二区久久久精品| 久草视频 久草视频2| 搡老妇人老女人老熟女| 沈阳熟妇28厘米大战黑人| 男生用鸡操女生视频动漫| 日韩亚国产欧美三级涩爱| 伊人网中文字幕在线视频| 亚洲熟妇久久无码精品| 午夜的视频在线观看| 视频一区二区综合精品| 一区二区视频在线观看免费观看| 色综合久久久久久久久中文| 亚洲狠狠婷婷综合久久app| 国产精彩福利精品视频| 99re国产在线精品| 亚洲国产第一页在线观看| 成人亚洲精品国产精品 | 国产精品久久久久久久久福交 | 老司机深夜免费福利视频在线观看| 久久这里只有精品热视频 | 狠狠操狠狠操免费视频| 亚洲精品精品国产综合| 97黄网站在线观看| 亚洲欧美久久久久久久久| 青青青青爽手机在线| 亚洲一区二区三区偷拍女厕91| 色花堂在线av中文字幕九九| 福利视频广场一区二区| 天天插天天狠天天操| 又黄又刺激的午夜小视频| 成人影片高清在线观看| 青青青爽视频在线播放| 亚洲精品 日韩电影| 国产白袜脚足J棉袜在线观看| 国产密臀av一区二区三| 99热99这里精品6国产| 亚洲欧美人精品高清| av中文字幕在线观看第三页| 日本少妇人妻xxxxxhd| 欧美亚洲少妇福利视频| 国产 在线 免费 精品| 视频一区二区在线免费播放| 狠狠地躁夜夜躁日日躁| 97国产福利小视频合集| 天天干天天操天天摸天天射| 精品美女久久久久久| 最近中文字幕国产在线| 97人人模人人爽人人喊 | 亚洲av第国产精品| 国产一区成人在线观看视频 | 国产97视频在线精品| 国产白袜脚足J棉袜在线观看| 三上悠亚和黑人665番号| 在线播放一区二区三区Av无码| 色狠狠av线不卡香蕉一区二区| 在线免费观看日本片| 91免费福利网91麻豆国产精品| 黄色三级网站免费下载| 伊人日日日草夜夜草| 亚洲av色图18p| 日韩欧美一级黄片亚洲| 欧美精品伦理三区四区 | 亚洲精品国产综合久久久久久久久| 亚洲精品麻豆免费在线观看| 动漫av网站18禁| AV天堂一区二区免费试看| 亚洲欧美综合另类13p| 中国视频一区二区三区| 欧美性感尤物人妻在线免费看| 亚洲区欧美区另类最新章节| 久久这里只有精品热视频| 大香蕉伊人国产在线| a v欧美一区=区三区| 亚洲精品高清自拍av | 91啪国自产中文字幕在线| 亚洲成人精品女人久久久| 999久久久久999| 青青青青青青青青青青草青青| 午夜美女少妇福利视频| 一区二区视频视频视频| 亚洲精品在线资源站| 亚洲欧美成人综合在线观看| 不卡精品视频在线观看| 直接观看免费黄网站| av高潮迭起在线观看| 欧美成人综合色在线噜噜| 国产日韩欧美美利坚蜜臀懂色| 粗大的内捧猛烈进出爽大牛汉子| 午夜青青草原网在线观看| 亚洲免费va在线播放| 99的爱精品免费视频| 韩国亚洲欧美超一级在线播放视频| 岛国黄色大片在线观看| 国产普通话插插视频| 欧美亚洲一二三区蜜臀| 欧美韩国日本国产亚洲| 中文字幕奴隷色的舞台50| av在线观看网址av| 亚洲精品乱码久久久久久密桃明| 成年午夜免费无码区| 久草极品美女视频在线观看| 天美传媒mv视频在线观看| 男女啪啪啪啪啪的网站| av亚洲中文天堂字幕网| 亚洲一区二区激情在线| 91精品高清一区二区三区| 成人av中文字幕一区| 91精品国产黑色丝袜| 99re6热在线精品| 老鸭窝日韩精品视频观看| 色天天天天射天天舔| 国产大学生援交正在播放| 日本午夜爽爽爽爽爽视频在线观看| 国产一区二区欧美三区| 欧美美女人体视频一区| 一区二区三区 自拍偷拍| 亚洲公开视频在线观看| 一区二区三区精品日本| 污污小视频91在线观看| 亚洲公开视频在线观看| 欧美精品欧美极品欧美视频 | 偷拍自拍 中文字幕| 韩国爱爱视频中文字幕| 色婷婷精品大在线观看| 午夜毛片不卡在线看| 亚洲精品欧美日韩在线播放| 国产精品亚洲а∨天堂免| 中文字幕在线免费第一页| 夜夜骑夜夜操夜夜奸| 天堂女人av一区二区| 天天干天天操天天玩天天射| 国产精品国产三级国产精东| 偷拍自拍国产在线视频| 91精品综合久久久久3d动漫| 成年美女黄网站18禁久久| 亚洲人妻30pwc| 午夜91一区二区三区| 九色精品视频在线播放| 久久精品国产亚洲精品166m| 一区二区三区日本伦理| 日本少妇精品免费视频| 91精品国产观看免费| 国产一区二区久久久裸臀| 免费看国产av网站| 姐姐的朋友2在线观看中文字幕| 伊人综合aⅴ在线网| 日本女人一级免费片| 亚洲午夜精品小视频| 内射久久久久综合网| 成人精品在线观看视频| av线天堂在线观看| 大香蕉玖玖一区2区| 大香蕉玖玖一区2区| 91九色国产porny蝌蚪| 亚洲少妇高潮免费观看| 天天做天天干天天操天天射| 亚洲一级av大片免费观看| 中文字幕1卡1区2区3区| 久久这里有免费精品| 精品黑人一区二区三区久久国产| 久青青草视频手机在线免费观看| 自拍偷拍,中文字幕| 91国产在线免费播放| 欧美视频不卡一区四区| av高潮迭起在线观看| 国产福利小视频免费观看| 日本www中文字幕| 热99re69精品8在线播放| 日本一区二区三区免费小视频| 国产精品自偷自拍啪啪啪| 日本真人性生活视频免费看| 国产激情av网站在线观看| 91福利在线视频免费观看| 成年女人免费播放视频| 97精品成人一区二区三区| 18禁美女黄网站色大片下载| 性色蜜臀av一区二区三区| 日本中文字幕一二区视频| 天天日天天做天天日天天做| 国产视频一区二区午夜| 日本精品美女在线观看| 女人精品内射国产99| 黄色男人的天堂视频| 97少妇精品在线观看| 精品首页在线观看视频| 超级碰碰在线视频免费观看| 熟女在线视频一区二区三区| 午夜在线一区二区免费| 97人人模人人爽人人喊| 91国内视频在线观看| 2022精品久久久久久中文字幕| 中文字幕日韩精品日本| 国产男女视频在线播放| 人人妻人人爽人人澡人人精品| 精品欧美一区二区vr在线观看| 亚洲中文字幕乱码区| 男人的网址你懂的亚洲欧洲av| 国产精品一区二区久久久av| 天堂av在线播放免费| 美日韩在线视频免费看| 天天日天天干天天插舔舔| 国产又粗又黄又硬又爽| 免费看国产又粗又猛又爽又黄视频| 五色婷婷综合狠狠爱| 66久久久久久久久久久| 中文字幕网站你懂的| 超碰中文字幕免费观看| 性感美女高潮视频久久久| 天天日天天鲁天天操| 毛茸茸的大外阴中国视频| 青青色国产视频在线| 专门看国产熟妇的网站| 国产精品探花熟女在线观看| 日本后入视频在线观看| 成人动漫大肉棒插进去视频| 年轻的人妻被夫上司侵犯| 一区二区三区美女毛片| v888av在线观看视频| 在线播放 日韩 av| 高清成人av一区三区| 成人蜜桃美臀九一一区二区三区| 老司机免费福利视频网| 天天想要天天操天天干| 欧美另类一区二区视频| 熟女在线视频一区二区三区| 欧美另类重口味极品在线观看| 日韩熟女av天堂系列| 亚洲欧美福利在线观看| 93精品视频在线观看| 亚洲va欧美va人人爽3p| 亚洲欧洲av天堂综合| www天堂在线久久| 亚洲综合色在线免费观看| 91大神福利视频网| 淫秽激情视频免费观看| 人妻丝袜精品中文字幕| 最新日韩av传媒在线| av一区二区三区人妻| 天天操夜夜操天天操天天操| 97少妇精品在线观看| 国产视频一区二区午夜| 91社福利《在线观看| a v欧美一区=区三区| 岛国av高清在线成人在线| 午夜青青草原网在线观看| 亚洲图片偷拍自拍区| 亚洲的电影一区二区三区| 岛国av高清在线成人在线| 中文字幕人妻被公上司喝醉在线| 一区二区在线视频中文字幕| 欧美第一页在线免费观看视频| 看一级特黄a大片日本片黑人| 美女吃鸡巴操逼高潮视频| 久久久精品999精品日本| 性感美女福利视频网站| 国产又粗又硬又猛的毛片视频| 中文人妻AV久久人妻水| 免费在线福利小视频| 亚洲 中文 自拍 另类 欧美| 日韩视频一区二区免费观看| 一区二区三区日韩久久| 亚洲欧美一卡二卡三卡| 制服丝袜在线人妻中文字幕| 一区二区三区精品日本| 九色porny九色9l自拍视频| 久久精品国产亚洲精品166m| 狠狠操操操操操操操操操| 天天干天天操天天插天天日| 国产揄拍高清国内精品对白| 在线观看911精品国产| 天天色天天操天天舔| 国产又粗又黄又硬又爽| 日韩无码国产精品强奸乱伦| 99久久久无码国产精品性出奶水 | 国产伦精品一区二区三区竹菊| 国产精品精品精品999| 最新97国产在线视频| 欧美交性又色又爽又黄麻豆| 国产福利小视频二区| 中文字幕乱码av资源| 日韩欧美国产一区ab| 啪啪啪啪啪啪啪啪啪啪黄色| 国产真实灌醉下药美女av福利| 日本黄色三级高清视频| 国产精品视频一区在线播放| 大胆亚洲av日韩av| 2025年人妻中文字幕乱码在线| 在线视频这里只有精品自拍| 91桃色成人网络在线观看| 欧美综合婷婷欧美综合| 青青青青青青青青青青草青青 | 好吊操视频这里只有精品| 精产国品久久一二三产区区别 | 日韩无码国产精品强奸乱伦| 黄网十四区丁香社区激情五月天| 欧美日韩高清午夜蜜桃大香蕉| 中文字幕奴隷色的舞台50| 不卡一不卡二不卡三| 一色桃子人妻一区二区三区| 97人妻色免费视频| 欧美aa一级一区三区四区| 中文字幕在线永久免费播放| 狍和女人的王色毛片| 人妻丝袜榨强中文字幕| av中文字幕在线观看第三页| 亚洲日本一区二区三区| 日本黄色特一级视频| gay gay男男瑟瑟在线网站| 国产精品日韩欧美一区二区| 91chinese在线视频| 999久久久久999| 高清成人av一区三区| 亚洲天堂有码中文字幕视频| 日本成人一区二区不卡免费在线| 特级无码毛片免费视频播放| 视频啪啪啪免费观看| jiuse91九色视频| 久久香蕉国产免费天天| 91成人在线观看免费视频| 性色av一区二区三区久久久| av乱码一区二区三区| 人妻少妇av在线观看| 国产1区,2区,3区| 一级A一级a爰片免费免会员 | 欧美精品激情在线最新观看视频| 久久热这里这里只有精品| 国产在线一区二区三区麻酥酥| 日韩熟女系列一区二区三区| 在线免费观看日本伦理| 老师让我插进去69AV| nagger可以指黑人吗| 欧美成一区二区三区四区| 国产精品国产精品一区二区| japanese日本熟妇另类| 亚洲一级av无码一级久久精品| 久久丁香花五月天色婷婷| 日韩三级电影华丽的外出| 日韩一区二区三区三州| 天码人妻一区二区三区在线看| 十八禁在线观看地址免费| 亚洲综合在线观看免费| 91精品激情五月婷婷在线| 人妻少妇性色欲欧美日韩| 国产日韩av一区二区在线| 精品一线二线三线日本| 日韩中文字幕精品淫| 九色porny九色9l自拍视频| 国产精品久久久久网| 日本乱人一区二区三区| 91极品新人『兔兔』精品新作| 传媒在线播放国产精品一区| 亚洲在线一区二区欧美| 国产在线免费观看成人| 免费看美女脱光衣服的视频| 国产精品视频资源在线播放| 国产精彩对白一区二区三区| 综合精品久久久久97| 91自产国产精品视频| 亚洲一区二区久久久人妻| 欧美另类一区二区视频| 性感美女高潮视频久久久| 大尺度激情四射网站| 国产精品一区二区av国| 日本在线不卡免费视频| 亚洲欧美综合另类13p| mm131美女午夜爽爽爽| 欧美va亚洲va天堂va| 日本av熟女在线视频| 啪啪啪操人视频在线播放| 亚洲激情偷拍一区二区| 欧美另类一区二区视频| 99精品免费久久久久久久久a| 国产亚洲精品视频合集| 青青青国产免费视频| 55夜色66夜色国产精品站| 97精品成人一区二区三区| 伊人精品福利综合导航| 91精品国产观看免费| 天堂av在线官网中文| 老师让我插进去69AV| 中文字幕在线乱码一区二区| 老师啊太大了啊啊啊尻视频| 99热国产精品666| 狠狠操狠狠操免费视频| 福利午夜视频在线观看| 国产黄色片在线收看| 97欧洲一区二区精品免费| wwwxxx一级黄色片| 99精品国产自在现线观看| 77久久久久国产精产品| 久久久久只精品国产三级| 午夜精品在线视频一区| 日韩在线中文字幕色| 国产美女午夜福利久久| 在线视频国产欧美日韩| 亚洲国产精品免费在线观看| 香港三日本三韩国三欧美三级| 天天艹天天干天天操| 久久精品国产999| 亚洲精品乱码久久久久久密桃明| 蜜桃精品久久久一区二区| 超碰中文字幕免费观看| 精品国产高潮中文字幕| 91人妻人人做人人爽在线| 中文字幕av男人天堂| 特级无码毛片免费视频播放| 巨乳人妻日下部加奈被邻居中出| 天堂av中文在线最新版| 抽查舔水白紧大视频| 2021久久免费视频| jiuse91九色视频| 亚洲第一黄色在线观看| 一区二区三区欧美日韩高清播放| 三上悠亚和黑人665番号| 中文字幕日本人妻中出| 91精品视频在线观看免费| 亚洲综合一区成人在线| 亚洲av日韩精品久久久| 黄色成年网站午夜在线观看| 视频 一区二区在线观看| 日韩人妻xxxxx| 中英文字幕av一区| 97欧洲一区二区精品免费| 亚洲成人黄色一区二区三区| 大屁股肉感人妻中文字幕在线| 91免费黄片可看视频| 亚洲国产中文字幕啊啊啊不行了| 亚洲国产在线精品国偷产拍| 亚洲公开视频在线观看| 超碰97人人澡人人| 国产午夜亚洲精品麻豆| 亚洲公开视频在线观看| 免费人成黄页网站在线观看国产| 熟女人妻一区二区精品视频| 大香蕉大香蕉在线看| 九一传媒制片厂视频在线免费观看| av男人天堂狠狠干| 51国产偷自视频在线播放| av手机在线观播放网站| 中文字幕中文字幕人妻| 天天操天天操天天碰| 日本性感美女三级视频| 一区二区三区另类在线| 美女张开两腿让男人桶av| 日本三极片中文字幕| 亚洲一级美女啪啪啪| 在线观看国产免费麻豆| 亚洲乱码中文字幕在线| 欧美成人综合色在线噜噜| 国产亚洲国产av网站在线| 狠狠的往里顶撞h百合| 91亚洲手机在线视频播放| 女同性ⅹxx女同hd| 成年人黄色片免费网站| 成人精品视频99第一页| 黄色片一级美女黄色片| 日本熟妇丰满厨房55| 超pen在线观看视频公开97 | 在线观看av2025| mm131美女午夜爽爽爽| 亚洲av一妻不如妾| 天天躁夜夜躁日日躁a麻豆| 视频一区二区三区高清在线| 蜜桃视频17c在线一区二区| 伊拉克及约旦宣布关闭领空| 狠狠鲁狠狠操天天晚上干干| 欧美日韩精品永久免费网址| 国产亚洲视频在线二区| 爆乳骚货内射骚货内射在线 | 又大又湿又爽又紧A视频| 综合精品久久久久97| 视频 国产 精品 熟女 | 日本午夜久久女同精女女| 亚洲区欧美区另类最新章节| 国产片免费观看在线观看| 538精品在线观看视频| 日日日日日日日日夜夜夜夜夜夜| 丰满少妇翘臀后进式| 班长撕开乳罩揉我胸好爽| 天天通天天透天天插| 2021最新热播中文字幕| 亚洲另类图片蜜臀av| 国产janese在线播放| 婷婷综合亚洲爱久久| 五十路熟女人妻一区二区9933| 性感美女诱惑福利视频| 2020av天堂网在线观看| 日韩美女福利视频网| 91自产国产精品视频| 啪啪啪啪啪啪啪免费视频| 黄色三级网站免费下载| 91小伙伴中女熟女高潮| 欧美另类重口味极品在线观看| 中文字母永久播放1区2区3区| 99av国产精品欲麻豆| 男人的天堂在线黄色| jiujiure精品视频在线| 国产大鸡巴大鸡巴操小骚逼小骚逼| 欧美成一区二区三区四区| 日本熟妇色熟妇在线观看| 中国黄片视频一区91| 日本高清成人一区二区三区| 亚欧在线视频你懂的| 蜜臀av久久久久蜜臀av麻豆| 在线免费视频 自拍| 成人性黑人一级av| 青青青青青免费视频| 免费69视频在线看| 色爱av一区二区三区| 天天色天天操天天舔| 国产精品女邻居小骚货| 91久久人澡人人添人人爽乱| 日韩精品激情在线观看| 亚洲综合图片20p| 国产视频精品资源网站| 五十路熟女人妻一区二| 2017亚洲男人天堂| 99精品国产aⅴ在线观看| 中国黄片视频一区91| 中文字幕免费福利视频6| 特大黑人巨大xxxx| 大香蕉福利在线观看| 欧美色婷婷综合在线| 国产日韩精品免费在线| 中文字幕在线永久免费播放| 99热碰碰热精品a中文| 国产午夜亚洲精品麻豆| 啪啪啪啪啪啪啪啪av| asmr福利视频在线观看| 成人动漫大肉棒插进去视频| 搡老熟女一区二区在线观看| 国产精品系列在线观看一区二区| 久草视频在线看免费| 欧美视频中文一区二区三区| 美女av色播在线播放| 人妻少妇精品久久久久久| 日本高清撒尿pissing| 亚洲午夜高清在线观看| 美日韩在线视频免费看| 另类av十亚洲av| 精品成人啪啪18免费蜜臀| 黑人借宿ntr人妻的沦陷2| 夜色福利视频在线观看| 在线不卡日韩视频播放| 天干天天天色天天日天天射| 国产1区,2区,3区| 九色porny九色9l自拍视频| 中文字幕高清免费在线人妻 | 五月天色婷婷在线观看视频免费| 自拍偷拍亚洲另类色图| 午夜久久香蕉电影网| 国内精品在线播放第一页| 国产露脸对白在线观看| 国产chinesehd精品麻豆| 大鸡吧插逼逼视频免费看| 自拍偷拍亚洲欧美在线视频| 亚洲护士一区二区三区| 天天干狠狠干天天操| 夜夜嗨av蜜臀av| 特大黑人巨大xxxx| 顶级尤物粉嫩小尤物网站| gogo国模私拍视频| 红桃av成人在线观看| 一区二区视频在线观看免费观看 | 啊啊啊视频试看人妻| 欧美aa一级一区三区四区| 午夜dv内射一区区| 粗大的内捧猛烈进出爽大牛汉子| 亚洲va天堂va国产va久| 国内精品在线播放第一页| 亚洲一区二区三区精品乱码| 中文字幕人妻熟女在线电影| 9国产精品久久久久老师| 日本人妻欲求不满中文字幕| 日韩中文字幕精品淫| 亚洲激情,偷拍视频| 播放日本一区二区三区电影| av老司机亚洲一区二区| 在线视频国产欧美日韩| 亚洲av成人免费网站| 2019av在线视频| 在线观看av2025| 色婷婷久久久久swag精品| 人妻无码色噜噜狠狠狠狠色| 亚洲黄色av网站免费播放| 中文人妻AV久久人妻水| 一区二区视频在线观看视频在线 | 亚洲蜜臀av一区二区三区九色| 91国产资源在线视频| 91‖亚洲‖国产熟女| 日本真人性生活视频免费看| 国产超码片内射在线| 天天日天天干天天要| 女同久久精品秋霞网| 午夜福利资源综合激情午夜福利资| 大学生A级毛片免费视频| 66久久久久久久久久久| 一级黄片大鸡巴插入美女| 日韩精品中文字幕福利| 国产日韩精品电影7777| 亚洲午夜精品小视频| 亚洲图片欧美校园春色| 91国产资源在线视频| 综合精品久久久久97| 又大又湿又爽又紧A视频| 免费黄页网站4188| AV天堂一区二区免费试看| 国产精品入口麻豆啊啊啊| 超级碰碰在线视频免费观看| 91色老99久久九九爱精品| 特一级特级黄色网片| 国产精品一二三不卡带免费视频| 欧美交性又色又爽又黄麻豆| 青青社区2国产视频| 中国熟女一区二区性xx| aaa久久久久久久久| 久久农村老妇乱69系列| 亚洲第一黄色在线观看| 国产刺激激情美女网站| 美女骚逼日出水来了| 日韩美女综合中文字幕pp| 亚洲欧美国产综合777| 女同互舔一区二区三区| 国产一区成人在线观看视频| 成人资源在线观看免费官网| 国产精品人妻熟女毛片av久| 99久久成人日韩欧美精品| 成人免费公开视频无毒| 亚洲熟妇无码一区二区三区| 三上悠亚和黑人665番号| 色秀欧美视频第一页| 阿v天堂2014 一区亚洲| 国产一区二区欧美三区| av在线免费中文字幕| 好男人视频在线免费观看网站| 视频一区 视频二区 视频| 免费观看理论片完整版| 天堂中文字幕翔田av| 久碰精品少妇中文字幕av| 午夜影院在线观看视频羞羞羞| 色花堂在线av中文字幕九九| 3344免费偷拍视频| 欧洲精品第一页欧洲精品亚洲| 美女少妇亚洲精选av| 久久久久久久久久久免费女人| 天天干狠狠干天天操| 亚洲成人黄色一区二区三区 | 91 亚洲视频在线观看| 亚洲激情,偷拍视频| aⅴ精产国品一二三产品| 亚洲第一伊人天堂网| 欧美区一区二区三视频| 免费黄色成人午夜在线网站| 国产精品自拍在线视频| 天天射夜夜操综合网| 天天干天天操天天插天天日| 色综合天天综合网国产成人| 午夜精品福利一区二区三区p | 久草免费人妻视频在线| 亚洲成人激情视频免费观看了| 一色桃子久久精品亚洲| 2022国产精品视频| 岛国免费大片在线观看| 亚洲色偷偷综合亚洲AV伊人| 人妻在线精品录音叫床| 人妻另类专区欧美制服| 精品人人人妻人人玩日产欧| 阴茎插到阴道里面的视频| 一区二区三区日韩久久| 亚洲另类综合一区小说| 99热色原网这里只有精品| 天天日天天添天天爽| 欧美特色aaa大片| 2020久久躁狠狠躁夜夜躁| 精品黑人一区二区三区久久国产| 伊人精品福利综合导航| 亚洲一区av中文字幕在线观看| 淫秽激情视频免费观看| 国产精品视频男人的天堂| 日本一本午夜在线播放| 亚洲熟女久久久36d| 日韩视频一区二区免费观看| 大鸡巴操娇小玲珑的女孩逼| 国产高潮无码喷水AV片在线观看| 视频 国产 精品 熟女 | 一区二区三区视频,福利一区二区 丰满的子国产在线观看 | 999久久久久999| 亚洲精品三级av在线免费观看| 欧美亚洲偷拍自拍色图| 国产黄网站在线观看播放| 欧美一区二区三区在线资源 | 日日日日日日日日夜夜夜夜夜夜| 青娱乐蜜桃臀av色| 最新中文字幕乱码在线| 国产在线观看黄色视频| 久久尻中国美女视频| 综合国产成人在线观看| 姐姐的朋友2在线观看中文字幕| 青青擦在线视频国产在线| 狠狠躁狠狠爱网站视频| 欧美一级色视频美日韩| 欧美视频一区免费在线| 美女大bxxxx内射| 日比视频老公慢点好舒服啊| 91she九色精品国产| 年轻的人妻被夫上司侵犯| 久久精品国产23696| 亚洲成人av在线一区二区| 欧美伊人久久大香线蕉综合| 大香蕉日本伊人中文在线| 99re国产在线精品| 国产九色91在线视频| 老司机免费福利视频网| 老师啊太大了啊啊啊尻视频| 999九九久久久精品| 初美沙希中文字幕在线| 97超碰免费在线视频| 2020久久躁狠狠躁夜夜躁| 天天色天天舔天天射天天爽| 亚洲精品国品乱码久久久久 | 成人av亚洲一区二区| 美女小视频网站在线| 东游记中文字幕版哪里可以看到| 区一区二区三国产中文字幕| 国产自拍在线观看成人| 亚洲麻豆一区二区三区| 精品成人午夜免费看| 亚洲av无乱一区二区三区性色 | 精品区一区二区三区四区人妻| 日韩亚洲高清在线观看| 日韩加勒比东京热二区| 日韩亚国产欧美三级涩爱| 青青青视频手机在线观看| 免费大片在线观看视频网站| 中国黄色av一级片| 天天干天天日天天谢综合156| 在线成人日韩av电影| 日本一二三中文字幕| 成年人啪啪视频在线观看| 日韩av有码一区二区三区4| 2025年人妻中文字幕乱码在线| 99热国产精品666| 欧美精品中文字幕久久二区| 亚洲午夜在线视频福利| 顶级尤物粉嫩小尤物网站| 免费男阳茎伸入女阳道视频| 天天干天天插天天谢| AV无码一区二区三区不卡| 91色老99久久九九爱精品| 国产又粗又硬又大视频| 涩涩的视频在线观看视频| 少妇高潮一区二区三区| 最新欧美一二三视频| 国产a级毛久久久久精品| 色狠狠av线不卡香蕉一区二区| 99久久99久国产黄毛片| 欧美在线精品一区二区三区视频| 传媒在线播放国产精品一区| 欧美精品免费aaaaaa| 国产va在线观看精品| 欧美va亚洲va天堂va| 同居了嫂子在线播高清中文| 91精品国产黑色丝袜| 毛茸茸的大外阴中国视频| 国产又粗又猛又爽又黄的视频在线| 日本美女性生活一级片| 啊用力插好舒服视频| 男人和女人激情视频| 国产品国产三级国产普通话三级| 亚洲av日韩高清hd| 日韩一区二区三区三州| 哥哥姐姐综合激情小说| 99av国产精品欲麻豆| 成年人的在线免费视频| 亚洲国产在人线放午夜| 美女骚逼日出水来了| 国产精品久久久久久久精品视频| 麻豆性色视频在线观看| 久久久久久cao我的性感人妻| 国产福利在线视频一区| 蜜桃专区一区二区在线观看| 日韩人妻在线视频免费| 亚洲专区激情在线观看视频| 精品一区二区三区三区88| 国产亚洲视频在线二区| 热99re69精品8在线播放| 91九色porny国产蝌蚪视频| 国产精品一二三不卡带免费视频 | 日韩精品二区一区久久| 99精品国产自在现线观看| 成人网18免费视频版国产| 在线视频免费观看网| 老司机99精品视频在线观看| 美日韩在线视频免费看| 岛国毛片视频免费在线观看| 午夜91一区二区三区| 亚洲精品国品乱码久久久久| 香蕉91一区二区三区| 国产亚洲欧美另类在线观看| 极品丝袜一区二区三区| 91色九色porny| 亚洲成人情色电影在线观看| 亚洲欧美一区二区三区爱爱动图| 青青青国产免费视频| 免费在线观看视频啪啪| 巨乳人妻日下部加奈被邻居中出| 中文字幕 码 在线视频| 亚洲av可乐操首页| 亚洲青青操骚货在线视频| 日本www中文字幕| 春色激情网欧美成人| 天天插天天色天天日| 漂亮 人妻被中出中文| 亚洲伊人久久精品影院一美女洗澡 | 中文字幕第1页av一天堂网| 年轻的人妻被夫上司侵犯| 久久农村老妇乱69系列| 粗大的内捧猛烈进出爽大牛汉子| 欧洲国产成人精品91铁牛tv| 久草视频在线看免费| 可以免费看的www视频你懂的| 乱亲女秽乱长久久久| 亚洲成人精品女人久久久| 日韩欧美中文国产在线 | 欧美中文字幕一区最新网址| 一色桃子久久精品亚洲| 18禁美女黄网站色大片下载| 性色av一区二区三区久久久| 好吊视频—区二区三区| 亚洲av无女神免非久久| 亚洲精品午夜aaa久久| 鸡巴操逼一级黄色气| 日韩a级精品一区二区| 3344免费偷拍视频| 国产一区二区神马久久| 新婚人妻聚会被中出| 国产真实乱子伦a视频| 经典国语激情内射视频| 精品美女福利在线观看| 少妇人妻100系列| 午夜精品一区二区三区4| 亚洲欧美另类自拍偷拍色图| 最新欧美一二三视频| 免费大片在线观看视频网站| 亚洲精品亚洲人成在线导航| 中文字幕 亚洲av| 亚洲免费视频欧洲免费视频 | 北条麻妃肉色丝袜视频| 欧美色呦呦最新网址| 2021年国产精品自拍| 夜色17s精品人妻熟女| 久久久久久久久久一区二区三区| 91精品啪在线免费| 国产精品国产三级国产精东| 中文字幕亚洲久久久| 久久精品视频一区二区三区四区 | 78色精品一区二区三区| 亚洲人人妻一区二区三区| 日本少妇的秘密免费视频| 狠狠地躁夜夜躁日日躁| 免费看高清av的网站| 国产超码片内射在线| 丰满的子国产在线观看| 久久永久免费精品人妻专区 | 亚洲免费成人a v| 久久久久久九九99精品| 91精品国产综合久久久蜜 | 五月激情婷婷久久综合网| 天天躁日日躁狠狠躁躁欧美av| 久久丁香婷婷六月天| 91色网站免费在线观看| 91天堂精品一区二区| 日本韩国免费一区二区三区视频| 国产一区成人在线观看视频| 亚洲嫩模一区二区三区| 天天色天天爱天天爽| 国产精品福利小视频a| 99精品免费久久久久久久久a| 亚洲图片偷拍自拍区| 特级欧美插插插插插bbbbb| 摧残蹂躏av一二三区| 成人色综合中文字幕| 和邻居少妇愉情中文字幕| 午夜久久久久久久99| 日本成人一区二区不卡免费在线| 国产三级片久久久久久久| 这里有精品成人国产99| 大黑人性xxxxbbbb| 国产老熟女伦老熟妇ⅹ| 啊用力插好舒服视频| 国产成人精品久久二区91| 99热这里只有精品中文| 久久机热/这里只有| 欧美亚洲一二三区蜜臀| 福利视频广场一区二区| 日本少妇人妻xxxxxhd| 日韩欧美国产一区ab| 亚洲护士一区二区三区| 香蕉91一区二区三区| 亚洲蜜臀av一区二区三区九色 | 9l人妻人人爽人人爽| 日本中文字幕一二区视频| 青青青视频手机在线观看| 91九色porny蝌蚪国产成人| 色婷婷六月亚洲综合香蕉| 大鸡巴操娇小玲珑的女孩逼| 日韩美女综合中文字幕pp| 岛国av高清在线成人在线| 91九色porny国产蝌蚪视频| 蜜桃臀av蜜桃臀av| 亚洲成人国产av在线| 一区二区三区激情在线| 欧洲日韩亚洲一区二区三区| 日韩不卡中文在线视频网站| 激情综合治理六月婷婷| 在线观看免费岛国av| 国产精品三级三级三级| 天天日天天玩天天摸| 极品粉嫩小泬白浆20p主播| 日本成人不卡一区二区| www,久久久,com| 福利在线视频网址导航| 国产欧美精品不卡在线| 精内国产乱码久久久久久| 日韩美女综合中文字幕pp| 国产不卡av在线免费| 老司机福利精品免费视频一区二区| 特级欧美插插插插插bbbbb| 老熟妇凹凸淫老妇女av在线观看| 国产成人综合一区2区| 91老师蜜桃臀大屁股| 成熟熟女国产精品一区| 丰满少妇人妻xxxxx| 蜜桃视频17c在线一区二区| 一区二区三区av高清免费| 91人妻精品一区二区久久| 国际av大片在线免费观看| 91she九色精品国产| 亚洲一区二区三区久久受| 一区二区三区精品日本| 在线视频免费观看网| 美女少妇亚洲精选av| 在线不卡日韩视频播放| 红杏久久av人妻一区| gogo国模私拍视频| 日韩欧美国产精品91| ka0ri在线视频| 国产aⅴ一线在线观看| 99精品亚洲av无码国产另类| 亚洲一区二区人妻av| 欧美日韩激情啪啪啪| 快插进小逼里大鸡吧视频| 九九视频在线精品播放| 精品亚洲国产中文自在线| 成人30分钟免费视频| 91麻豆精品91久久久久同性| 久草视频福利在线首页| 日本免费一级黄色录像| 在线可以看的视频你懂的| 欧美 亚洲 另类综合| 亚洲精品乱码久久久本| 超级碰碰在线视频免费观看| 国产麻豆乱子伦午夜视频观看| 毛片一级完整版免费| 亚洲精品国产久久久久久| 欧美色呦呦最新网址| 亚洲欧美综合在线探花| 日本一本午夜在线播放| 国产精品视频欧美一区二区| 亚洲高清国产自产av| 在线国产中文字幕视频| 亚洲最大黄了色网站| 亚洲成人av一区在线| 亚洲一区二区三区精品视频在线| 日韩人妻xxxxx| 亚洲精品国品乱码久久久久| 只有精品亚洲视频在线观看| 综合激情网激情五月天| 宅男噜噜噜666国产| 夜夜嗨av一区二区三区中文字幕| av久久精品北条麻妃av观看| 欧美日本aⅴ免费视频| 神马午夜在线观看视频| 国产又粗又黄又硬又爽| 2017亚洲男人天堂| 天天日夜夜干天天操| 亚洲成人黄色一区二区三区 | 欧美香蕉人妻精品一区二区| 天天色天天舔天天射天天爽| aaa久久久久久久久| 一区二区三区国产精选在线播放| 人妻熟女中文字幕aⅴ在线| av完全免费在线观看av| 亚洲1069综合男同| 人妻熟女中文字幕aⅴ在线| 亚洲熟妇x久久av久久| av中文字幕在线导航| 大香蕉大香蕉大香蕉大香蕉大香蕉| 国产黄色片蝌蚪九色91| 欧美日本国产自视大全| 在线观看视频网站麻豆| 国产精品自拍在线视频| 亚洲另类综合一区小说| 免费在线福利小视频| 天堂v男人视频在线观看| 久久久久久性虐视频| av在线免费资源站| 欧美性感尤物人妻在线免费看| 天天插天天色天天日| 沈阳熟妇28厘米大战黑人| av老司机精品在线观看| 亚洲少妇高潮免费观看| 国产av一区2区3区| 亚洲狠狠婷婷综合久久app| 国产性生活中老年人视频网站| 亚洲嫩模一区二区三区| 人妻少妇亚洲精品中文字幕| 男人天堂最新地址av| 蜜桃精品久久久一区二区| 亚洲精品高清自拍av| 天天干天天日天天谢综合156| 国产三级片久久久久久久| 亚洲欧美综合在线探花| 免费在线福利小视频| 日韩二区视频一线天婷婷五| 夜色福利视频在线观看| 啪啪啪18禁一区二区三区| 亚洲一区自拍高清免费视频| 9国产精品久久久久老师| 日韩伦理短片在线观看| 亚洲一区二区三区av网站| 懂色av之国产精品| 人妻丝袜榨强中文字幕| 高清成人av一区三区| 亚洲一区制服丝袜美腿| 亚洲 中文字幕在线 日韩| 一级a看免费观看网站| 好了av中文字幕在线| 日韩av中文在线免费观看| 午夜免费观看精品视频| 亚洲日产av一区二区在线| 精产国品久久一二三产区区别| 日本熟妇丰满厨房55| 国产精选一区在线播放| 91九色porny国产蝌蚪视频| 99re久久这里都是精品视频| 国产精品伦理片一区二区| 日本男女操逼视频免费看 | 中文字幕在线欧美精品| 精品91自产拍在线观看一区| 白白操白白色在线免费视频| 在线免费视频 自拍| 最新激情中文字幕视频| 亚洲午夜伦理视频在线 | 狠狠的往里顶撞h百合| 一区二区熟女人妻视频| 风流唐伯虎电视剧在线观看| 粉嫩av蜜乳av蜜臀| 中文字幕午夜免费福利视频| 蜜桃视频在线欧美一区| 黄色视频在线观看高清无码 | 偷拍自拍亚洲美腿丝袜| av破解版在线观看| 六月婷婷激情一区二区三区| 男生用鸡操女生视频动漫| 老司机你懂得福利视频| 天天日天天天天天天天天天天| 九色porny九色9l自拍视频| 亚洲综合乱码一区二区| 青青青青青操视频在线观看| 国产精品黄大片在线播放| av男人天堂狠狠干| 色呦呦视频在线观看视频| 国产黄色大片在线免费播放| 18禁精品网站久久| 少妇被强干到高潮视频在线观看| 精品国产高潮中文字幕| 欧美在线精品一区二区三区视频| 亚洲粉嫩av一区二区三区| 亚洲av日韩av网站| 在线观看免费av网址大全| 护士小嫩嫩又紧又爽20p| 女同久久精品秋霞网| 99精品一区二区三区的区| 亚洲成高清a人片在线观看| 五月色婷婷综合开心网4438| 国内资源最丰富的网站| 熟女少妇激情五十路| 不戴胸罩引我诱的隔壁的人妻| 免费观看丰满少妇做受| 亚洲欧美自拍另类图片| 亚洲熟色妇av日韩熟色妇在线| 亚洲少妇高潮免费观看| 五十路老熟女码av| 女生自摸在线观看一区二区三区| 国产伊人免费在线播放| 中文字幕在线永久免费播放| 亚洲日本一区二区久久久精品| av在线资源中文字幕| 九色精品视频在线播放| 99热久久极品热亚洲| 亚洲综合另类精品小说| 午夜激情高清在线观看| 岛国av高清在线成人在线| 91 亚洲视频在线观看| 一色桃子久久精品亚洲 | 一区二区三区激情在线| 福利在线视频网址导航| 中文字幕第1页av一天堂网 | 无码中文字幕波多野不卡| 中文字幕第一页国产在线| 伊人成人综合开心网| 黄色三级网站免费下载| 老师啊太大了啊啊啊尻视频| 青青青青青青青青青青草青青| 久久久人妻一区二区| 国产成人精品午夜福利训2021| 91小伙伴中女熟女高潮| 沙月文乃人妻侵犯中文字幕在线| 40道精品招牌菜特色| 蜜桃久久久久久久人妻| 日韩人妻xxxxx| 青青青青青青青青青青草青青| 男人操女人的逼免费视频| 国产真实灌醉下药美女av福利| 久久久久久性虐视频| 天天射,天天操,天天说| 五色婷婷综合狠狠爱| 国产熟妇乱妇熟色T区| 久久精品国产999| 男人操女人的逼免费视频| 18禁网站一区二区三区四区| 插小穴高清无码中文字幕| 都市家庭人妻激情自拍视频| 亚洲 欧美 精品 激情 偷拍 | 日韩激情文学在线视频| 中文字幕高清资源站| 人妻少妇性色欲欧美日韩| 日本在线一区二区不卡视频| 91p0rny九色露脸熟女| 国产chinesehd精品麻豆| 青青青艹视频在线观看| 午夜精品亚洲精品五月色| 99热色原网这里只有精品| 国产精品一二三不卡带免费视频| 天天日夜夜操天天摸| 婷婷综合亚洲爱久久| 国产精品成人xxxx| 国产精彩对白一区二区三区| 国产性感美女福利视频| 午夜久久久久久久精品熟女| 久久久久久性虐视频| 国产极品美女久久久久久| 国产乱子伦精品视频潮优女| 亚洲伊人av天堂有码在线| 懂色av之国产精品| 亚洲欧美清纯唯美另类| 日韩a级黄色小视频| 亚洲一区二区三区在线高清 | 专门看国产熟妇的网站| 和邻居少妇愉情中文字幕| 一级黄片久久久久久久久| 国产日本欧美亚洲精品视| 精品一区二区亚洲欧美| 亚洲色偷偷综合亚洲AV伊人| 少妇高潮无套内谢麻豆| 亚洲一区自拍高清免费视频| 青青青青青青青在线播放视频| 青青草在观免费国产精品| 日本美女成人在线视频| 91亚洲国产成人精品性色| 国产精品国色综合久久| 啪啪啪啪啪啪啪啪av| 成年人啪啪视频在线观看| 秋霞午夜av福利经典影视| 欧美爆乳肉感大码在线观看| 亚洲一区av中文字幕在线观看| 亚洲色偷偷综合亚洲AV伊人 | 一区二区三区日本伦理| 四川五十路熟女av| 免费成人av中文字幕| 在线免费观看黄页视频| 搞黄色在线免费观看| 亚洲国产成人最新资源| 美女张开两腿让男人桶av| 白白操白白色在线免费视频| 可以免费看的www视频你懂的| 国产一区二区久久久裸臀| 国产亚洲国产av网站在线| 亚洲av无硬久久精品蜜桃| 午夜福利资源综合激情午夜福利资 | 91极品新人『兔兔』精品新作| 一二三中文乱码亚洲乱码one| av中文字幕电影在线看| 宅男噜噜噜666免费观看| 午夜成午夜成年片在线观看 | 国产精品成久久久久三级蜜臀av| 日韩欧美亚洲熟女人妻| 亚洲一区二区三区在线高清| 日本人竟这样玩学生妹| 免费福利av在线一区二区三区| 亚洲精品无码色午夜福利理论片| 岛国毛片视频免费在线观看| 一区二区三区蜜臀在线| 久久久久久国产精品| 喷水视频在线观看这里只有精品| 99精品视频在线观看免费播放 | 美女福利视频网址导航| 激情图片日韩欧美人妻| 综合色区亚洲熟妇shxstz| 天堂av狠狠操蜜桃| 超黄超污网站在线观看| 91极品大一女神正在播放 | 久久h视频在线观看| 日本熟妇一区二区x x| 亚洲人人妻一区二区三区| tube69日本少妇| 欧美精产国品一二三产品区别大吗| 黄色男人的天堂视频| 中文字幕成人日韩欧美| 黄色资源视频网站日韩| 97人妻总资源视频| 成年人黄视频在线观看| 日本韩国亚洲综合日韩欧美国产| 色花堂在线av中文字幕九九| av视屏免费在线播放| 亚洲天堂精品久久久| 国产白袜脚足J棉袜在线观看| 中文 成人 在线 视频| 天天日天天干天天舔天天射| 日韩一区二区电国产精品| 男人操女人的逼免费视频| 午夜成午夜成年片在线观看| 伊人综合免费在线视频| 任你操任你干精品在线视频| 亚洲精品国产综合久久久久久久久| 天天想要天天操天天干| 91国产资源在线视频| 成熟熟女国产精品一区| 国产午夜男女爽爽爽爽爽视频| 日本少妇在线视频大香蕉在线观看| 亚洲欧美一区二区三区电影| 黄色成人在线中文字幕| 2022国产精品视频| 涩爱综合久久五月蜜臀| 婷婷色国产黑丝少妇勾搭AV| 精品老妇女久久9g国产| 亚洲va国产va欧美精品88| 伊人成人综合开心网| 天天操天天爽天天干| 国产精品人妻66p| 中文字幕乱码av资源| 97超碰免费在线视频| aaa久久久久久久久| 天天操天天操天天碰| 大香蕉伊人国产在线| 日日操夜夜撸天天干| 精品视频中文字幕在线播放| 亚洲熟女久久久36d| 99热碰碰热精品a中文| 91精品高清一区二区三区| 亚洲的电影一区二区三区 | 国产丰满熟女成人视频| 91精品激情五月婷婷在线| 中文字幕 码 在线视频| 久久久久久久久久久久久97| 国产日韩精品电影7777| 天天操天天操天天碰| 激情图片日韩欧美人妻| 天天日天天天天天天天天天天| 青青草在观免费国产精品| 888亚洲欧美国产va在线播放| 国产亚洲视频在线二区| 日本男女操逼视频免费看| 天天日天天爽天天爽| 黄色片黄色片wyaa| 国产一线二线三线的区别在哪| 高潮喷水在线视频观看| 天天日天天天天天天天天天天| 偷拍3456eee| 久久久极品久久蜜桃| 亚洲国产美女一区二区三区软件| 色偷偷伊人大杳蕉综合网 | 91天堂天天日天天操| 天天色天天操天天舔| 日韩欧美中文国产在线| 东京干手机福利视频| 无码中文字幕波多野不卡| 全国亚洲男人的天堂| 午夜场射精嗯嗯啊啊视频| 激情国产小视频在线| 久久免看30视频口爆视频| 色呦呦视频在线观看视频| 又色又爽又黄的美女裸体| 天天干夜夜操啊啊啊| 美味人妻2在线播放| 亚洲精品午夜aaa久久| 99精品视频之69精品视频 | 亚洲视频在线观看高清| 91 亚洲视频在线观看| 亚洲精品成人网久久久久久小说| 99精品视频在线观看免费播放 | 十八禁在线观看地址免费| 操日韩美女视频在线免费看| 一区二区三区av高清免费| 国产精品国产三级麻豆| 国产一线二线三线的区别在哪 | 午夜久久香蕉电影网| 国产真实乱子伦a视频| 国产使劲操在线播放| 青青操免费日综合视频观看| 天天操天天射天天操天天天| 伊人成人综合开心网| sw137 中文字幕 在线| 538精品在线观看视频| 99精品国产aⅴ在线观看 | 老鸭窝在线观看一区| 亚洲少妇人妻无码精品| 国产av自拍偷拍盛宴| 日韩中文字幕精品淫| 97欧洲一区二区精品免费| 精品视频一区二区三区四区五区| 韩国爱爱视频中文字幕| 熟妇一区二区三区高清版| 亚洲午夜精品小视频| 亚洲成人午夜电影在线观看| 中字幕人妻熟女人妻a62v网| 老司机欧美视频在线看| 人妻少妇亚洲精品中文字幕| 国产精品探花熟女在线观看| 亚洲av日韩av网站| 国产又色又刺激在线视频 | 日本女大学生的黄色小视频| 免费观看理论片完整版| 精品国产高潮中文字幕| 女同久久精品秋霞网| 亚洲在线观看中文字幕av| 国产亚洲天堂天天一区| 国产揄拍高清国内精品对白| av中文字幕电影在线看| 人妻久久久精品69系列| 天天干天天搞天天摸| 2018在线福利视频| 中出中文字幕在线观看 | 91香蕉成人app下载| 亚洲无线观看国产高清在线| 成人亚洲精品国产精品| 成年人的在线免费视频| 亚洲国产中文字幕啊啊啊不行了| 97精品视频在线观看| 成人蜜桃美臀九一一区二区三区| 自拍偷拍 国产资源| 亚洲成av人无码不卡影片一| 社区自拍揄拍尻屁你懂的| 亚洲av无乱一区二区三区性色| 亚洲国产40页第21页| 美女福利写真在线观看视频| 9国产精品久久久久老师| 99国产精品窥熟女精品| 丝袜肉丝一区二区三区四区在线| 亚洲高清国产拍青青草原| 日本一二三中文字幕| 在线观看免费av网址大全| 2022国产综合在线干| 国产成人精品福利短视频| 人妻久久久精品69系列| 2022精品久久久久久中文字幕| 日日操夜夜撸天天干| 一区二区熟女人妻视频| 一区二区久久成人网| 狠狠的往里顶撞h百合| av森泽佳奈在线观看| 欧美黑人与人妻精品| 视频啪啪啪免费观看| 五月天色婷婷在线观看视频免费| 偷拍美女一区二区三区| 天天做天天干天天操天天射| 999九九久久久精品| 国产janese在线播放| 深田咏美亚洲一区二区| 在线观看操大逼视频| 一个人免费在线观看ww视频| 97超碰免费在线视频| av线天堂在线观看| 欧美国品一二三产区区别| 中文字幕一区的人妻欧美日韩| 1区2区3区不卡视频| 国产成人自拍视频在线免费观看| 欧美交性又色又爽又黄麻豆| 亚洲美女自偷自拍11页| 亚洲福利精品视频在线免费观看 | 最新的中文字幕 亚洲| 亚洲高清视频在线不卡| 黄页网视频在线免费观看| 直接能看的国产av| 后入美女人妻高清在线| 亚洲精品一区二区三区老狼| 亚洲国产中文字幕啊啊啊不行了| 巨乳人妻日下部加奈被邻居中出| 亚洲国产成人最新资源| 亚洲 国产 成人 在线| 亚洲天堂精品福利成人av| 成人av免费不卡在线观看| 欧美成人黄片一区二区三区| 国产午夜亚洲精品麻豆| 欧美亚洲偷拍自拍色图| 九九热99视频在线观看97| 国产精品视频欧美一区二区| 日本免费午夜视频网站| 精品国产午夜视频一区二区| 夜色撩人久久7777| 日本高清在线不卡一区二区| 亚洲国产在线精品国偷产拍| 国产高清女主播在线| 国产伊人免费在线播放| 免费在线观看污污视频网站| 青青在线视频性感少妇和隔壁黑丝| 漂亮 人妻被中出中文| 国语对白xxxx乱大交| 成人24小时免费视频| 亚洲一区二区三区在线高清 | 亚洲熟色妇av日韩熟色妇在线| 国产一级精品综合av| 精品91高清在线观看| 少妇高潮无套内谢麻豆| av一本二本在线观看| 亚洲伊人久久精品影院一美女洗澡| 亚洲国产40页第21页| 无码中文字幕波多野不卡| 日韩av中文在线免费观看| av在线免费中文字幕| 亚洲2021av天堂| 精品区一区二区三区四区人妻| 综合一区二区三区蜜臀| 一色桃子久久精品亚洲| 97国产在线av精品| 国产欧美精品一区二区高清| 久久这里有免费精品| 日本黄在免费看视频| 91免费观看国产免费| 日韩美女综合中文字幕pp| 天天操天天干天天插| 亚洲av成人网在线观看| 视频二区在线视频观看| 久久久久五月天丁香社区| 日本18禁久久久久久| 91亚洲精品干熟女蜜桃频道| 男人操女人逼逼视频网站| 天天日天天天天天天天天天天| 又大又湿又爽又紧A视频| 一区二区三区麻豆福利视频| 亚洲成人情色电影在线观看| 护士小嫩嫩又紧又爽20p| 韩国亚洲欧美超一级在线播放视频| 欧美成人小视频在线免费看| av在线shipin| 久久精品国产23696| 大鸡巴操娇小玲珑的女孩逼| 91九色porny国产蝌蚪视频| 中文字幕乱码人妻电影| 2020久久躁狠狠躁夜夜躁| 99热这里只有国产精品6| 熟女人妻三十路四十路人妻斩| 亚洲成人激情av在线| 欧美va亚洲va天堂va| 中文字幕中文字幕人妻| 伊人精品福利综合导航| 天堂av狠狠操蜜桃| 天天摸天天干天天操科普| 成年女人免费播放视频| 日韩精品激情在线观看| 丝袜国产专区在线观看| av高潮迭起在线观看| 亚洲中文字字幕乱码| 亚洲成人国产av在线| 97国产在线观看高清| 啊慢点鸡巴太大了啊舒服视频| 最新国产精品拍在线观看| 又粗又长 明星操逼小视频| 午夜毛片不卡免费观看视频 | av高潮迭起在线观看| 青青在线视频性感少妇和隔壁黑丝 | 日曰摸日日碰夜夜爽歪歪| 日韩精品中文字幕在线| 亚洲av日韩av网站| 91试看福利一分钟| 人妻丝袜av在线播放网址| 在线制服丝袜中文字幕| 99热久久这里只有精品| 亚洲久久午夜av一区二区| 亚洲欧美激情国产综合久久久| 久久久久只精品国产三级| 色婷婷久久久久swag精品| 色花堂在线av中文字幕九九| 这里只有精品双飞在线播放| 亚洲 欧美 精品 激情 偷拍| 黄色视频成年人免费观看| 五十路息与子猛烈交尾视频| av日韩在线免费播放| 中文字幕综合一区二区| 538精品在线观看视频| 国产精品sm调教视频| 亚洲中文字幕乱码区| 亚洲少妇高潮免费观看| 亚洲国产免费av一区二区三区 | 无码精品一区二区三区人| 11久久久久久久久久久| 中文字幕中文字幕 亚洲国产| 午夜影院在线观看视频羞羞羞| 亚洲欧美另类自拍偷拍色图| chinese国产盗摄一区二区| 骚货自慰被发现爆操| 国产亚洲视频在线二区| 偷拍美女一区二区三区| 视频二区在线视频观看| 亚洲人人妻一区二区三区| 日本三极片视频网站观看| 在线免费观看99视频| 亚洲精品亚洲人成在线导航| 国产精品成人xxxx| 区一区二区三国产中文字幕| 欧美成人精品在线观看| 国产免费高清视频视频| 97成人免费在线观看网站| 99久久激情婷婷综合五月天| 青青青国产片免费观看视频| 亚洲av一妻不如妾| 香港一级特黄大片在线播放 | 免费在线看的黄片视频| 亚洲精品国品乱码久久久久| 久草电影免费在线观看| 亚洲美女美妇久久字幕组| 欧美va亚洲va天堂va| 91精品激情五月婷婷在线| 亚洲av无女神免非久久| 内射久久久久综合网| 日韩a级精品一区二区| 国产午夜激情福利小视频在线| 熟女在线视频一区二区三区| 女蜜桃臀紧身瑜伽裤| 成年午夜影片国产片| 欧美亚洲免费视频观看| 69精品视频一区二区在线观看| 超碰在线中文字幕一区二区| 国产精品伦理片一区二区| 3337p日本欧洲大胆色噜噜| 乱亲女秽乱长久久久| 国产中文字幕四区在线观看| 全国亚洲男人的天堂| 婷婷久久一区二区字幕网址你懂得| xxx日本hd高清| 午夜激情精品福利视频| 亚洲av日韩精品久久久久久hd| 亚洲另类综合一区小说| 国产又粗又猛又爽又黄的视频美国| 中文字幕在线欧美精品| 在线观看日韩激情视频| 综合激情网激情五月天| 国产麻豆乱子伦午夜视频观看| 日韩欧美制服诱惑一区在线| 免费在线观看视频啪啪| 国产精品人久久久久久| 天天摸天天日天天操| 日视频免费在线观看| 又色又爽又黄的美女裸体| 五十路老熟女码av| 无套猛戳丰满少妇人妻| 91中文字幕免费在线观看| 久久综合老鸭窝色综合久久 | japanese日本熟妇另类| 第一福利视频在线观看| 亚洲激情偷拍一区二区| 欧美女同性恋免费a| 一二三中文乱码亚洲乱码one| 色花堂在线av中文字幕九九 | 99热久久这里只有精品8| 青青青青青青青在线播放视频| 亚洲免费国产在线日韩| 日韩三级黄色片网站| 久久综合老鸭窝色综合久久| jiuse91九色视频| 亚洲欧美激情国产综合久久久| 天天日夜夜操天天摸| 大鸡巴操娇小玲珑的女孩逼| 11久久久久久久久久久| 91免费观看国产免费| 啪啪啪操人视频在线播放| 啊用力插好舒服视频| 欧美日韩中文字幕欧美| 亚洲一区制服丝袜美腿| 久精品人妻一区二区三区| 亚洲精品国偷自产在线观看蜜桃| 国产成人自拍视频播放| 98视频精品在线观看| 久草视频在线免播放| 大屁股熟女一区二区三区| 国产免费av一区二区凹凸四季| 年轻的人妻被夫上司侵犯| 欧美视频综合第一页| aaa久久久久久久久| 一区二区在线观看少妇| 亚洲精品午夜aaa久久| 黑人变态深video特大巨大| 快插进小逼里大鸡吧视频| 3344免费偷拍视频| 嫩草aⅴ一区二区三区| 欧美香蕉人妻精品一区二区| 中国产一级黄片免费视频播放| 精品国产亚洲av一淫| 日本xx片在线观看| 在线观看欧美黄片一区二区三区 | 动漫精品视频在线观看| 97国产在线观看高清| 亚洲综合自拍视频一区| 成年人啪啪视频在线观看| 91免费黄片可看视频| 夜夜嗨av一区二区三区中文字幕| 美女骚逼日出水来了| 99国产精品窥熟女精品| 亚洲av日韩av网站| 亚洲伊人久久精品影院一美女洗澡 | 精品人妻每日一部精品| 亚洲日产av一区二区在线| 日韩熟女系列一区二区三区| 第一福利视频在线观看| 亚洲av在线观看尤物| 黄页网视频在线免费观看| 偷青青国产精品青青在线观看 | 日韩中文字幕福利av| 中国黄片视频一区91| 日本熟女50视频免费| 欧美精品国产综合久久| 男人的网址你懂的亚洲欧洲av| 青青草国内在线视频精选| 五十路熟女人妻一区二| 美女av色播在线播放| 国产 在线 免费 精品| 黄色av网站免费在线| 97黄网站在线观看| 亚洲国产精品中文字幕网站| 亚洲高清视频在线不卡| 久久午夜夜伦痒痒想咳嗽P| 中文字幕1卡1区2区3区| 性感美女诱惑福利视频| 亚洲推理片免费看网站| 国产精品探花熟女在线观看| 性生活第二下硬不起来| 91传媒一区二区三区| 久久精品视频一区二区三区四区| 国产麻豆国语对白露脸剧情| 日韩一个色综合导航| 午夜婷婷在线观看视频| 亚洲粉嫩av一区二区三区| 天天干天天搞天天摸| 天天日天天做天天日天天做| 日韩av熟妇在线观看| 91色老99久久九九爱精品| 激情综合治理六月婷婷| 青草亚洲视频在线观看| 天天操天天插天天色| 欧美视频一区免费在线| 天天做天天干天天舔| 三级av中文字幕在线观看| 熟女俱乐部一二三区| 色狠狠av线不卡香蕉一区二区| 91麻豆精品传媒国产黄色片| 五十路人妻熟女av一区二区| 亚洲欧美一区二区三区电影| 特级无码毛片免费视频播放| 国产三级精品三级在线不卡| 国产亚州色婷婷久久99精品| 亚洲国产精品久久久久久6| 国产精品3p和黑人大战| 日韩欧美一级aa大片| 中文字幕AV在线免费看 | 性色av一区二区三区久久久| 欲乱人妻少妇在线视频裸| 国产精品成久久久久三级蜜臀av| 午夜影院在线观看视频羞羞羞| 欧美交性又色又爽又黄麻豆| 成人性爱在线看四区| 欧美特级特黄a大片免费| 大鸡巴插入美女黑黑的阴毛| 国产1区,2区,3区| 91精品国产高清自在线看香蕉网| 国产三级片久久久久久久| 好吊视频—区二区三区| 免费观看国产综合视频| 91超碰青青中文字幕| 国产日韩欧美美利坚蜜臀懂色| 日本美女成人在线视频| 亚洲免费视频欧洲免费视频| 日本一本午夜在线播放| 在线观看的黄色免费网站| 国产在线拍揄自揄视频网站| av大全在线播放免费| 五月激情婷婷久久综合网| 国产成人午夜精品福利| 91‖亚洲‖国产熟女| 成年人免费看在线视频| 91桃色成人网络在线观看| 风流唐伯虎电视剧在线观看| 又粗又硬又猛又爽又黄的| 四川乱子伦视频国产vip| 黄色大片免费观看网站| 国产综合视频在线看片| 爱爱免费在线观看视频| 国产精品系列在线观看一区二区| 国产精品黄色的av| 国产aⅴ一线在线观看| 成人国产激情自拍三区| 国产91嫩草久久成人在线视频| 免费在线看的黄片视频| 青青草原色片网站在线观看| 日本熟妇一区二区x x| 亚洲欧美激情国产综合久久久 | 午夜精品一区二区三区福利视频| 2021国产一区二区| 91精品国产观看免费| 免费手机黄页网址大全| 青青青青视频在线播放| 午夜精品一区二区三区4| 成人综合亚洲欧美一区| 视频在线亚洲一区二区| 国产在线自在拍91国语自产精品| 国产麻豆精品人妻av| 男生用鸡操女生视频动漫| 日韩美女搞黄视频免费| 美女操逼免费短视频下载链接| 免费无码人妻日韩精品一区二区| 一色桃子人妻一区二区三区| 国产成人一区二区三区电影网站| 少妇人妻真实精品视频| 99精品国自产在线人| 午夜福利人人妻人人澡人人爽| 果冻传媒av一区二区三区| 色吉吉影音天天干天天操| 久久久制服丝袜中文字幕| 亚洲精品福利网站图片| 精品黑人一区二区三区久久国产| 经典亚洲伊人第一页| av在线观看网址av| 97人人妻人人澡人人爽人人精品| 女同互舔一区二区三区| 亚洲国产在线精品国偷产拍 | 色av色婷婷人妻久久久精品高清| 午夜成午夜成年片在线观看 | 在线观看视频一区麻豆| 中文字幕人妻一区二区视频 | 欧美精品免费aaaaaa| 日本熟女50视频免费| 日韩精品激情在线观看| 女同性ⅹxx女同h偷拍| 白白操白白色在线免费视频 | 人妻凌辱欧美丰满熟妇| 美女av色播在线播放| 少妇人妻100系列| 国产一级精品综合av| 日本五十路熟新垣里子| 亚洲欧美激情中文字幕| 毛片一级完整版免费| 都市激情校园春色狠狠| 狠狠操操操操操操操操操| 美洲精品一二三产区区别| 男生用鸡操女生视频动漫 | 国产使劲操在线播放| 国产丰满熟女成人视频| 女同互舔一区二区三区| 久久久久久久久久一区二区三区| 97少妇精品在线观看| 天天干天天搞天天摸| 国产欧美精品免费观看视频| 美女 午夜 在线视频| 精品一区二区三区三区88| 亚洲午夜电影之麻豆| 一区二区三区国产精选在线播放| 欲乱人妻少妇在线视频裸| 一区二区三区精品日本| 亚洲一区二区三区av网站| 欧美精品国产综合久久| 在线免费观看国产精品黄色| 五月婷婷在线观看视频免费| 欧美在线精品一区二区三区视频| 动色av一区二区三区| 99国内精品永久免费视频| 国产麻豆国语对白露脸剧情| 美女福利视频网址导航| 视频 一区二区在线观看| 亚洲 中文 自拍 另类 欧美| 午夜国产福利在线观看| 97精品视频在线观看| 亚洲综合在线视频可播放| 午夜久久久久久久99| 亚洲欧美激情国产综合久久久| 极品粉嫩小泬白浆20p主播| 熟女人妻一区二区精品视频| 国产成人精品一区在线观看| 欧美精品亚洲精品日韩在线| 玩弄人妻熟妇性色av少妇| 天天躁夜夜躁日日躁a麻豆| 精品国产午夜视频一区二区| 超碰97人人澡人人| 香蕉片在线观看av| 91免费放福利在线观看| 亚洲区欧美区另类最新章节| 夜夜操,天天操,狠狠操| 老师啊太大了啊啊啊尻视频| 国产熟妇一区二区三区av| 亚洲午夜伦理视频在线| 日韩精品二区一区久久| 1区2区3区4区视频在线观看| 成年午夜影片国产片| 韩国三级aaaaa高清视频| 中文字幕 亚洲av| 午夜在线一区二区免费| 自拍偷拍一区二区三区图片| 青青草人人妻人人妻| 在线免费观看靠比视频的网站| 午夜国产免费福利av| 晚上一个人看操B片| 91九色porny国产蝌蚪视频| 午夜精品在线视频一区| 亚洲高清自偷揄拍自拍| 成年美女黄网站18禁久久| 在线观看av亚洲情色| 成人综合亚洲欧美一区| 成人亚洲精品国产精品| 亚洲国产精品久久久久蜜桃| 97黄网站在线观看| 99人妻视频免费在线| 天天干天天操天天爽天天摸| 国产免费高清视频视频| 中文字幕一区的人妻欧美日韩| 沈阳熟妇28厘米大战黑人| 精品国产在线手机在线| 欧美日韩国产一区二区三区三州 | 国产极品美女久久久久久| 国产大学生援交正在播放| 欧美男人大鸡吧插女人视频| 国产成人精品一区在线观看 | 天堂资源网av中文字幕| 在线观看免费视频色97| 一区二区三区四区五区性感视频| 色在线观看视频免费的| 亚洲一级特黄特黄黄色录像片| 成人精品在线观看视频| 精品一区二区三区三区88 | 欧美精品 日韩国产| 欧美一区二区三区啪啪同性| 欧美黄片精彩在线免费观看| 青青草视频手机免费在线观看| 1区2区3区不卡视频| 红杏久久av人妻一区| 动色av一区二区三区| 中文字幕在线视频一区二区三区| 激情图片日韩欧美人妻| 国产密臀av一区二区三| 日韩精品电影亚洲一区| 97小视频人妻一区二区| 免费福利av在线一区二区三区| 中文字幕一区二区三区人妻大片| 老司机福利精品视频在线| 日视频免费在线观看| 日韩特级黄片高清在线看| 日日操综合成人av| 唐人色亚洲av嫩草| 可以免费看的www视频你懂的| 日曰摸日日碰夜夜爽歪歪| 18禁污污污app下载| 成人亚洲国产综合精品| 伊人综合aⅴ在线网| 少妇与子乱在线观看| 天天射夜夜操狠狠干| 91欧美在线免费观看| 日日日日日日日日夜夜夜夜夜夜| 国产精品成久久久久三级蜜臀av| 久久久久久久精品成人热| 一区二区三区激情在线| 男生舔女生逼逼视频| 最近的中文字幕在线mv视频| 欧美韩国日本国产亚洲| 国产亚州色婷婷久久99精品| 88成人免费av网站| 大鸡吧插入女阴道黄色片| 亚洲国产中文字幕啊啊啊不行了 | 在线视频自拍第三页| 国产亚洲精品欧洲在线观看| gogo国模私拍视频| 欧美va亚洲va天堂va| 天天射夜夜操综合网| av在线免费中文字幕| 日视频免费在线观看| 五月婷婷在线观看视频免费 | 一区二区三区久久中文字幕| 把腿张开让我插进去视频| 又粗又硬又猛又爽又黄的| 福利国产视频在线观看| 青青青青青免费视频| 亚洲 欧美 自拍 偷拍 在线| 久草极品美女视频在线观看| 精品一线二线三线日本| 精品国产成人亚洲午夜| 人妻激情图片视频小说| 中文字幕亚洲中文字幕| 狍和女人的王色毛片| 日本少妇人妻xxxxxhd| 亚洲激情偷拍一区二区| 日本乱人一区二区三区| 亚洲av自拍天堂网| 免费费一级特黄真人片| 中文字幕 人妻精品| 91精品国产综合久久久蜜| 日韩成人性色生活片| 人妻av无码专区久久绿巨人| 国产黑丝高跟鞋视频在线播放 | 人妻久久无码中文成人| 丰满少妇人妻xxxxx| 日本一道二三区视频久久| 97a片免费在线观看| 天天做天天爽夜夜做少妇| 2020中文字幕在线播放| 亚洲精品ww久久久久久| 欧美一级片免费在线成人观看| 亚洲欧美国产综合777| 激情伦理欧美日韩中文字幕| 国产自拍黄片在线观看| 日本三极片中文字幕| 伊人开心婷婷国产av| 五十路息与子猛烈交尾视频 | 中文字幕第三十八页久久| 国产日本精品久久久久久久 | 日本一二三区不卡无| 亚洲美女高潮喷浆视频| 日韩美女精品视频在线观看网站| 日本一区精品视频在线观看| 亚洲 中文字幕在线 日韩| av老司机亚洲一区二区| 激情五月婷婷免费视频| 最近中文2019年在线看| av天堂中文字幕最新| 日韩成人性色生活片| 国产又大又黄免费观看| 毛片av在线免费看| 国产精品女邻居小骚货| 不卡一不卡二不卡三| 国产伦精品一区二区三区竹菊| 传媒在线播放国产精品一区| 国产九色91在线视频| 老司机99精品视频在线观看| 欧美激情精品在线观看| 精内国产乱码久久久久久| 极品丝袜一区二区三区| 性色av一区二区三区久久久| 曰本无码人妻丰满熟妇啪啪| 美日韩在线视频免费看| 成人精品视频99第一页| 超碰在线中文字幕一区二区| 国产亚洲四十路五十路| 国产精品黄页网站视频| 成人网18免费视频版国产| 亚洲欧美一区二区三区爱爱动图| 日本丰满熟妇大屁股久久| 久草视频在线一区二区三区资源站 | 亚洲精品一线二线在线观看| 欧美日韩熟女一区二区三区| 大肉大捧一进一出好爽在线视频| 青青草在观免费国产精品| 日韩一个色综合导航| 国产性色生活片毛片春晓精品 | 国产精品一区二区三区蜜臀av| av中文字幕电影在线看| 啪啪啪操人视频在线播放| 91麻豆精品传媒国产黄色片| 日本丰满熟妇BBXBBXHD| 亚洲专区激情在线观看视频| 一区二区三区在线视频福利| 国产美女一区在线观看| 97精品综合久久在线| 青青草原网站在线观看| 经典av尤物一区二区| 久草免费人妻视频在线| 日韩中文字幕在线播放第二页 | 亚洲一级特黄特黄黄色录像片| 51国产偷自视频在线播放| 色综合久久五月色婷婷综合| 天天做天天干天天舔| 国产成人综合一区2区| 久久久久久9999久久久久| 超碰97免费人妻麻豆| 男人天堂色男人av| 99久久久无码国产精品性出奶水| 国产精品黄页网站视频| 亚洲av香蕉一区区二区三区犇| 日本性感美女写真视频| 在线免费观看靠比视频的网站| 日本最新一二三区不卡在线| 亚洲特黄aaaa片| 亚洲综合在线观看免费| 亚洲激情,偷拍视频| 做爰视频毛片下载蜜桃视频1| 中文字幕 亚洲av| 国产男女视频在线播放| 亚洲精品久久综合久| 天天干狠狠干天天操| 黄色视频成年人免费观看| 1000小视频在线| 最近中文2019年在线看| 可以免费看的www视频你懂的| 38av一区二区三区| 午夜精品福利91av| 亚洲国产精品黑丝美女| 亚洲1区2区3区精华液| 999九九久久久精品| av俺也去在线播放| 午夜精品一区二区三区福利视频| 日本脱亚入欧是指什么| 亚洲综合自拍视频一区| 中文字幕无码日韩专区免费| 在线制服丝袜中文字幕| 福利在线视频网址导航| 亚洲激情av一区二区| 四川乱子伦视频国产vip| 国产福利小视频大全| 青青草视频手机免费在线观看| 免费在线看的黄网站| 亚洲熟女女同志女同| 国产污污污污网站在线| 日韩美在线观看视频黄| heyzo蜜桃熟女人妻| 欧美日本国产自视大全| 亚洲第一黄色在线观看| 国产麻豆剧传媒精品国产av蜜桃| 中国黄色av一级片| 国产白嫩美女一区二区| 免费男阳茎伸入女阳道视频 | 2020久久躁狠狠躁夜夜躁| 91欧美在线免费观看| 亚洲欧美精品综合图片小说| 99亚洲美女一区二区三区| 在线免费观看欧美小视频| 瑟瑟视频在线观看免费视频| 人人妻人人爱人人草| 动漫av网站18禁| 亚洲av琪琪男人的天堂| 亚洲欧美一区二区三区爱爱动图| 午夜精品一区二区三区4| 99热色原网这里只有精品| 又色又爽又黄又刺激av网站| wwwxxx一级黄色片| 欧美80老妇人性视频| 黄片三级三级三级在线观看| 亚洲成人激情av在线| 亚洲欧美成人综合视频| 97年大学生大白天操逼| 亚洲精品色在线观看视频| 端庄人妻堕落挣扎沉沦| 18禁网站一区二区三区四区| 同居了嫂子在线播高清中文| 国产乱子伦精品视频潮优女| 综合激情网激情五月天| 国产品国产三级国产普通话三级| 国产精选一区在线播放| 999九九久久久精品| 五十路息与子猛烈交尾视频| 大学生A级毛片免费视频| 色偷偷伊人大杳蕉综合网 | 婷婷综合蜜桃av在线| 欧美一区二区三区激情啪啪啪| 亚洲图库另类图片区| 2012中文字幕在线高清| 中文字幕日韩精品日本| 懂色av之国产精品| 黄色大片免费观看网站| 日本啪啪啪啪啪啪啪| 中文字幕一区二 区二三区四区| 国产va精品免费观看| 国产一区自拍黄视频免费观看| 成年人午夜黄片视频资源| gay gay男男瑟瑟在线网站| 亚洲av一妻不如妾| 青青青国产片免费观看视频| 大胆亚洲av日韩av| 在线观看911精品国产| 不戴胸罩引我诱的隔壁的人妻| 护士小嫩嫩又紧又爽20p| 国产伦精品一区二区三区竹菊| 2021久久免费视频| 天天干夜夜操啊啊啊| 夏目彩春在线中文字幕| 2012中文字幕在线高清| 日本黄在免费看视频| 2020韩国午夜女主播在线| 亚洲精品ww久久久久久| 美女福利写真在线观看视频| 少妇露脸深喉口爆吞精| 天天日天天日天天射天天干| 日韩三级电影华丽的外出| 国产97在线视频观看| 青青青视频自偷自拍38碰| 午夜大尺度无码福利视频| 亚洲av在线观看尤物| 亚洲护士一区二区三区| 天天草天天色天天干| 久久99久久99精品影院| 都市激情校园春色狠狠| 亚洲综合色在线免费观看| 摧残蹂躏av一二三区| 中文字幕高清免费在线人妻| 五月天中文字幕内射| 五月精品丁香久久久久福利社| 日韩二区视频一线天婷婷五| 91一区精品在线观看| 大香蕉大香蕉在线有码 av| tube69日本少妇| 午夜国产免费福利av| 免费一级特黄特色大片在线观看| 98视频精品在线观看| 人妻丰满熟妇综合网| 丝袜肉丝一区二区三区四区在线| 日本韩国亚洲综合日韩欧美国产 | 狠狠的往里顶撞h百合| 国产精品3p和黑人大战| 在线观看视频网站麻豆| 2021天天色天天干| 一区二区在线视频中文字幕 | 顶级尤物粉嫩小尤物网站| 亚洲av黄色在线网站| 天天插天天狠天天操| 亚洲精品精品国产综合| 亚洲女人的天堂av| 国产精品成久久久久三级蜜臀av| 亚洲第一黄色在线观看| 中国熟女一区二区性xx| 日韩熟女系列一区二区三区| 天天干天天操天天爽天天摸| 免费黄色成人午夜在线网站| 国产在线一区二区三区麻酥酥| jul—619中文字幕在线| 一区二区视频在线观看免费观看| 亚洲国产香蕉视频在线播放| 91自产国产精品视频| 精产国品久久一二三产区区别 | 丝袜长腿第一页在线| 中文字幕日本人妻中出| 狠狠操操操操操操操操操| 黄色三级网站免费下载| 动漫精品视频在线观看| 无码国产精品一区二区高潮久久4| 亚洲一区二区三区久久午夜| 亚洲国产欧美国产综合在线 | 人妻久久久精品69系列| 欧美视频综合第一页| 午夜毛片不卡免费观看视频| 国产亚洲国产av网站在线| 午夜久久久久久久99| 中文人妻AV久久人妻水| 亚洲av无硬久久精品蜜桃| 天天日天天操天天摸天天舔| 欧美女同性恋免费a| 57pao国产一区二区| 天天色天天操天天舔| 日本又色又爽又黄又粗| av天堂资源最新版在线看| 女同互舔一区二区三区| 亚洲一区制服丝袜美腿| 日本少妇在线视频大香蕉在线观看| 国产密臀av一区二区三| 天天躁日日躁狠狠躁av麻豆| 美女福利写真在线观看视频| 中文字母永久播放1区2区3区| 亚洲国产成人最新资源| 国产一区二区在线欧美| 18禁无翼鸟成人在线| 天天射,天天操,天天说| 66久久久久久久久久久| 色综合天天综合网国产成人| 好吊操视频这里只有精品| 日日摸夜夜添夜夜添毛片性色av| 成人av久久精品一区二区| 91成人精品亚洲国产| 少妇人妻100系列| 19一区二区三区在线播放| 999九九久久久精品| 国产美女精品福利在线| 亚洲午夜电影之麻豆| 精品久久久久久高潮| 97精品综合久久在线| 久久久精品欧洲亚洲av| 久久久久久久精品成人热| 伊人情人综合成人久久网小说| 五十路av熟女松本翔子| 大香蕉大香蕉在线有码 av| 一区二区三区久久中文字幕| 成年人黄视频在线观看| 啪啪啪18禁一区二区三区| 香港三日本三韩国三欧美三级| 在线观看av观看av| 天天干天天操天天爽天天摸| lutube在线成人免费看| 日日日日日日日日夜夜夜夜夜夜| 精品一区二区三区三区88| 亚洲午夜伦理视频在线| 亚洲成人激情视频免费观看了| 啪啪啪啪啪啪啪免费视频| 成人动漫大肉棒插进去视频| 91九色国产熟女一区二区| 午夜精品久久久久久99热| 天天摸天天亲天天舔天天操天天爽| 精品视频一区二区三区四区五区 | 最新中文字幕乱码在线| 丝袜长腿第一页在线| 天天做天天爽夜夜做少妇| 成年女人免费播放视频| 午夜毛片不卡免费观看视频| 久久精品亚洲成在人线a| 又大又湿又爽又紧A视频| 国产精品久久久久久久久福交| 少妇高潮一区二区三区| 精品久久久久久高潮| 亚洲精品福利网站图片| 中文字幕一区二区亚洲一区| 天天通天天透天天插| 亚洲 中文 自拍 另类 欧美| 在线观看av观看av| 80电影天堂网官网| 国产精品视频资源在线播放| 免费黄页网站4188| 亚洲欧美成人综合在线观看| 夜夜操,天天操,狠狠操|