• wifi display代码 分析


    转自:http://blog.csdn.net/lilian0118/article/details/23168531

    这一章中我们来看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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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, null, 0, 0, 0);  
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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
     
     在CODE上查看代码片派生到我的代码片
    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.     return new RemoteDisplay(client, iface.string());  
    8. }  

    首先进行权限的检查,然后创建一个RemoteDisplay对象(注意现在已经在C++层了),这里看RemoteDisplay.cpp文件。RemoteDisplay继承于BnRemoteDisplay,并实现BnRemoteDisplay中的一些方法,有兴趣的可以去看一下IRemoteDisplay的实现。接下来来看RemoteDisplay的构造函数:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. RemoteDisplay::RemoteDisplay(  
    2.         const sp<IRemoteDisplayClient> &client,  
    3.         const char *iface)  
    4.     : mLooper(new ALooper),  
    5.       mNetSession(new ANetworkSession) {  
    6.     mLooper->setName("wfd_looper");  
    7.   
    8.     mSource = new WifiDisplaySource(mNetSession, client);  
    9.     mLooper->registerHandler(mSource);  
    10.   
    11.     mNetSession->start();  
    12.     mLooper->start();  
    13.   
    14.     mSource->start(iface);  
    15. }  
    RemoteDisplay类包含三个比较重要的元素:ALooper、ANetworkSession、WifiDisplaySource。首先来看一下在Native层的类图:
     
    ALooper中会创建一个Thread,并且不断的进行Looper循环去收消息,并dispatch给WifiDisplaySource去处理消息。首先来看它的构造函数和setName以及registerHandler这三个方法:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. ALooper::ALooper()  
    2.     : mRunningLocally(false) {  
    3. }  
    4.   
    5. void ALooper::setName(const char *name) {  
    6.     mName = name;  
    7. }  
    8.   
    9. ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {  
    10.     return gLooperRoster.registerHandler(this, handler);  
    11. }  

    这三个方法都比较简单,我们看LooperRoster的registerHandler方法:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. ALooper::handler_id ALooperRoster::registerHandler(  
    2.         const sp<ALooper> looper, const sp<AHandler> &handler) {  
    3.     Mutex::Autolock autoLock(mLock);  
    4.   
    5.     if (handler->id() != 0) {  
    6.         CHECK(!"A handler must only be registered once.");  
    7.         return INVALID_OPERATION;  
    8.     }  
    9.   
    10.     HandlerInfo info;  
    11.     info.mLooper = looper;  
    12.     info.mHandler = handler;  
    13.     ALooper::handler_id handlerID = mNextHandlerID++;  
    14.     mHandlers.add(handlerID, info);  
    15.   
    16.     handler->setID(handlerID);  
    17.   
    18.     return handlerID;  
    19. }  

    这里为每一个注册的AHandler分配一个handlerID,并且把注册的AHandler保存在mHandlers列表中,后面使用时,就可以快速的通过HandlerID找到对应的AHandler以及ALooper了。注意这里HandlerInfo结构中的mLooper和mHander都是是wp,是一个弱引用,在使用中必须调用其promote方法获取sp指针才能使用。再回到RemoteDisplay的构造函数中看ALooper的start方法:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. status_t ALooper::start(  
    2.         bool runOnCallingThread, bool canCallJava, int32_t priority) {  
    3.     if (runOnCallingThread) {  
    4.   
    5.     }  
    6.   
    7.     Mutex::Autolock autoLock(mLock);  
    8.   
    9.     mThread = new LooperThread(this, canCallJava);  
    10.   
    11.     status_t err = mThread->run(  
    12.             mName.empty() ? "ALooper" : mName.c_str(), priority);  
    13.     if (err != OK) {  
    14.         mThread.clear();  
    15.     }  
    16.   
    17.     return err;  
    18. }  

    这里的runOnCallingThread会根据默认形参为false,所以会新建一个LooperThread来不断的做循环,LooperThread是继承于Thread,并实现它的readyToRun和threadLoop方法,在threadLoop方法中去调用ALooper的loop方法,代码如下:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. virtual bool threadLoop() {  
    2.     return mLooper->loop();  
    3. }  
    4.   
    5.  ALooper::loop() {  
    6. Event event;  
    7.   
    8. {  
    9.     Mutex::Autolock autoLock(mLock);  
    10.   
    11.     if (mEventQueue.empty()) {  
    12.         mQueueChangedCondition.wait(mLock);  
    13.         return true;  
    14.     }  
    15.     int64_t whenUs = (*mEventQueue.begin()).mWhenUs;  
    16.     int64_t nowUs = GetNowUs();  
    17.   
    18.     if (whenUs > nowUs) {  
    19.         int64_t delayUs = whenUs - nowUs;  
    20.         mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);  
    21.   
    22.         return true;  
    23.     }  
    24.   
    25.     event = *mEventQueue.begin();  
    26.     mEventQueue.erase(mEventQueue.begin());  
    27. }  
    28.   
    29. gLooperRoster.deliverMessage(event.mMessage);  
    30.   
    31. return true;  

    在loop方法中,不断的从mEventQueue取出消息,并dispatch给LooperRoster处理,mEventQueue是一个list链表,其元素都是Event结构,Event结构又包含消息处理的时间以及消息本身AMessage。再来看ALooperRoster的deliverMessage方法:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void ALooperRoster::deliverMessage(const sp<AMessage> &msg) {  
    2.     sp<AHandler> handler;  
    3.   
    4.     {  
    5.         Mutex::Autolock autoLock(mLock);  
    6.   
    7.         ssize_t index = mHandlers.indexOfKey(msg->target());  
    8.   
    9.         if (index < 0) {  
    10.             ALOGW("failed to deliver message. Target handler not registered.");  
    11.             return;  
    12.         }  
    13.   
    14.         const HandlerInfo &info = mHandlers.valueAt(index);  
    15.         handler = info.mHandler.promote();  
    16.   
    17.         if (handler == NULL) {  
    18.             ALOGW("failed to deliver message. "  
    19.                  "Target handler %d registered, but object gone.",  
    20.                  msg->target());  
    21.   
    22.             mHandlers.removeItemsAt(index);  
    23.             return;  
    24.         }  
    25.     }  
    26.   
    27.     handler->onMessageReceived(msg);  
    28. }  

    这里首先通过AMessage的target找到需要哪个AHandler处理,然后调用这个AHandler的onMessageReceived去处理这个消息。注意前面的info.mHandler.promote()用于当前AHandler的强引用指针,也可以用来判断当前AHandler是否还存活在。由前面的知识我们知道,这里会调用到WifiDisplaySource的onMessageReceived方法,至于这些消息如何被处理,我们后面再来分析。再回到RemoteDisplay的构造函数中,ANetworkSession用于处理与网络请求相关的工作,比如创建socket,从socket中收发数据,当然这些工作都是由WifiDisplaySource控制的,我们先来看ANetworkSession的构造方法和start方法:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. ANetworkSession::ANetworkSession()  
    2.     : mNextSessionID(1) {  
    3.     mPipeFd[0] = mPipeFd[1] = -1;  
    4. }  
    5.   
    6. status_t ANetworkSession::start() {  
    7.     if (mThread != NULL) {  
    8.         return INVALID_OPERATION;  
    9.     }  
    10.   
    11.     int res = pipe(mPipeFd);  
    12.     if (res != 0) {  
    13.         mPipeFd[0] = mPipeFd[1] = -1;  
    14.         return -errno;  
    15.     }  
    16.   
    17.     mThread = new NetworkThread(this);  
    18.   
    19.     status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO);  
    20.   
    21.     if (err != OK) {  
    22.         mThread.clear();  
    23.   
    24.         close(mPipeFd[0]);  
    25.         close(mPipeFd[1]);  
    26.         mPipeFd[0] = mPipeFd[1] = -1;  
    27.   
    28.         return err;  
    29.     }  
    30.   
    31.     return OK;  
    32. }  

    在start方法中,首先创建一个管道,这里创建的管理主要用于让ANetworkSession不断的做select循环,当有事务要处理时,就从select中跳出来处理,我们后面会分析到具体的代码。接着创建一个NetworkThread,NetworkThread也是继承于Thread,并实现threadLoop方法,在threadLoop方法中只是简单的调用ANetworkSession的threadLoop方法,我们来分析threadLoop方法:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void ANetworkSession::threadLoop() {  
    2.     fd_set rs, ws;  
    3.     FD_ZERO(&rs);  
    4.     FD_ZERO(&ws);  
    5.   
    6.     FD_SET(mPipeFd[0], &rs);  
    7.     int maxFd = mPipeFd[0];  
    8.   
    9.     {  
    10.         Mutex::Autolock autoLock(mLock);  
    11.   
    12.         for (size_t i = 0; i < mSessions.size(); ++i) {  
    13.             const sp<Session> &session = mSessions.valueAt(i);  
    14.   
    15.             int s = session->socket();  
    16.   
    17.             if (s < 0) {  
    18.                 continue;  
    19.             }  
    20.   
    21.             if (session->wantsToRead()) {  
    22.                 FD_SET(s, &rs);  
    23.                 if (s > maxFd) {  
    24.                     maxFd = s;  
    25.                 }  
    26.             }  
    27.   
    28.             if (session->wantsToWrite()) {  
    29.                 FD_SET(s, &ws);  
    30.                 if (s > maxFd) {  
    31.                     maxFd = s;  
    32.                 }  
    33.             }  
    34.         }  
    35.     }  
    36.   
    37.     int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */);  
    38.   
    39.     if (res == 0) {  
    40.         return;  
    41.     }  
    42.   
    43.     if (res < 0) {  
    44.         if (errno == EINTR) {  
    45.             return;  
    46.         }  
    47.   
    48.         ALOGE("select failed w/ error %d (%s)", errno, strerror(errno));  
    49.         return;  
    50.     }  
    51.   
    52. }  

    这个函数比较长,我们分段来看,首先看select前半段部分,首先将mPipeFd[0]作为select监听的一个fd。然后循环的从mSessions中取出各个子Session(Session即为一个回话,在RTSP中当双方连接好TCP连接,并交互完Setup以后,就表示一个回话建立成功了,在RTSP中,可以在一对Server & Client之间建立多个回话,用于传输不同的数据),并通过socket类型添加到ReadFd和WirteFd中,最后调用select去等待是否有可读或者可写的事件发生。mSessions是一个KeyedVector,保存所有的Session及其SessionID,方便查找。关于Session何时创建,如何创建,我们后面再来分析。
     
    接着回到RemoteDisplay的构造函数,再来分析WifiDisplaySource,WifiDisplaySource继承于AHandler,并实现其中的onMessageReceived方法用于处理消息。先来看WifiDisplaySource的构造函数:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. WifiDisplaySource::WifiDisplaySource(  
    2.         const sp<ANetworkSession> &netSession,  
    3.         const sp<IRemoteDisplayClient> &client,  
    4.         const char *path)  
    5.     : mState(INITIALIZED),  
    6.       mNetSession(netSession),  
    7.       mClient(client),  
    8.       mSessionID(0),  
    9.       mStopReplyID(0),  
    10.       mChosenRTPPort(-1),  
    11.       mUsingPCMAudio(false),  
    12.       mClientSessionID(0),  
    13.       mReaperPending(false),  
    14.       mNextCSeq(1),  
    15.       mUsingHDCP(false),  
    16.       mIsHDCP2_0(false),  
    17.       mHDCPPort(0),  
    18.       mHDCPInitializationComplete(false),  
    19.       mSetupTriggerDeferred(false),  
    20.       mPlaybackSessionEstablished(false) {  
    21.     if (path != NULL) {  
    22.         mMediaPath.setTo(path);  
    23.     }  
    24.   
    25.     mSupportedSourceVideoFormats.disableAll();  
    26.   
    27.     mSupportedSourceVideoFormats.setNativeResolution(  
    28.             VideoFormats::RESOLUTION_CEA, 5);  // 1280x720 p30  
    29.   
    30.     // Enable all resolutions up to 1280x720p30  
    31.     mSupportedSourceVideoFormats.enableResolutionUpto(  
    32.             VideoFormats::RESOLUTION_CEA, 5,  
    33.             VideoFormats::PROFILE_CHP,  // Constrained High Profile  
    34.             VideoFormats::LEVEL_32);    // Level 3.2  
    35. }  
    首先给一些变量出初始化处理,由默认形参我们知道path为空。接着去清空VideoFormats中所有的设置,并把1280*720p以上的所有分辨率打开。VideoFormats是用于与Sink回复的M3作比对用的,可以快速找出我们和Sink支持的分辨率以及帧率,作为回复M4消息用,也用作后续传输TS数据的格式。首先来看VideoFormats的构造函数:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. VideoFormats::VideoFormats() {  
    2.     memcpy(mConfigs, mResolutionTable, sizeof(mConfigs));  
    3.   
    4.     for (size_t i = 0; i < kNumResolutionTypes; ++i) {  
    5.         mResolutionEnabled[i] = 0;  
    6.     }  
    7.   
    8.     setNativeResolution(RESOLUTION_CEA, 0);  // default to 640x480 p60  
    9. }  

    mResolutionTable是按照Wifi Display 规范定义好的一个3*32数组,里面的元素是config_t类型:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. struct config_t {  
    2.     size_t width, height, framesPerSecond;  
    3.     bool interlaced;  
    4.     unsigned char profile, level;  
    5. };  

    config_t包含了长、宽、帧率、隔行视频、profile和H.264 level。然后在构造函数中,对mResolutionEnabled[]数组全部置为0,mResolutionEnabled数组有三个元素,分别对应CEA、VESA、HH被选取的位,如果在mConfigs数组中相应的格式被选取,就会置mResolutionEnabled对应的位为1;相反取消支持一种格式时,相应的位就被置为0。在来看setNativeResolution:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void VideoFormats::setNativeResolution(ResolutionType type, size_t index) {  
    2.     CHECK_LT(type, kNumResolutionTypes);  
    3.     CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));  
    4.   
    5.     mNativeType = type;  
    6.     mNativeIndex = index;  
    7.   
    8.     setResolutionEnabled(type, index);  
    9. }  

    首先做参数检查,检查输入的type和index是否合法,然后调用setResolutionEnabled去设置mResolutionEnabled和mConfigs中的相应的值:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void VideoFormats::setResolutionEnabled(  
    2.         ResolutionType type, size_t index, bool enabled) {  
    3.     CHECK_LT(type, kNumResolutionTypes);  
    4.     CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));  
    5.   
    6.     if (enabled) {  
    7.         mResolutionEnabled[type] |= (1ul << index);  
    8.         mConfigs[type][index].profile = (1ul << PROFILE_CBP);  
    9.         mConfigs[type][index].level = (1ul << LEVEL_31);  
    10.     } else {  
    11.         mResolutionEnabled[type] &= ~(1ul << index);  
    12.         mConfigs[type][index].profile = 0;  
    13.         mConfigs[type][index].level = 0;  
    14.     }  
    15. }  

    这里首先还是做参数的检查,由默认形参我们知道,enable是true,则设置mResolutionEnabled相应type中的对应格式为1,并设置mConfigs中的profile和level值为CBP和Level 3.1。这里设置640*480 p60是因为在Wifi Display规范中,这个格式是必须要强制支持的,在Miracast认证中,这种格式也会被测试到。然后回到WifiDisplaySource的构造函数中,接下来会调用setNativeResolution去设置当前系统支持的默认格式为1280*720 p30,并调用enableResolutionUpto去将1280*720 p30以上的格式都设置为支持:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void VideoFormats::enableResolutionUpto(  
    2.         ResolutionType type, size_t index,  
    3.         ProfileType profile, LevelType level) {  
    4.     size_t width, height, fps, score;  
    5.     bool interlaced;  
    6.     if (!GetConfiguration(type, index, &width, &height,  
    7.             &fps, &interlaced)) {  
    8.         ALOGE("Maximum resolution not found!");  
    9.         return;  
    10.     }  
    11.     score = width * height * fps * (!interlaced + 1);  
    12.     for (size_t i = 0; i < kNumResolutionTypes; ++i) {  
    13.         for (size_t j = 0; j < 32; j++) {  
    14.             if (GetConfiguration((ResolutionType)i, j,  
    15.                     &width, &height, &fps, &interlaced)  
    16.                     && score >= width * height * fps * (!interlaced + 1)) {  
    17.                 setResolutionEnabled((ResolutionType)i, j);  
    18.                 setProfileLevel((ResolutionType)i, j, profile, level);  
    19.             }  
    20.         }  
    21.     }  
    22. }  

    这里采用width * height * fps * (!interlaced + 1)的方式去计算一个score值,然后遍历所有的mResolutionTable中的值去检查是否计算到的值比当前score要高,如果大于当前score值,就将这种分辨率enable,并设置mConfigs中对应分辨率的profile和H.264 level为CHP和Level 3.2。到这里WifiDisplaySource的构造函数分析完了,接着回到RemoteDisplay构造函数中,它会调用WifiDisplaySource的start方法,参数是的"ip:rtspPort":
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. status_t WifiDisplaySource::start(const char *iface) {  
    2.     CHECK_EQ(mState, INITIALIZED);  
    3.   
    4.     sp<AMessage> msg = new AMessage(kWhatStart, id());  
    5.     msg->setString("iface", iface);  
    6.   
    7.     sp<AMessage> response;  
    8.     return PostAndAwaitResponse(msg, &response);  
    9. }  
    10.   
    11. static status_t PostAndAwaitResponse(  
    12.         const sp<AMessage> &msg, sp<AMessage> *response) {  
    13.     status_t err = msg->postAndAwaitResponse(response);  
    14.   
    15.     if (err != OK) {  
    16.         return err;  
    17.     }  
    18.   
    19.     if (response == NULL || !(*response)->findInt32("err", &err)) {  
    20.         err = OK;  
    21.     }  
    22.   
    23.     return err;  
    24. }  

    在start函数中,构造一个AMessage,消息种类是kWhatStart,id()返回在ALooperRoster注册的handlerID值,ALooperRoster通过handlerID值可以快速找到对应的AHandler,我们知道,这里的id()返回WifiDisplaySource这个AHander的id值,这个消息最终也会被WifiDisplaySource的onMessageReceived方法处理。首先来看AMessage的postAndAwaitResponse方法:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {  
    2.     return gLooperRoster.postAndAwaitResponse(this, response);  
    3. }  
    这里直接调用LooperRoster的postAndAwaitResponse方法,这里比较重要的是gLooperRoster在这里只是被extern引用:extern ALooperRoster gLooperRoster,其最终的声明和定义是在我们前面讲到的ALooper中。接着去看LooperRoster的postAndAwaitResponse方法:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. status_t ALooperRoster::postAndAwaitResponse(  
    2.         const sp<AMessage> &msg, sp<AMessage> *response) {  
    3.     Mutex::Autolock autoLock(mLock);  
    4.   
    5.     uint32_t replyID = mNextReplyID++;  
    6.   
    7.     msg->setInt32("replyID", replyID);  
    8.   
    9.     status_t err = postMessage_l(msg, /* delayUs */);  
    10.   
    11.     if (err != OK) {  
    12.         response->clear();  
    13.         return err;  
    14.     }  
    15.   
    16.     ssize_t index;  
    17.     while ((index = mReplies.indexOfKey(replyID)) < 0) {  
    18.         mRepliesCondition.wait(mLock);  
    19.     }  
    20.   
    21.     *response = mReplies.valueAt(index);  
    22.     mReplies.removeItemsAt(index);  
    23.   
    24.     return OK;  
    25. }  

    首先会为每个需要reply的消息赋予一个replyID,后面会根据这个replyID去mReplies找到对应的response。再来看postMessage_l的实现:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. status_t ALooperRoster::postMessage_l(  
    2.         const sp<AMessage> &msg, int64_t delayUs) {  
    3.     ssize_t index = mHandlers.indexOfKey(msg->target());  
    4.   
    5.     if (index < 0) {  
    6.         ALOGW("failed to post message '%s'. Target handler not registered.",  
    7.               msg->debugString().c_str());  
    8.         return -ENOENT;  
    9.     }  
    10.   
    11.     const HandlerInfo &info = mHandlers.valueAt(index);  
    12.   
    13.     sp<ALooper> looper = info.mLooper.promote();  
    14.   
    15.     if (looper == NULL) {  
    16.         ALOGW("failed to post message. "  
    17.              "Target handler %d still registered, but object gone.",  
    18.              msg->target());  
    19.   
    20.         mHandlers.removeItemsAt(index);  
    21.         return -ENOENT;  
    22.     }  
    23.   
    24.     looper->post(msg, delayUs);  
    25.   
    26.     return OK;  
    27. }  

    首先从mHandler数组中找到当前AMessage对应的ALooper,然后调用ALooper的post方法,来看一下实现:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {  
    2.     Mutex::Autolock autoLock(mLock);  
    3.   
    4.     int64_t whenUs;  
    5.     if (delayUs > 0) {  
    6.         whenUs = GetNowUs() + delayUs;  
    7.     } else {  
    8.         whenUs = GetNowUs();  
    9.     }  
    10.   
    11.     List<Event>::iterator it = mEventQueue.begin();  
    12.     while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {  
    13.         ++it;  
    14.     }  
    15.   
    16.     Event event;  
    17.     event.mWhenUs = whenUs;  
    18.     event.mMessage = msg;  
    19.   
    20.     if (it == mEventQueue.begin()) {  
    21.         mQueueChangedCondition.signal();  
    22.     }  
    23.   
    24.     mEventQueue.insert(it, event);  
    25. }  

    delayUs用于做延时消息使用,会加上当前时间作为消息应该被处理的时间。然后依次比较mEventQueue链表中的所有消息,并把当前消息插入到比whenUs大的前面一个位置。如果这是mEventQueue中的第一个消息,则发出一个signal通知等待的线程。前面我们知道在ALooper的loop方法中会循环的从mEventQueue获取消息并dispatch出去给WifiDisplaySource的onMessageReceived去处理,我们接着来看这部分的实现。这里绕这么大一圈,最后WifiDisplaySource发送的消息还是给自己处理,主要是为了避开主线程处理的事务太多,通过消息机制,让更多的繁杂的活都在Thread中去完成。
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {  
    2.     switch (msg->what()) {  
    3.         case kWhatStart:  
    4.         {  
    5.             uint32_t replyID;  
    6.             CHECK(msg->senderAwaitsResponse(&replyID));  
    7.   
    8.             AString iface;  
    9.             CHECK(msg->findString("iface", &iface));  
    10.   
    11.             status_t err = OK;  
    12.   
    13.             ssize_t colonPos = iface.find(":");  
    14.   
    15.             unsigned long port;  
    16.   
    17.             if (colonPos >= 0) {  
    18.                 const char *s = iface.c_str() + colonPos + 1;  
    19.   
    20.                 char *end;  
    21.                 port = strtoul(s, &end, 10);  
    22.   
    23.                 if (end == s || *end != '' || port > 65535) {  
    24.                     err = -EINVAL;  
    25.                 } else {  
    26.                     iface.erase(colonPos, iface.size() - colonPos);  
    27.                 }  
    28.             } else {  
    29.                 port = kWifiDisplayDefaultPort;  
    30.             }  
    31.   
    32.             if (err == OK) {  
    33.                 if (inet_aton(iface.c_str(), &mInterfaceAddr) != 0) {  
    34.                     sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());  
    35.   
    36.                     err = mNetSession->createRTSPServer(  
    37.                             mInterfaceAddr, port, notify, &mSessionID);  
    38.                 } else {  
    39.                     err = -EINVAL;  
    40.                 }  
    41.             }  
    42.   
    43.             mState = AWAITING_CLIENT_CONNECTION;  
    44.   
    45.             sp<AMessage> response = new AMessage;  
    46.             response->setInt32("err", err);  
    47.             response->postReply(replyID);  
    48.             break;  
    49.         }  

    首先通过AMessage获取到replayID和iface,然后把iface分割成ip和port,分别保存在mInterfaceAddr和port中。在调用ANetSession的createRTSPServer去创建一个RTSP server,最后构造一个response对象并返回。我们先来看createRTSPServer方法:
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. status_t ANetworkSession::createRTSPServer(  
    2.         const struct in_addr &addr, unsigned port,  
    3.         const sp<AMessage> ¬ify, int32_t *sessionID) {  
    4.     return createClientOrServer(  
    5.             kModeCreateRTSPServer,  
    6.             &addr,  
    7.             port,  
    8.             NULL /* remoteHost */,  
    9.             /* remotePort */,  
    10.             notify,  
    11.             sessionID);  
    12. }  
    13.   
    14. status_t ANetworkSession::createClientOrServer(  
    15.         Mode mode,  
    16.         const struct in_addr *localAddr,  
    17.         unsigned port,  
    18.         const char *remoteHost,  
    19.         unsigned remotePort,  
    20.         const sp<AMessage> ¬ify,  
    21.         int32_t *sessionID) {  
    22.     Mutex::Autolock autoLock(mLock);  
    23.   
    24.     *sessionID = 0;  
    25.     status_t err = OK;  
    26.     int s, res;  
    27.     sp<Session> session;  
    28.   
    29.     s = socket(  
    30.             AF_INET,  
    31.             (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,  
    32.             0);  
    33.   
    34.     if (s < 0) {  
    35.         err = -errno;  
    36.         goto bail;  
    37.     }  
    38.   
    39.     if (mode == kModeCreateRTSPServer  
    40.             || mode == kModeCreateTCPDatagramSessionPassive) {  
    41.         const int yes = 1;  
    42.         res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));  
    43.   
    44.         if (res < 0) {  
    45.             err = -errno;  
    46.             goto bail2;  
    47.         }  
    48.     }  
    49.   
    50.     err = MakeSocketNonBlocking(s);  
    51.   
    52.     if (err != OK) {  
    53.         goto bail2;  
    54.     }  
    55.   
    56.     struct sockaddr_in addr;  
    57.     memset(addr.sin_zero, 0, sizeof(addr.sin_zero));  
    58.     addr.sin_family = AF_INET;  
    59.     } else if (localAddr != NULL) {  
    60.         addr.sin_addr = *localAddr;  
    61.         addr.sin_port = htons(port);  
    62.   
    63.         res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));  
    64.   
    65.         if (res == 0) {  
    66.             if (mode == kModeCreateRTSPServer  
    67.                     || mode == kModeCreateTCPDatagramSessionPassive) {  
    68.                 res = listen(s, 4);  
    69.             } else {  
    70.   
    71.   
    72.     if (res < 0) {  
    73.         err = -errno;  
    74.         goto bail2;  
    75.     }  
    76.   
    77.     Session::State state;  
    78.     switch (mode) {  
    79.   
    80.         case kModeCreateRTSPServer:  
    81.             state = Session::LISTENING_RTSP;  
    82.             break;  
    83.   
    84.         default:  
    85.             CHECK_EQ(mode, kModeCreateUDPSession);  
    86.             state = Session::DATAGRAM;  
    87.             break;  
    88.     }  
    89.   
    90.     session = new Session(  
    91.             mNextSessionID++,  
    92.             state,  
    93.             s,  
    94.             notify);  
    95.   
    96.   
    97.     mSessions.add(session->sessionID(), session);  
    98.   
    99.     interrupt();  
    100.   
    101.     *sessionID = session->sessionID();  
    102.   
    103.     goto bail;  
    104.   
    105. bail2:  
    106.     close(s);  
    107.     s = -1;  
    108.   
    109. bail:  
    110.     return err;  
    111. }  

    createRTSPServer直接调用createClientOrServer,第一个参数是kModeCreateRTSPServer表示要创建一个RTSP server。createClientOrServer的代码比较长,上面是精简后的代码,其它没看到的代码我们以后遇到的过程中再来分析。上面的代码中首先创建一个socket,然后设置一下socket的reuse和no-block属性,接着bind到指定的IP和port上,然后再此socket上开始listen。接下来置当前ANetworkSession的状态是LISTENING_RTSP。然后创建一个Session会话对象,在构造函数中会传入notify作为参数,notify是一个kWhatRTSPNotify的AMessag,后面会看到如何使用它。然后添加到mSessions数组当中。接着调用interrupt方法,让ANetworkSession的NetworkThread线程跳出select语句,并重新计算readFd和writeFd用于select监听的文件句柄。
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void ANetworkSession::interrupt() {  
    2.     static const char dummy = 0;  
    3.   
    4.     ssize_t n;  
    5.     do {  
    6.         n = write(mPipeFd[1], &dummy, 1);  
    7.     } while (n < 0 && errno == EINTR);  
    8.   
    9.     if (n < 0) {  
    10.         ALOGW("Error writing to pipe (%s)", strerror(errno));  
    11.     }  
    12. }  

    interrupt方法向pipe中写入一个空消息,前面我们已经介绍过threadLoop了,这里就会把刚刚创建的socket加入到监听的readFd中。到这里,关于WifiDisplay连接的建立就讲完了,后面会再从收到Sink的TCP连接请求开始讲起。最后贴一份从WifiDisplaySettings到ANetworkSession如何创建socket的时序图:
     
  • 相关阅读:
    HDU 1423 Greatest Common Increasing Subsequence(LICS入门,只要求出最长数)
    HDU 5455 Fang Fang
    hihoCoder 1234 fractal
    二叉排序树
    最大连续子序列和,最大上升子序列和,最长上升子序列,最长公共子串,最长公共子序列,最长上升公共子序列
    C++ string string string string string string string string string string
    pip安装第三方库
    pip安装时使用国内源,加快下载速度
    pip升级命令
    instanceof -- JS
  • 原文地址:https://www.cnblogs.com/dirt2/p/5962285.html
Copyright © 2020-2023  润新知