Android WifiDisplay分析二:Wifi display连接过程

Posted 魏长志

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android WifiDisplay分析二:Wifi display连接过程相关的知识,希望对你有一定的参考价值。

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[-]

  1. WifiDisplay之P2P的建立
  2. WifiDisplay之RTSP server的创建

这一章中我们来看Wifi Display连接过程的建立,包含P2P的部分和RTSP的部分,首先来大致看一下Wifi Display规范相关的东西。

 

HIDC: Human Interface Device Class  (遵循HID标准的设备类)
UIBC: User Input Back Channel  (UIBC分为两种,一种是Generic,包含鼠标、键盘等;另一种是HIDC,HID是一个规范,只有遵循HID的标准,都可以叫做HID设备,包含USB鼠标、键盘、蓝牙、红外等)
PES: Packetized Elementary Stream (数字电视基本码流)
HDCP: High-bandwidth Digital Content Protection  (加密方式,用于加密传输的MPEG2-TS流)
MPEG2-TS: Moving Picture Experts Group 2 Transport Stream   (Wifi display之间传输的是MPEG2-TS流)
RTSP: Real-Time Streaming Protocol     (Wifi display通过RTSP协议来交互两边的能力)
RTP: Real-time Transport Protocol        (Wifi display通过RTP来传输MPEG2-TS流)
Wi-Fi P2P: Wi-Fi Direct
TDLS: Tunneled Direct Link Setup        (另一种方式建立两台设备之间的直连,与P2P类似,但要借助一台AP)

 

另一种比较重要的概念是在Wifi Display中分为Source和Sink两种角色,如下图。Source是用于encode并输出TS流;Sink用于decode并显示TS流。相当于Server/Client架构中,Source就是Server,用于提供服务;Sink就是Client。当然,我们这篇文章主要介绍在Android上Wifi display Source的流程。

 

从上面的架构图我们可以看到,Wifi display是建立在TCP/UDP上面的应用层协议,L2链路层是通过P2P和TDLS两种方式建立,TDLS是optional的。在L2层建立连接后,Source就会在一个特定的port上listen,等待client的TCP连接。当与Client建立了TCP连接后,就会有M1~M7七个消息的交互,用户获取对方设备的能力,包括视频编码能力、Audio输出能力、是否支持HDCP加密等等。在获取这些能力之后,Source就会选择一种视频编码格式以及Audio格式用于这次会话当中。当一个RTSP会话建立后,双方就会决定出用于传输TS流的RTP port,RTP协议是基于UDP的。当这些都准备好后,Sink设备就会发送M7消息,也就是Play给Source,双方就可以开始传输数据了。

关于M1~M7是什么,我们后面再来介绍。首先我们来介绍在android WifiDisplay中如何建立P2P的连接。

 

WifiDisplay之P2P的建立

 

通过我们之间关于Wifi display的service启动以及enable的分析,我们知道当扫描到可用的设备后,就会显示在WifiDisplaySettings这个页面上,当我们选择其中一个后,就会开始P2P的建立了,首先到WifiDisplaySettings中的代码分析:

[java] view plain copy  

  1. private void pairWifiDisplay(WifiDisplay display)   
  2.     if (display.canConnect())   
  3.         mDisplayManager.connectWifiDisplay(display.getDeviceAddress());  
  4.       
  5.   


WifiDisplaySettings通过AIDL调用到DisplayManagerService的connectWifiDisplay方法,关于AIDL的调用过程这里不讲了,直接到DisplayManagerService的connectWifiDisplay方法来看:

[java] view plain copy  

  1. public void connectWifiDisplay(String address)   
  2.     if (address == null)   
  3.         throw new IllegalArgumentException("address must not be null");  
  4.       
  5.     mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,  
  6.             "Permission required to connect to a wifi display");  
  7.   
  8.     final long token = Binder.clearCallingIdentity();  
  9.     try   
  10.         synchronized (mSyncRoot)   
  11.             if (mWifiDisplayAdapter != null)   
  12.                 mWifiDisplayAdapter.requestConnectLocked(address);  
  13.               
  14.           
  15.      finally   
  16.         Binder.restoreCallingIdentity(token);  
  17.       
  18.   


首先做参数的检查,即MAC地址不能为空,然后做权限检查,调用这个方法的application必须要在manifest中声明有CONFIGURE_WIFI_DISPLAY权限,最后直接调用WifiDisplayAdapter的requestConnectLocked方法:

