Android 4.2 Wifi Display 之 Settings 源码分析(二)

转自:http://blog.csdn.net/mznewfacer/article/details/8268930

在上一回我们一块分析了WifiDisplay有关设备发现的部分,这一回将主要针对设备连接和建立数据流展开分析。

首先,回顾下应用层,当用户在搜寻完设备后,可以选择设备进行连接,当然正在进行连接或已经连接配对的设备,再次点击配置后,会弹出对话框供用户选择断开连接。

packages/apps/Settings/src/com/android/settings/wfd/WifiDisplaySettings.java

  1. public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
  2. Preference preference) {
  3. if (preference instanceof WifiDisplayPreference) {
  4. WifiDisplayPreference p = (WifiDisplayPreference)preference;
  5. WifiDisplay display = p.getDisplay();
  6. if (display.equals(mWifiDisplayStatus.getActiveDisplay())) {
  7. showDisconnectDialog(display);
  8. } else {
  9. mDisplayManager.connectWifiDisplay(display.getDeviceAddress());
  10. }
  11. }
  12. return super.onPreferenceTreeClick(preferenceScreen, preference);
  13. }
 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
            Preference preference) {
        if (preference instanceof WifiDisplayPreference) {
            WifiDisplayPreference p = (WifiDisplayPreference)preference;
            WifiDisplay display = p.getDisplay();

            if (display.equals(mWifiDisplayStatus.getActiveDisplay())) {
                showDisconnectDialog(display);
            } else {
                mDisplayManager.connectWifiDisplay(display.getDeviceAddress());
            }
        }

        return super.onPreferenceTreeClick(preferenceScreen, preference);
    }

如同设备发现的调用流程,当用户选择设备进行连接后,程序会调用DisplayManager的connectWifiDisplay()函数接口。该函数会进一步根据DisplayManagerGlobal提供的单实例对象调用AIDL提供的接口函数connectWifiDisplay(),这又是上一回已经提到过的调用模式。其实际的调用实现是Displaymanager service中提供的connectWifiDisplay()函数,

frameworks/base/services/java/com/android/server/display/DisplayManagerService.java

  1. public void connectWifiDisplay(String address) {
  2. if (address == null) {
  3. throw new IllegalArgumentException("address must not be null");
  4. }
  5. final boolean trusted = canCallerConfigureWifiDisplay();
  6. final long token = Binder.clearCallingIdentity();
  7. try {
  8. synchronized (mSyncRoot) {
  9. if (mWifiDisplayAdapter != null) {
  10. mWifiDisplayAdapter.requestConnectLocked(address, trusted);
  11. }
  12. }
  13. } finally {
  14. Binder.restoreCallingIdentity(token);
  15. }
  16. }
public void connectWifiDisplay(String address) {
        if (address == null) {
            throw new IllegalArgumentException("address must not be null");
        }

        final boolean trusted = canCallerConfigureWifiDisplay();
        final long token = Binder.clearCallingIdentity();
        try {
            synchronized (mSyncRoot) {
                if (mWifiDisplayAdapter != null) {
                    mWifiDisplayAdapter.requestConnectLocked(address, trusted);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }


 

到此,我们容易发现连接WifiDisplay设备的函数调用流程与发现设备的流程一致,这里将不做多余解释(详见),在此会罗列出之后的基本流程。

frameworks/base/services/java/com/android/server/display/WifiDisplayAdapter.java

  1. public void requestConnectLocked(final String address, final boolean trusted) {
  2. if (DEBUG) {
  3. Slog.d(TAG, "requestConnectLocked: address=" + address + ", trusted=" + trusted);
  4. }
  5. if (!trusted) {
  6. synchronized (getSyncRoot()) {
  7. if (!isRememberedDisplayLocked(address)) { //如果设备地址不在保存列表中则忽略不做处理
  8. ...
  9. return;
  10. }
  11. }
  12. }
  13. getHandler().post(new Runnable() {
  14. @Override
  15. public void run() {
  16. if (mDisplayController != null) {
  17. mDisplayController.requestConnect(address);
  18. }
  19. }
  20. });
  21. }
public void requestConnectLocked(final String address, final boolean trusted) {
        if (DEBUG) {
            Slog.d(TAG, "requestConnectLocked: address=" + address + ", trusted=" + trusted);
        }

        if (!trusted) {
            synchronized (getSyncRoot()) {
                if (!isRememberedDisplayLocked(address)) {   //如果设备地址不在保存列表中则忽略不做处理
                   ...
                    return;
                }
            }
        }

        getHandler().post(new Runnable() {
            @Override
            public void run() {
                if (mDisplayController != null) {
                    mDisplayController.requestConnect(address);
                }
            }
        });
    }


 

frameworks/base/services/java/com/android/server/display/WifiDisplayController.java

  1. public void requestConnect(String address) {
  2. for (WifiP2pDevice device : mAvailableWifiDisplayPeers) {
  3. if (device.deviceAddress.equals(address)) {
  4. connect(device);
  5. }
  6. }
  7. }
  8. private void connect(final WifiP2pDevice device) {
  9. if (mDesiredDevice != null
  10. && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) { //如果设备已经正在连接则返回
  11. if (DEBUG) {
  12. ...
  13. }
  14. return;
  15. }
  16. if (mConnectedDevice != null
  17. && !mConnectedDevice.deviceAddress.equals(device.deviceAddress)
  18. && mDesiredDevice == null) {//如果设备已经连接则返回
  19. if (DEBUG) {
  20. ...
  21. }
  22. return;
  23. }
  24. mDesiredDevice = device;
  25. mConnectionRetriesLeft = CONNECT_MAX_RETRIES; //尝试连接最大次数
  26. updateConnection();
  27. }
 public void requestConnect(String address) {
        for (WifiP2pDevice device : mAvailableWifiDisplayPeers) {
            if (device.deviceAddress.equals(address)) {
                connect(device);
            }
        }
    }

    private void connect(final WifiP2pDevice device) {
        if (mDesiredDevice != null
                && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) {  //如果设备已经正在连接则返回
            if (DEBUG) {
               ...
            }
            return;
        }

        if (mConnectedDevice != null
                && !mConnectedDevice.deviceAddress.equals(device.deviceAddress)
                && mDesiredDevice == null) {//如果设备已经连接则返回
            if (DEBUG) {
                 ...
            }
            return;
        }

        mDesiredDevice = device;
        mConnectionRetriesLeft = CONNECT_MAX_RETRIES; //尝试连接最大次数
        updateConnection();
    }


 

接下来,我们将重点看一看updateConnection()函数,此函数是建立Wifidisplay连接,监听RTSP连接的核心实现函数。

  1. private void updateConnection() {
  2. //在尝试连接到新设备时,需要通知系统这里已经与旧的设备断开连接
  3. if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
  4. ...
  5. mRemoteDisplay.dispose(); //释放NativeRemoteDisplay资源停止监听
  6. mRemoteDisplay = null; //监听返回对象置为空
  7. mRemoteDisplayInterface = null; //监听端口置为空
  8. mRemoteDisplayConnected = false; //连接标识为未连接
  9. mHandler.removeCallbacks(mRtspTimeout);//将挂起的mRtspTimeout线程从消息队列中移除
  10. setRemoteSubmixOn(false); //关闭远程混音重建模式
  11. unadvertiseDisplay();
  12. }
  13. if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
  14. ...
  15. unadvertiseDisplay();
  16. final WifiP2pDevice oldDevice = mConnectedDevice;
  17. mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
  18. @Override
  19. public void onSuccess() {
  20. ...
  21. next();
  22. }
  23. @Override
  24. public void onFailure(int reason) {
  25. ...
  26. next();
  27. }
  28. private void next() {
  29. if (mConnectedDevice == oldDevice) { //确保连接设备已经不是旧的设备否则递归调用该函数
  30. mConnectedDevice = null;
  31. updateConnection();
  32. }
  33. }
  34. });
  35. return;
  36. }
  37. if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
  38. ...
  39. unadvertiseDisplay();
  40. mHandler.removeCallbacks(mConnectionTimeout);
  41. final WifiP2pDevice oldDevice = mConnectingDevice;
  42. mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() { //在尝试连接到新设备之前,取消正在进行的p2p连接
  43. @Override
  44. public void onSuccess() {
  45. ...
  46. next();
  47. }
  48. @Override
  49. public void onFailure(int reason) {
  50. ...
  51. next();
  52. }
  53. private void next() {
  54. if (mConnectingDevice == oldDevice) {
  55. mConnectingDevice = null;
  56. updateConnection();
  57. }
  58. }
  59. });
  60. return;
  61. }
  62. // 如果想断开连接,则任务结束
  63. if (mDesiredDevice == null) {
  64. unadvertiseDisplay();
  65. return;
  66. }
  67. if (mConnectedDevice == null && mConnectingDevice == null) {
  68. Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);
  69. mConnectingDevice = mDesiredDevice;
  70. WifiP2pConfig config = new WifiP2pConfig();
  71. config.deviceAddress = mConnectingDevice.deviceAddress;
  72. config.groupOwnerIntent = WifiP2pConfig.MIN_GROUP_OWNER_INTENT;
  73. WifiDisplay display = createWifiDisplay(mConnectingDevice);
  74. advertiseDisplay(display, null, 0, 0, 0);
  75. final WifiP2pDevice newDevice = mDesiredDevice;
  76. mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {
  77. //以特定的配置信息开启P2P连接,如果当前设备不是P2P组的一部分,会建立P2P小组并发起连接请求;如果当前设备是现存P2P组的一部分,则加入该组的邀请会发送至该配对设备。
  78. @Override
  79. public void onSuccess() {
  80. //为了防止连接还没有建立成功,这里设定了等待处理函数,如果在定长时间内还没有接受到WIFI_P2P_CONNECTION_CHANGED_ACTION广播,则按照handleConnectionFailure(true)处理。
  81. Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);
  82. mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);
  83. }
  84. @Override
  85. public void onFailure(int reason) {
  86. if (mConnectingDevice == newDevice) {
  87. Slog.i(TAG, "Failed to initiate connection to Wifi display: "
  88. + newDevice.deviceName + ", reason=" + reason);
  89. mConnectingDevice = null;
  90. handleConnectionFailure(false);
  91. }
  92. }
  93. });
  94. return;
  95. }
  96. // 根据连接的网络地址和端口号监听Rtsp流连接
  97. if (mConnectedDevice != null && mRemoteDisplay == null) {
  98. Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
  99. if (addr == null) {
  100. Slog.i(TAG, "Failed to get local interface address for communicating "
  101. + "with Wifi display: " + mConnectedDevice.deviceName);
  102. handleConnectionFailure(false);
  103. return; // done
  104. }
  105. setRemoteSubmixOn(true);
  106. final WifiP2pDevice oldDevice = mConnectedDevice;
  107. final int port = getPortNumber(mConnectedDevice);
  108. final String iface = addr.getHostAddress() + ":" + port;
  109. mRemoteDisplayInterface = iface;
  110. Slog.i(TAG, "Listening for RTSP connection on " + iface
  111. + " from Wifi display: " + mConnectedDevice.deviceName);
  112. mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {
  113. //开始监听连接上的接口
  114. @Override
  115. public void onDisplayConnected(Surface surface,
  116. int width, int height, int flags) {
  117. if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) {
  118. Slog.i(TAG, "Opened RTSP connection with Wifi display: "
  119. + mConnectedDevice.deviceName);
  120. mRemoteDisplayConnected = true;
  121. mHandler.removeCallbacks(mRtspTimeout);
  122. final WifiDisplay display = createWifiDisplay(mConnectedDevice);
  123. advertiseDisplay(display, surface, width, height, flags);
  124. }
  125. }
  126. @Override
  127. public void onDisplayDisconnected() {
  128. if (mConnectedDevice == oldDevice) {
  129. Slog.i(TAG, "Closed RTSP connection with Wifi display: "
  130. + mConnectedDevice.deviceName);
  131. mHandler.removeCallbacks(mRtspTimeout);
  132. disconnect();
  133. }
  134. }
  135. @Override
  136. public void onDisplayError(int error) {
  137. if (mConnectedDevice == oldDevice) {
  138. Slog.i(TAG, "Lost RTSP connection with Wifi display due to error "
  139. + error + ": " + mConnectedDevice.deviceName);
  140. mHandler.removeCallbacks(mRtspTimeout);
  141. handleConnectionFailure(false);
  142. }
  143. }
  144. }, mHandler);
  145. mHandler.postDelayed(mRtspTimeout, RTSP_TIMEOUT_SECONDS * 1000);
  146. }
  147. }
 private void updateConnection() {
       //在尝试连接到新设备时,需要通知系统这里已经与旧的设备断开连接
        if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
            ...
            mRemoteDisplay.dispose();  //释放NativeRemoteDisplay资源停止监听
            mRemoteDisplay = null;   //监听返回对象置为空
            mRemoteDisplayInterface = null;   //监听端口置为空
            mRemoteDisplayConnected = false;  //连接标识为未连接
            mHandler.removeCallbacks(mRtspTimeout);//将挂起的mRtspTimeout线程从消息队列中移除

            setRemoteSubmixOn(false);   //关闭远程混音重建模式
            unadvertiseDisplay();   
        }
        if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
             ...
            unadvertiseDisplay();

            final WifiP2pDevice oldDevice = mConnectedDevice;
            mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
                @Override
                public void onSuccess() {
                    ...
                    next();
                }

                @Override
                public void onFailure(int reason) {
                   ...
                    next();
                }

                private void next() {
                    if (mConnectedDevice == oldDevice) {  //确保连接设备已经不是旧的设备否则递归调用该函数
                        mConnectedDevice = null;
                        updateConnection();
                    }
                }
            });
            return; 
        }


        if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
            ...
            unadvertiseDisplay();
            mHandler.removeCallbacks(mConnectionTimeout);

            final WifiP2pDevice oldDevice = mConnectingDevice;
            mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() {  //在尝试连接到新设备之前,取消正在进行的p2p连接
                @Override
                public void onSuccess() {
                    ...
                    next();
                }

                @Override
                public void onFailure(int reason) {
                    ...
                    next();
                }

                private void next() {
                    if (mConnectingDevice == oldDevice) {
                        mConnectingDevice = null;
                        updateConnection();
                    }
                }
            });
            return; 
        }
    //  如果想断开连接,则任务结束
        if (mDesiredDevice == null) {
            unadvertiseDisplay();
            return; 
        }

        if (mConnectedDevice == null && mConnectingDevice == null) {
            Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);
            mConnectingDevice = mDesiredDevice;
            WifiP2pConfig config = new WifiP2pConfig();
            config.deviceAddress = mConnectingDevice.deviceAddress;
            config.groupOwnerIntent = WifiP2pConfig.MIN_GROUP_OWNER_INTENT;

            WifiDisplay display = createWifiDisplay(mConnectingDevice);
            advertiseDisplay(display, null, 0, 0, 0);

            final WifiP2pDevice newDevice = mDesiredDevice;
            mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {
      //以特定的配置信息开启P2P连接,如果当前设备不是P2P组的一部分,会建立P2P小组并发起连接请求;如果当前设备是现存P2P组的一部分,则加入该组的邀请会发送至该配对设备。

                @Override
                public void onSuccess() {
        //为了防止连接还没有建立成功,这里设定了等待处理函数,如果在定长时间内还没有接受到WIFI_P2P_CONNECTION_CHANGED_ACTION广播,则按照handleConnectionFailure(true)处理。
                    Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);
                    mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);
                }

                @Override
                public void onFailure(int reason) {
                    if (mConnectingDevice == newDevice) {
                        Slog.i(TAG, "Failed to initiate connection to Wifi display: "
                                + newDevice.deviceName + ", reason=" + reason);
                        mConnectingDevice = null;
                        handleConnectionFailure(false);
                    }
                }
            });
            return; 
        }
        // 根据连接的网络地址和端口号监听Rtsp流连接
        if (mConnectedDevice != null && mRemoteDisplay == null) {
            Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
            if (addr == null) {
                Slog.i(TAG, "Failed to get local interface address for communicating "
                        + "with Wifi display: " + mConnectedDevice.deviceName);
                handleConnectionFailure(false);
                return; // done
            }

            setRemoteSubmixOn(true);

            final WifiP2pDevice oldDevice = mConnectedDevice;
            final int port = getPortNumber(mConnectedDevice);
            final String iface = addr.getHostAddress() + ":" + port;
            mRemoteDisplayInterface = iface;

            Slog.i(TAG, "Listening for RTSP connection on " + iface
                    + " from Wifi display: " + mConnectedDevice.deviceName);

            mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {
//开始监听连接上的接口
                @Override
                public void onDisplayConnected(Surface surface,
                        int width, int height, int flags) {
                    if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) {
                        Slog.i(TAG, "Opened RTSP connection with Wifi display: "
                                + mConnectedDevice.deviceName);
                        mRemoteDisplayConnected = true;
                        mHandler.removeCallbacks(mRtspTimeout);

                        final WifiDisplay display = createWifiDisplay(mConnectedDevice);
                        advertiseDisplay(display, surface, width, height, flags);
                    }
                }

                @Override
                public void onDisplayDisconnected() {
                    if (mConnectedDevice == oldDevice) {
                        Slog.i(TAG, "Closed RTSP connection with Wifi display: "
                                + mConnectedDevice.deviceName);
                        mHandler.removeCallbacks(mRtspTimeout);
                        disconnect();
                    }
                }

                @Override
                public void onDisplayError(int error) {
                    if (mConnectedDevice == oldDevice) {
                        Slog.i(TAG, "Lost RTSP connection with Wifi display due to error "
                                + error + ": " + mConnectedDevice.deviceName);
                        mHandler.removeCallbacks(mRtspTimeout);
                        handleConnectionFailure(false);
                    }
                }
            }, mHandler);

            mHandler.postDelayed(mRtspTimeout, RTSP_TIMEOUT_SECONDS * 1000);
        }
    }


 