[java] view plain copy  

  1. public void requestConnectLocked(final String address)   
  2.     if (DEBUG)   
  3.         Slog.d(TAG, "requestConnectLocked: address=" + address);  
  4.       
  5.   
  6.     getHandler().post(new Runnable()   
  7.         @Override  
  8.         public void run()   
  9.             if (mDisplayController != null)   
  10.                 mDisplayController.requestConnect(address);  
  11.               
  12.           
  13.     );  
  14.   


这里比较简单,直接调用WifiDisplayController的requestConnect方法。前面都是直接的调用,最终做事情的还是WifiDisplayController。

[java] view plain copy  

  1. public void requestConnect(String address)   
  2.     for (WifiP2pDevice device : mAvailableWifiDisplayPeers)   
  3.         if (device.deviceAddress.equals(address))   
  4.             connect(device);  
  5.           
  6.       
  7.   
  8.   
  9. private void connect(final WifiP2pDevice device)   
  10.     if (mDesiredDevice != null  
  11.             && !mDesiredDevice.deviceAddress.equals(device.deviceAddress))   
  12.         if (DEBUG)   
  13.             Slog.d(TAG, "connect: nothing to do, already connecting to "  
  14.                     + describeWifiP2pDevice(device));  
  15.           
  16.         return;  
  17.       
  18.   
  19.     if (mConnectedDevice != null  
  20.             && !mConnectedDevice.deviceAddress.equals(device.deviceAddress)  
  21.             && mDesiredDevice == null)   
  22.         if (DEBUG)   
  23.             Slog.d(TAG, "connect: nothing to do, already connected to "  
  24.                     + describeWifiP2pDevice(device) + " and not part way through "  
  25.                     + "connecting to a different device.");  
  26.           
  27.         return;  
  28.       
  29.   
  30.     if (!mWfdEnabled)   
  31.         Slog.i(TAG, "Ignoring request to connect to Wifi display because the "  
  32.                 +" feature is currently disabled: " + device.deviceName);  
  33.         return;  
  34.       
  35.   
  36.     mDesiredDevice = device;  
  37.     mConnectionRetriesLeft = CONNECT_MAX_RETRIES;  
  38.     updateConnection();  
  39.   


requestConnect先从mAvaiableWifiDsiplayPeers中通过Mac地址找到所有连接的WifiP2pDevice,然后调用connect方法,在connect方法中会做一系列的判断,看首先是否有正在连接中或者断开中的设备,如果有就直接返回;再看有没有已经连接上的设备,如果有,也直接返回,然后赋值mDesiredDevice为这次要连接的设备,最后调用updateConnection来更新连接状态并发起连接。updateConnection的代码比较长,我们分段来分析:

[java] view plain copy  

  1. private void updateConnection()   
  2. n style="white-space:pre">  </span>//更新是否需要scan或者停止scan  
  3.     updateScanState();  
  4.   
  5. n style="white-space:pre">  </span>//如果有已经连接上的RemoteDisplay,先断开。这里先不看  
  6.     if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice)   
  7.           
  8.       
  9.   
  10.     // 接上面的一步,段开这个group  
  11.     if (mDisconnectingDevice != null)   
  12.         return// wait for asynchronous callback  
  13.       
  14.     if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice)   
  15.   
  16.       
  17.   
  18.     // 如果有正在连接的设备,先停止连接之前的设备  
  19.     if (mCancelingDevice != null)   
  20.         return// wait for asynchronous callback  
  21.       
  22.     if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice)   
  23.           
  24.       
  25.   
  26.     // 当断开之前的连接或者启动匿名GROUP时,这里就结束了  
  27.     if (mDesiredDevice == null)   
  28.   
  29.       
  30.   
  31.     // 开始连接,这是我们要看的重点  
  32.     if (mConnectedDevice == null && mConnectingDevice == null)   
  33.         Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);  
  34.   
  35.         mConnectingDevice = mDesiredDevice;  
  36.         WifiP2pConfig config = new WifiP2pConfig();  
  37.         WpsInfo wps = new WpsInfo();  
  38.         if (mWifiDisplayWpsConfig != WpsInfo.INVALID)   
  39.             wps.setup = mWifiDisplayWpsConfig;  
  40.          else if (mConnectingDevice.wpsPbcSupported())   
  41.             wps.setup = WpsInfo.PBC;  
  42.          else if (mConnectingDevice.wpsDisplaySupported())   
  43.             wps.setup = WpsInfo.KEYPAD;  
  44.          else   
  45.             wps.setup = WpsInfo.DISPLAY;  
  46.           
  47.         config.wps = wps;  
  48.         config.deviceAddress = mConnectingDevice.deviceAddress;  
  49.         config.groupOwnerIntent = WifiP2pConfig.MIN_GROUP_OWNER_INTENT;  
  50.   
  51.         WifiDisplay display = createWifiDisplay(mConnectingDevice);  
  52.         advertiseDisplay(display, null000);  
  53.   
  54.         final WifiP2pDevice newDevice = mDesiredDevice;  
  55.         mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener()   
  56.             @Override  
  57.             public void onSuccess()   
  58.                 Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);  
  59.   
  60.                 mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);  
  61.               
  62.   
  63.             @Override  
  64.             public void onFailure(int reason)   
  65.                 if (mConnectingDevice == newDevice)   
  66.                     Slog.i(TAG, "Failed to initiate connection to Wifi display: "  
  67.                             + newDevice.deviceName + ", reason=" + reason);  
  68.                     mConnectingDevice = null;  
  69.                     handleConnectionFailure(false);  
  70.                   
  71.               
  72.         );  
  73.         return;   
  74.     <span style="font-family: Arial, Helvetica, sans-serif;">   </span>  