至此,我们已经了解了建立WifiDisplay连接的基本流程,当然可以继续向底层深入,只要用户选择尝试连接并且已经确认处于连接断开的状态,则会调用WifiP2pManager中的connect()接口函数,该函数会向Channel中发送CONNECT信号,并注册监听器监听相应结果。在进入P2pStateMachine状态机后,WifiP2pService会分为两种情况进行处理。如果当前的设备不是P2P组的成员,WifiP2pService会调用WifiNative类中的p2pConnect()函数,该函数会继续向底层调用,最终会调用wifi.cwifi_send_command()命令,把groupnegotiation请求发送至wpa_supplicant供其处理;如果这个设备已经是P2P组的成员,或者自己通过WifiNative类中的p2pGroupAdd()函数创建了一个组,那么会进入GroupCreatedState,进一步会调用WifiNative类中的p2pInvite()函数向设备发送邀请请求。具体的有关wpa_supplicant同底层驱动的交互,以及wpa_supplicant同WifiMonitor与WifiP2pService状态机之间的调用流程以后有机会再讨论。

在本文的最后,还想继续讨论一下监听RTSP连接的核心实现函数RemoteDisplay.listen(...),

frameworks/base/media/java/android/media/RemoteDisplay.java

  1. public static RemoteDisplay listen(String iface, Listener listener, Handler handler) {
  2. ...
  3. RemoteDisplay display = new RemoteDisplay(listener, handler);
  4. display.startListening(iface);
  5. return display;
  6. }
  7. 可以看到该监听函数会调用以下函数,并把监听端口作为参数进行传递,
  8. private void startListening(String iface) {
  9. mPtr = nativeListen(iface);
  10. if (mPtr == 0) {
  11. throw new IllegalStateException("Could not start listening for "
  12. + "remote display connection on \"" + iface + "\"");
  13. }
  14. mGuard.open("dispose");
  15. }
public static RemoteDisplay listen(String iface, Listener listener, Handler handler) {
...
        RemoteDisplay display = new RemoteDisplay(listener, handler);
        display.startListening(iface);
        return display;
    }
可以看到该监听函数会调用以下函数,并把监听端口作为参数进行传递,
 private void startListening(String iface) {
        mPtr = nativeListen(iface);
        if (mPtr == 0) {
            throw new IllegalStateException("Could not start listening for "
                    + "remote display connection on \"" + iface + "\"");
        }
        mGuard.open("dispose");  
    }

以上函数最终会调用JNI层的接口函数nativeListen()进行监听。至于CloseGuardmGuard.open(),不理解的话,我们就把它看作是Android提供的一种资源清理机制。

接下来,可以具体看一下RemoteDisplay在JNI层的接口实现,

frameworks/base/core/jni/android_media_RemoteDisplay.cpp

  1. static jint nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) {
  2. ScopedUtfChars iface(env, ifaceStr); //通过智能指针的方式将string类型转化为只读的UTF chars类型
  3. sp<IServiceManager> sm = defaultServiceManager();
  4. sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(
  5. sm->getService(String16("media.player")));
  6. //用service manager获得 media player服务的代理实例,即通过interface_cast将其转化成BpMediaPlayerService (Bridge模式)
  7. if (service == NULL) {
  8. ALOGE("Could not obtain IMediaPlayerService from service manager");
  9. return 0;
  10. }
  11. sp<NativeRemoteDisplayClient> client(new NativeRemoteDisplayClient(env, remoteDisplayObj));
  12. sp<IRemoteDisplay> display = service->listenForRemoteDisplay(
  13. client, String8(iface.c_str()));
  14. //调用BpMediaPlayerService提供的接口函数,与服务端BnMediaPlayerService进行通讯
  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. NativeRemoteDisplay* wrapper = new NativeRemoteDisplay(display, client);
  21. return reinterpret_cast<jint>(wrapper);
  22. }