这段函数比较长,我们先看我们需要的,剩下的后面再来分析。首先赋值给mConnectingDevice表示当前正在连接的设备,然后构造一个WifiP2pConfig对象,这个对象包含这次连接的设备的Mac地址、wps方式以及我们自己的GROUP_OWNER intent值,然后调用advertieseDisplay方法来通知WifiDisplayAdapter相关状态的改变,WifiDisplayAdapter会发送相应的broadcast出来,这是WifiDisplaySettings可以接收这些broadcast,然后在UI上更新相应的状态。关于advertieseDisplay的实现,我们后面再来分析。

 

接着看updateConnection,调用WifiP2pManager的connect方法去实现两台设备的P2P连接,具体过程可以参考前面介绍的P2P连接的文章。这里的onSuccess()并不是表示P2P已经建立成功,而只是表示这个发送命令到wpa_supplicant成功,所以在这里设置了一个连接超时的timeout,为30秒。当连接成功后,会发送WIFI_P2P_CONNECTION_CHANGED_ACTION的广播出来,接着回到WifiDisplayController看如何处理连接成功的broadcast:

[java] view plain copy  

  1.          else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION))   
  2.             NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(  
  3.                     WifiP2pManager.EXTRA_NETWORK_INFO);  
  4.             if (DEBUG)   
  5.                 Slog.d(TAG, "Received WIFI_P2P_CONNECTION_CHANGED_ACTION: networkInfo="  
  6.                         + networkInfo);  
  7.               
  8.   
  9.             handleConnectionChanged(networkInfo);  
  10.   
  11. private void handleConnectionChanged(NetworkInfo networkInfo)   
  12.     mNetworkInfo = networkInfo;  
  13.     if (mWfdEnabled && networkInfo.isConnected())   
  14.         if (mDesiredDevice != null || mWifiDisplayCertMode)   
  15.             mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, new GroupInfoListener()   
  16.                 @Override  
  17.                 public void onGroupInfoAvailable(WifiP2pGroup info)   
  18.                     if (DEBUG)   
  19.                         Slog.d(TAG, "Received group info: " + describeWifiP2pGroup(info));  
  20.                       
  21.   
  22.                     if (mConnectingDevice != null && !info.contains(mConnectingDevice))   
  23.                         Slog.i(TAG, "Aborting connection to Wifi display because "  
  24.                                 + "the current P2P group does not contain the device "  
  25.                                 + "we expected to find: " + mConnectingDevice.deviceName  
  26.                                 + ", group info was: " + describeWifiP2pGroup(info));  
  27.                         handleConnectionFailure(false);  
  28.                         return;  
  29.                       
  30.   
  31.                     if (mDesiredDevice != null && !info.contains(mDesiredDevice))   
  32.                         disconnect();  
  33.                         return;  
  34.                       
  35.   
  36.                     if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice)   
  37.                         Slog.i(TAG, "Connected to Wifi display: "  
  38.                                 + mConnectingDevice.deviceName);  
  39.   
  40.                         mHandler.removeCallbacks(mConnectionTimeout);  
  41.                         mConnectedDeviceGroupInfo = info;  
  42.                         mConnectedDevice = mConnectingDevice;  
  43.                         mConnectingDevice = null;  
  44.                         updateConnection();  
  45.                       
  46.                   
  47.             );  
  48.           
  49.       