static jint nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) {
    ScopedUtfChars iface(env, ifaceStr);  //通过智能指针的方式将string类型转化为只读的UTF chars类型

    sp<IServiceManager> sm = defaultServiceManager();
    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(
            sm->getService(String16("media.player"))); 
//用service manager获得 media player服务的代理实例,即通过interface_cast将其转化成BpMediaPlayerService  (Bridge模式)
    if (service == NULL) {
        ALOGE("Could not obtain IMediaPlayerService from service manager");
        return 0;
    }
    sp<NativeRemoteDisplayClient> client(new NativeRemoteDisplayClient(env, remoteDisplayObj));
    sp<IRemoteDisplay> display = service->listenForRemoteDisplay(
            client, String8(iface.c_str()));
//调用BpMediaPlayerService提供的接口函数,与服务端BnMediaPlayerService进行通讯
    if (display == NULL) {
        ALOGE("Media player service rejected request to listen for remote display '%s'.",
                iface.c_str());
        return 0;
    }

    NativeRemoteDisplay* wrapper = new NativeRemoteDisplay(display, client);
    return reinterpret_cast<jint>(wrapper);
}


 

这里采用了Binder通信机制,BpMediaPlayerService继承BpInterface<IMediaPlayerService>作为代理端,采用Bridge模式调用listenForRemoteDisplay()接口函数将上层的监听接口以及实例化的NativeRemoteDisplayClient代理对象传递至服务端BnMediaPlayerService进行处理。