当WifiDisplayController收到WIFI_P2P_CONNECTION_CHANGED_ACTION广播后,会调用handleConnectionChanged来获取当前P2P Group相关的信息,如果获取到的P2P Group信息里面没有mConnectingDevice或者mDesiredDevice的信息,则表示连接出错了,直接退出。如果当前连接信息与前面设置的mConnectingDevice一直,则表示连接P2P成功,这里首先会移除前面设置的连接timeout的callback,然后设置mConnectedDevice为当前连接的设备,并设置mConnectingDevice为空,最后调用updateConnection来更新连接状态信息。我们又回到updateConnection这个函数了,但这次进入的分支与之前连接请求的分支又不同了,我们来看代码:

[java] view plain copy  

  1. private void updateConnection()   
  2.      // 更新是否需要scan或者停止scan  
  3.      updateScanState();  
  4.   
  5.      // 如果有连接上的RemoteDisplay,这里先断开  
  6.      if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice)   
  7.   
  8.        
  9.   
  10.      // 接着上面的一步,先断开之前连接的设备  
  11.      if (mDisconnectingDevice != null)   
  12.          return// wait for asynchronous callback  
  13.        
  14.      if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice)   
  15.   
  16.        
  17.   
  18.      // 如果有正在连接的设备,先断开之前连接的设备  
  19.      if (mCancelingDevice != null)   
  20.          return// wait for asynchronous callback  
  21.        
  22.      if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice)   
  23.   
  24.        
  25.   
  26.      // 当断开之前的连接或者匿名GO时,这里就结束了  
  27.      if (mDesiredDevice == null)   
  28.   
  29.        
  30.   
  31.      // 如果有连接请求,则进入此  
  32.      if (mConnectedDevice == null && mConnectingDevice == null)   
  33.   
  34.        
  35.   
  36.      // 当连接上P2P后,就进入到此  
  37.      if (mConnectedDevice != null && mRemoteDisplay == null)   
  38.          Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);  
  39.          if (addr == null)   
  40.              Slog.i(TAG, "Failed to get local interface address for communicating "  
  41.                      + "with Wifi display: " + mConnectedDevice.deviceName);  
  42.              handleConnectionFailure(false);  
  43.              return// done  
  44.            
  45.   
  46.          mWifiP2pManager.setMiracastMode(WifiP2pManager.MIRACAST_SOURCE);  
  47.   
  48.          final WifiP2pDevice oldDevice = mConnectedDevice;  
  49.          final int port = getPortNumber(mConnectedDevice);  
  50.          final String iface = addr.getHostAddress() + ":" + port;  
  51.          mRemoteDisplayInterface = iface;  
  52.   
  53.          Slog.i(TAG, "Listening for RTSP connection on " + iface  
  54.                  + " from Wifi display: " + mConnectedDevice.deviceName);  
  55.   
  56.          mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener()   
  57.              @Override  
  58.              public void onDisplayConnected(Surface surface,  
  59.                      int width, int height, int flags, int session)   
  60.                  if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected)   
  61.                      Slog.i(TAG, "Opened RTSP connection with Wifi display: "  
  62.                              + mConnectedDevice.deviceName);  
  63.                      mRemoteDisplayConnected = true;  
  64.                      mHandler.removeCallbacks(mRtspTimeout);  
  65.   
  66.                      if (mWifiDisplayCertMode)   
  67.                          mListener.onDisplaySessionInfo(  
  68.                                  getSessionInfo(mConnectedDeviceGroupInfo, session));  
  69.                        
  70.   
  71.                      final WifiDisplay display = createWifiDisplay(mConnectedDevice);  
  72.                      advertiseDisplay(display, surface, width, height, flags);  
  73.                    
  74.                
  75.   
  76.              @Override  
  77.              public void onDisplayDisconnected()   
  78.                  if (mConnectedDevice == oldDevice)   
  79.                      Slog.i(TAG, "Closed RTSP connection with Wifi display: "  
  80.                              + mConnectedDevice.deviceName);  
  81.                      mHandler.removeCallbacks(mRtspTimeout);  
  82.                      disconnect();  
  83.                    
  84.                
  85.   
  86.              @Override  
  87.              public void onDisplayError(int error)   
  88.                  if (mConnectedDevice == oldDevice)   
  89.                      Slog.i(TAG, "Lost RTSP connection with Wifi display due to error "  
  90.                              + error + ": " + mConnectedDevice.deviceName);  
  91.                      mHandler.removeCallbacks(mRtspTimeout);  
  92.                      handleConnectionFailure(false);  
  93.                    
  94.                
  95.          , mHandler);  
  96.   
  97.          // Use extended timeout value for certification, as some tests require user inputs  
  98.          int rtspTimeout = mWifiDisplayCertMode ?  
  99.                  RTSP_TIMEOUT_SECONDS_CERT_MODE : RTSP_TIMEOUT_SECONDS;  
  100.   
  101.          mHandler.postDelayed(mRtspTimeout, rtspTimeout * 1000);  
  102.        
  103.    

 