/frameworks/av/media/libmedia/IMediaPlayerService.cpp

  1. class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
  2. {
  3. public:
  4. virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
  5. const String8& iface)
  6. {
  7. Parcel data, reply;
  8. data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
  9. data.writeStrongBinder(client->asBinder());
  10. data.writeString8(iface);
  11. remote()->transact(LISTEN_FOR_REMOTE_DISPLAY, data, &reply); //向服务端BnMediaPlayerService发送LISTEN_FOR_REMOTE_DISPLAY 处理命令
  12. return interface_cast<IRemoteDisplay>(reply.readStrongBinder());
  13. }
  14. };
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
  public:
        …
  virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
            const String8& iface)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        data.writeStrongBinder(client->asBinder());
        data.writeString8(iface);
        remote()->transact(LISTEN_FOR_REMOTE_DISPLAY, data, &reply);  //向服务端BnMediaPlayerService发送LISTEN_FOR_REMOTE_DISPLAY 处理命令
        return interface_cast<IRemoteDisplay>(reply.readStrongBinder());
    }
};


 

进一步可以看到,NativeRemoteDisplayClient继承于BnRemoteDisplayClient,其实这是IRemoteDisplayClient接口的服务端实现。该类提供了三个接口函数onDisplayConnected()、onDisplayDisconnected()、onDisplayError()是frameworks/base/media/java/android/media/RemoteDisplay.java中RemoteDisplay.Listener{}的三个监听函数在JNI层的实现,特别的,对于onDisplayConnected()函数而言,调用android_view_Surface_createFromISurfaceTexture()函数创建surfaceObj并将其向RemoteDisplay中注册的监听线程传递并进行回调。