到这里P2P的连接就算建立成功了,接下来就是RTSP的部分了

 

WifiDisplay之RTSP server的创建

这里首先设置MiracastMode,博主认为这部分应该放在enable WifiDisplay时,不知道Google为什么放在这里? 然后从GroupInfo中取出对方设备的IP地址,利用默认的CONTROL PORT构建mRemoteDisplayInterface,接着调用RemoteDisplay的listen方法去listen指定的IP和端口上面的TCP连接请求。最后会设置Rtsp的连接请求的timeout,当用于Miracast认证时是120秒,正常的使用中是30秒,如果在这么长的时间内没有收到Sink的TCP请求,则表示失败了。下面来看RemoteDisplay的listen的实现:

[java] view plain copy  

  1. public static RemoteDisplay listen(String iface, Listener listener, Handler handler)   
  2.     if (iface == null)   
  3.         throw new IllegalArgumentException("iface must not be null");  
  4.       
  5.     if (listener == null)   
  6.         throw new IllegalArgumentException("listener must not be null");  
  7.       
  8.     if (handler == null)   
  9.         throw new IllegalArgumentException("handler must not be null");  
  10.       
  11.   
  12.     RemoteDisplay display = new RemoteDisplay(listener, handler);  
  13.     display.startListening(iface);  
  14.     return display;  
  15.   


这里首先进行参数的检查,然后创建一个RemoteDisplay对象(这里不能直接创建RemoteDisplay对象,因为它的构造函数是private的),接着调用RemoteDisplay的startListening方法:

[java] view plain copy  

  1. private void startListening(String iface)   
  2.     mPtr = nativeListen(iface);  
  3.     if (mPtr == 0)   
  4.         throw new IllegalStateException("Could not start listening for "  
  5.                 + "remote display connection on \\"" + iface + "\\"");  
  6.       
  7.     mGuard.open("dispose");  
  8.   


nativeListen会调用JNI中的实现,相关代码在android_media_RemoteDisplay.cpp中。注意上面的mGuard是CloseGuard对象,是一种用于显示释放一些资源的机制。

[java] view plain copy  

  1. static jint nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr)   
  2.     ScopedUtfChars iface(env, ifaceStr);  
  3.   
  4.     sp<IServiceManager> sm = defaultServiceManager();  
  5.     sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(  
  6.             sm->getService(String16("media.player")));  
  7.     if (service == NULL)   
  8.         ALOGE("Could not obtain IMediaPlayerService from service manager");  
  9.         return 0;  
  10.       
  11.   
  12.     sp<NativeRemoteDisplayClient> client(new NativeRemoteDisplayClient(env, remoteDisplayObj));  
  13.     sp<IRemoteDisplay> display = service->listenForRemoteDisplay(  
  14.             client, String8(iface.c_str()));  
  15.     if (display == NULL)   
  16.         ALOGE("Media player service rejected request to listen for remote display '%s'.",  
  17.                 iface.c_str());  
  18.         return 0;  
  19.       
  20.   
  21.     NativeRemoteDisplay* wrapper = new NativeRemoteDisplay(display, client);  
  22.     return reinterpret_cast<jint>(wrapper);  
  23.   