frameworks/base/core/jni/android_media_RemoteDisplay.cpp

  1. virtual void onDisplayConnected(const sp<ISurfaceTexture>& surfaceTexture,
  2. uint32_t width, uint32_t height, uint32_t flags) {
  3. JNIEnv* env = AndroidRuntime::getJNIEnv();
  4. jobject surfaceObj = android_view_Surface_createFromISurfaceTexture(env, surfaceTexture);
  5. //跟据当前获取的media server的surface texture来创建Surface对象
  6. if (surfaceObj == NULL) {
  7. ...
  8. return;
  9. }
  10. env->CallVoidMethod(mRemoteDisplayObjGlobal,
  11. gRemoteDisplayClassInfo.notifyDisplayConnected,
  12. surfaceObj, width, height, flags); //将Suface对象作为参数传递至notifyDisplayConnected函数用于监听函数的回调
  13. env->DeleteLocalRef(surfaceObj);
  14. checkAndClearExceptionFromCallback(env, "notifyDisplayConnected");
  15. }
virtual void onDisplayConnected(const sp<ISurfaceTexture>& surfaceTexture,
            uint32_t width, uint32_t height, uint32_t flags) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        jobject surfaceObj = android_view_Surface_createFromISurfaceTexture(env, surfaceTexture); 
  //跟据当前获取的media server的surface texture来创建Surface对象
        if (surfaceObj == NULL) {
            ...
            return;
        }

        env->CallVoidMethod(mRemoteDisplayObjGlobal,
                gRemoteDisplayClassInfo.notifyDisplayConnected,
                surfaceObj, width, height, flags);   //将Suface对象作为参数传递至notifyDisplayConnected函数用于监听函数的回调
        env->DeleteLocalRef(surfaceObj);
        checkAndClearExceptionFromCallback(env, "notifyDisplayConnected");
    }

接下来,我们继续来看服务端BnMediaPlayerService的实现,其中onTransact函数用于接收来自BpMediaPlayerService发送的命令,如果命令为LISTEN_FOR_REMOTE_DISPLAY,则会读取相应数据并作为参数进行传递。这里的listenForRemoteDisplay()函数是纯虚函数,其实现是由派生类MediaPlayerService来完成的。


 

  1. status_t BnMediaPlayerService::onTransact(
  2. uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
  3. {
  4. switch (code) {
  5. case LISTEN_FOR_REMOTE_DISPLAY: {
  6. CHECK_INTERFACE(IMediaPlayerService, data, reply);
  7. sp<IRemoteDisplayClient> client(
  8. interface_cast<IRemoteDisplayClient>(data.readStrongBinder()));
  9. String8 iface(data.readString8());
  10. sp<IRemoteDisplay> display(listenForRemoteDisplay(client, iface));//调用纯虚函数接口,运行时实际调用派生类MediaPlayerService的函数实现
  11. reply->writeStrongBinder(display->asBinder());
  12. return NO_ERROR;
  13. } break;
  14. default:
  15. return BBinder::onTransact(code, data, reply, flags);
  16. }
  17. }
status_t BnMediaPlayerService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch (code) {
       …
    case LISTEN_FOR_REMOTE_DISPLAY: {
            CHECK_INTERFACE(IMediaPlayerService, data, reply);
            sp<IRemoteDisplayClient> client(
                    interface_cast<IRemoteDisplayClient>(data.readStrongBinder()));
            String8 iface(data.readString8());
            sp<IRemoteDisplay> display(listenForRemoteDisplay(client, iface));//调用纯虚函数接口,运行时实际调用派生类MediaPlayerService的函数实现
            reply->writeStrongBinder(display->asBinder());
            return NO_ERROR;
        } break;
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}


 

最后,来看一看该函数的实际实现,

frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp

  1. sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay(
  2. const sp<IRemoteDisplayClient>& client, const String8& iface) {
  3. if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY")) {
  4. //检查是否有WIFI Display权限
  5. return NULL;
  6. }
  7. return new RemoteDisplay(client, iface.string()); //直接调用 RemoteDisplay构造函数来开启Wifi display source端
  8. }
sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay(
        const sp<IRemoteDisplayClient>& client, const String8& iface) {
    if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY")) {
      //检查是否有WIFI Display权限
        return NULL;
    }

    return new RemoteDisplay(client, iface.string());  //直接调用 RemoteDisplay构造函数来开启Wifi display source端
}


 

其中,RemoteDisplay继承于BnRemoteDisplay,也采取了Binder通信机制,代理端BpRemoteDisplay与服务端BnRemoteDisplay的接口实现详见frameworks/av/media/libmedia/IRemoteDisplay.cpp。这里,值得一提的是,函数listenForRemoteDisplay()假设在同一时刻连接到指定网络端口iface的remotedisplay设备最多只有一个。换句话说,在同一时刻只有一个设备能作为WifiDisplay source端设备进行播放。

最后,我们来看一看开启Wifidisplay source端的这个构造函数,

frameworks/av/media/libmediaplayerservice/RemoteDisplay.cpp

  1. RemoteDisplay::RemoteDisplay(
  2. const sp<IRemoteDisplayClient> &client, const char *iface)
  3. : mLooper(new ALooper),
  4. mNetSession(new ANetworkSession),
  5. mSource(new WifiDisplaySource(mNetSession, client)) {
  6. mLooper->setName("wfd_looper");
  7. mLooper->registerHandler(mSource); //注册了Wifi display 处理线程
  8. mNetSession->start(); //初始化数据管道,启动NetworkThread线程,进入threadLoop中监听数据流变化等待处理
  9. mLooper->start(); //开启消息处理管理线程
  10. mSource->start(iface); //将网络端口作为消息载体进行传递处理,并等待响应结果,完成与Wifi Display source端开启播放的相关工作
  11. }
RemoteDisplay::RemoteDisplay(
        const sp<IRemoteDisplayClient> &client, const char *iface)
    : mLooper(new ALooper),
      mNetSession(new ANetworkSession),
      mSource(new WifiDisplaySource(mNetSession, client)) {
    mLooper->setName("wfd_looper");
    mLooper->registerHandler(mSource);  //注册了Wifi display 处理线程

    mNetSession->start();  //初始化数据管道,启动NetworkThread线程,进入threadLoop中监听数据流变化等待处理
    mLooper->start();   //开启消息处理管理线程

    mSource->start(iface);    //将网络端口作为消息载体进行传递处理,并等待响应结果,完成与Wifi Display source端开启播放的相关工作
}


 

其中mLooper,mNetSession, mSource分别为sp<ALooper>mLooper,sp<ANetworkSession>mNetSession以及sp<WifiDisplaySource>mSource等三个强指针,对强指针概念不清的请见此。此处是利用构造函数的初始化列表将这三个强指针指向这三个new出来的对象。之后便是利用这三个指针,调用类中的方法以开启Wifidisplay source端进行播放。这里,ALooper是关于线程以及消息队列等待处理管理相关的一个类。ANetworkSessions是管理所有与数据报文和数据流相关socket的一个单线程帮助类。在此处,该类负责管理与WifiDisplay播放相关的socket,其中相关的数据传递和消息返回通过AMessage类对象和方法进行。WifiDisplaySource光看命名就知道,其主要负责WifiDisplaysource端的开启关闭,以及与其相关的建立Rtsp服务器,管理所有支持的协议连接、数据流传递以及各个状态之间转换处理等内容。此外,该类还定义了关闭WifiDisplay source端,停止相关线程、关闭socket以及释放资源等内容。

至此,有关WifiDisplay设备连接和建立数据流的流程已经交代清楚了,可以看到应用层建立的连接是与source端相关的。Sink端的主程序在frameworks/av/media/libstagefright/wifi-display/wfd.cpp中,与sink端实现相关的程序在frameworks/av/media/libstagefright/wifi-display/sink目录下面。关于source如何建立rtsp连接,开始通信,各个状态之间的转换以及与sink端的交互将在下回介绍。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值