上面的代码中先从ServiceManager中获取MediaPlayerService的Bpbinder引用,然后由传入的第二个参数remoteDisplayObj,也就是RemoteDisplay对象构造一个NativeRemoteDisplayClient,在framework中,我们经常看到像这样的用法,类似于设计模式中的包装模式,例如在framework中对Java层的BnBinder也是做了一层封装JavaBBinder。在NativeRemoteDisplayClient中通过JNI的反向调用,就可以直接回调RemoteDisplay中的一些函数,实现回调方法了,下面来看它的实现:

[java] view plain copy  

  1. class NativeRemoteDisplayClient : public BnRemoteDisplayClient   
  2. public:  
  3.     NativeRemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) :  
  4.             mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj))   
  5.       
  6.   
  7. protected:  
  8.     ~NativeRemoteDisplayClient()   
  9.         JNIEnv* env = AndroidRuntime::getJNIEnv();  
  10.         env->DeleteGlobalRef(mRemoteDisplayObjGlobal);  
  11.       
  12.   
  13. public:  
  14.     virtual void onDisplayConnected(const sp<IGraphicBufferProducer>& bufferProducer,  
  15.             uint32_t width, uint32_t height, uint32_t flags, uint32_t session)   
  16.         env->CallVoidMethod(mRemoteDisplayObjGlobal,  
  17.                 gRemoteDisplayClassInfo.notifyDisplayConnected,  
  18.                 surfaceObj, width, height, flags, session);  
  19.       
  20.   
  21.     virtual void onDisplayDisconnected()   
  22.   
  23.       
  24.   
  25.     virtual void onDisplayError(int32_t error)   
  26.   
  27.       
  28.   
  29. private:  
  30.     jobject mRemoteDisplayObjGlobal;  
  31.   
  32.     static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName)   
  33.   
  34.           
  35.       
  36. ;  


在NativeRemoteDisplayClient的构造函数中,把RemoteDisplay对象先保存到mRemoteDisplayObjGlobal中,可以看到上面主要实现了三个回调函数,onDisplayConnected、onDisplayDisconnected、onDisplayError,这三个回调函数对应到RemoteDisplay类的notifyDisplayConnected、notifyDisplayDisconnected和notifyDisplayError三个方法。接着回到nativeListen中,接着会调用MediaPlayerService的listenForRemoteDisplay方法去监听socket连接,这个方法是返回一个RemoteDisplay对象,当然经过binder的调用,最终返回到nativeListen的是BpRemoteDisplay对象,然后会由这个BpRemoteDisplay对象构造一个NativeRemoteDisplay对象并把它的指针地址返回给上层RemoteDisplay使用。

[java] view plain copy  

  1. class NativeRemoteDisplay   
  2. public:  
  3.     NativeRemoteDisplay(const sp<IRemoteDisplay>& display,  
  4.             const sp<NativeRemoteDisplayClient>& client) :  
  5.             mDisplay(display), mClient(client)   
  6.       
  7.   
  8.     ~NativeRemoteDisplay()   
  9.         mDisplay->dispose();  
  10.       
  11.   
  12.     void pause()   
  13.         mDisplay->pause();  
  14.       
  15.   
  16.     void resume()   
  17.         mDisplay->resume();  
  18.       
  19.   
  20. private:  
  21.     sp<IRemoteDisplay> mDisplay;  
  22.     sp<NativeRemoteDisplayClient> mClient;  
  23. ;  


来看一下这时Java层的RemoteDisplay和Native层RemoteDisplay之间的关系:

WifiDisplayController通过左边的一条线路关系去控制WifiDisplaySource,而WifiDisplaySource又通过右边一条线路关系去回调WifiDisplayController的一些方法。

 

接着来看MediaPlayerService的listenForRemoteDisplay方法:

[java] view plain copy  

  1. sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay(  
  2.         const sp<IRemoteDisplayClient>& client, const String8& iface)   
  3.     if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY"))   
  4.         return NULL;  
  5.       
  6.   
  7.  

    以上是关于Android WifiDisplay分析二:Wifi display连接过程的主要内容,如果未能解决你的问题,请参考以下文章

    Android下WiFiDisplay功能探究

    Android下WiFiDisplay功能探究

    android4.2 wifidisplay远程显示修改为保存文件

    android4.2 WifiDisplay远程显示修改为保存文件

    Android Wi-Fi/Cellular多网络通道绑定方案对比

    Android Wi-Fi/Cellular多网络通道绑定方案对比