wifi display学习总结

世界杯球童 2025-08-27 02:30:10 1582

① DisplayManagerService向DisplayManagerHandler 发送两个消息:

MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER、MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPT;

② DisplayManagerHandler 调用DisplayManagerService中两个函数注册displayAdapter;

③ DisplayManagerService调用registerWifiDisplayAdapterLocked()初始化一个WifiDisplayAdapter;

④ WifiDisplayAdapter创建一个WifiDisplayController,并注册wifip2p相关的广播和监听Settings.Global数据库里的三个表WIFI_DISPLAY_ON、WIFI_DISPLAY_CERTIFICATION_ON和WIFI_DISPLAY_WPS_CONFIG。

2.MediaRouterService和MediaRouter初始化

MediaRouter中主要通过Static对象来实现其大多数的方法,先创建一个Static对象,再调用其startMonitoringRoutes方法:

1. public MediaRouter(Context context) {

2. synchronized (Static.class) {

3. if (sStatic == null) {

4. final Context appContext = context.getApplicationContext();

5. sStatic = new Static(appContext);

6. sStatic.startMonitoringRoutes(appContext);

7. }

8. }

9. }

1. void startMonitoringRoutes(Context appContext) {

2. mDefaultAudioVideo = new RouteInfo(mSystemCategory);

3. mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;

4. mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;

5. mDefaultAudioVideo.updatePresentationDisplay();

6. addRouteStatic(mDefaultAudioVideo);

7.

8. // This will select the active wifi display route if there is one.

9. updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());

10.

11. appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(),

12. new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));

13. appContext.registerReceiver(new VolumeChangeReceiver(),

14. new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));

15.

16. mDisplayService.registerDisplayListener(this, mHandler);

17.

18.

19. // Bind to the media router service.

20. rebindAsUser(UserHandle.myUserId());

21.

22. // Select the default route if the above didn't sync us up

23. // appropriately with relevant system state.

24. if (mSelectedRoute == null) {

25. selectDefaultRouteStatic();

26. }

27. }

这里通过注册ACTION_WIFI_DISPLAY_STATUS_CHANGED广播,监听WifiDisplay的状态改变,并且通过rebindAsUser(UserHandle.myUserId())绑定MediaRouterService。

(二)WifiDisplaySettings初始化

当用户进入WifiDisplaySettings界面,会调用其对应的onCreate和onStart方法:

1. public void onCreate(Bundle icicle) {

2. super.onCreate(icicle);

3.

4. final Context context = getActivity();

5. mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);

6. mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);

7. mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);

8. mWifiP2pChannel = mWifiP2pManager.initialize(context, Looper.getMainLooper(), null);

9.

10. addPreferencesFromResource(R.xml.wifi_display_settings);

11. setHasOptionsMenu(true);

12. }

13.

14. public void onStart() {

15. super.onStart();

16. mStarted = true;

17.

18. final Context context = getActivity();

19. IntentFilter filter = new IntentFilter();

20. filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);

21. context.registerReceiver(mReceiver, filter);

22.

23. getContentResolver().registerContentObserver(Settings.Global.getUriFor(

24. Settings.Global.WIFI_DISPLAY_ON), false, mSettingsObserver);

25. getContentResolver().registerContentObserver(Settings.Global.getUriFor(

26. Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON), false, mSettingsObserver);

27. getContentResolver().registerContentObserver(Settings.Global.getUriFor(

28. Settings.Global.WIFI_DISPLAY_WPS_CONFIG), false, mSettingsObserver);

29.

30. mRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mRouterCallback,

31. MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);

32.

33. update(CHANGE_ALL);

34. }

首先注册对ACTION_WIFI_DISPLAY_STATUS_CHANGED的receiver,这个broadcast会在WifiDisplayAdapter里面当wifi display的状态发生改变时发送,包括扫描到新的设备、开始连接、连接成功、断开等消息都会被这个receiver接收到。然后类似WifiDisplayController一样,注册一些对数据库改变的ContentObserver

(三)WifiDisplay扫描

当用户点击了optionMenu中enablewifi display后:

1. public boolean onOptionsItemSelected(MenuItem item) {

2. switch (item.getItemId()) {

3. case MENU_ID_ENABLE_WIFI_DISPLAY:

4. mWifiDisplayOnSetting = !item.isChecked();

5. item.setChecked(mWifiDisplayOnSetting);

6. Settings.Global.putInt(getContentResolver(),

7. Settings.Global.WIFI_DISPLAY_ON, mWifiDisplayOnSetting ? 1 : 0);

首先改变menu状态,然后向Settings.Global.WIFI_DISPLAY_ON数据写入1,WifiDisplaySettings在监控到这个值的变化后,主要是调用MediaRouter和DisplayManager的方法去获取系统中已经扫描到的remote display设备,同时WifiDisplayController也监控到该值的变化,处理如下:

1. private void updateSettings() {

2. final ContentResolver resolver = mContext.getContentResolver();

3. mWifiDisplayOnSetting = Settings.Global.getInt(resolver,

4. Settings.Global.WIFI_DISPLAY_ON, 0) != 0;

5. mWifiDisplayCertMode = Settings.Global.getInt(resolver,

6. Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0;

7.

8. mWifiDisplayWpsConfig = WpsInfo.INVALID;

9. if (mWifiDisplayCertMode) {

10. mWifiDisplayWpsConfig = Settings.Global.getInt(resolver,

11. Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID);

12. }

13.

14. updateWfdEnableState();

15. }

设置mWifiDisplayOnSetting 为1,然后调用updateWfdEnableState()更新wfd的状态:

1. private void updateWfdEnableState() {

2. if (mWifiDisplayOnSetting && mWifiP2pEnabled) {

3. // WFD should be enabled.

4. if (!mWfdEnabled && !mWfdEnabling) {

5. mWfdEnabling = true;

6.

7. WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();

8. wfdInfo.setWfdEnabled(true);

9. wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE);

10. wfdInfo.setSessionAvailable(true);

11. wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);

12. wfdInfo.setMaxThroughput(MAX_THROUGHPUT);

13. mWifiP2pManager.setWFDInfo(mWifiP2pChannel, wfdInfo, new ActionListener({

14. @Override

15. public void onSuccess() {

16. if (DEBUG) {

17. Slog.d(TAG, "Successfully set WFD info.");

18. }

19. if (mWfdEnabling) {

20. mWfdEnabling = false;

21. mWfdEnabled = true;

22. reportFeatureState();

23. updateScanState();

24. }

25. }

26.

27. @Override

28. public void onFailure(int reason) {

29. if (DEBUG) {

30. Slog.d(TAG, "Failed to set WFD info with reason " + reason + ".");

31. }

32. mWfdEnabling = false;

33. }

34. });

35. }

首先调用WifiP2pMananger的setWFDInfo把与wifi display相关的信息设置到wpa_supplicant,这些信息包括enable状态、device type(指为source还是sink)、sessionavailable(当前可否连接)、control port(用于rtsp连接)、maxThroughput(吞吐量),这些信息最终会随着P2P的IE信息在扫描阶段被对方知道。接着会调用reportFeatureState来通知WifiDisplayAdapter相应状态的变化:

1. private void reportFeatureState() {

2. final int featureState = computeFeatureState();

3. mHandler.post(new Runnable() {

4. @Override

5. public void run() {

6. mListener.onFeatureStateChanged(featureState);

7. }

8. });

9. }

直接回调WifiDisplayListener的onFeatureStateChanged,它由WifiDisplayAdapter注册,查看这部分的实现:

public void onFeatureStateChanged(int featureState) {

2. synchronized (getSyncRoot()) {

3. if (mFeatureState != featureState) {

4. mFeatureState = featureState;

5. scheduleStatusChangedBroadcastLocked();

6. }

7. }

8. }

9.

10. private void scheduleStatusChangedBroadcastLocked() {

11. mCurrentStatus = null;

12. if (!mPendingStatusChangeBroadcast) {

13. mPendingStatusChangeBroadcast = true;

14. mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);

15. }

16. }

通过mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST),在WifiDisplayHandler中处理:

1. public void handleMessage(Message msg) {

2. switch (msg.what) {

3. case MSG_SEND_STATUS_CHANGE_BROADCAST:

4. handleSendStatusChangeBroadcast();

5. break;

6.

7. case MSG_UPDATE_NOTIFICATION:

8. handleUpdateNotification();

9. break;

10. }

11.

12. private void handleSendStatusChangeBroadcast() {

13. final Intent intent;

14. synchronized (getSyncRoot()) {

15. if (!mPendingStatusChangeBroadcast) {

16. return;

17. }

18.

19. mPendingStatusChangeBroadcast = false;

20. intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);

21. intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);

22. intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,

23. getWifiDisplayStatusLocked());

24. }

25.

26. // Send protected broadcast about wifi display status to registered receivers.

27. getContext().sendBroadcastAsUser(intent, UserHandle.ALL);

这里发送广播ACTION_WIFI_DISPLAY_STATUS_CHANGED,通知MediaRouter调用updateWifiDisplayStatus来更新状态。

WifiDisplayController在调用reportFeatureState()后还调用了updateScanState():

1. private void updateScanState() {

2. if (mScanRequested && mWfdEnabled && mDesiredDevice == null) {

3. if (!mDiscoverPeersInProgress) {

4. Slog.i(TAG, "Starting Wifi display scan.");

5. mDiscoverPeersInProgress = true;

6. handleScanStarted();

7. tryDiscoverPeers();

8. }

9. }

handleScanStarted用于通知WifiDisplayAdapter扫描开始了, WifiDisplayAdapter也会发broadcast给MediaRouter。接着会调用tryDiscoverPeers,去扫描所有的p2p设备,扫描成功后,调用requestPeers()获取设备信息,之后再将这些设备信息发送到WifiDisplayAdapter中去跟之前保存的设备比较并更新,并返回给MediaRouter的设备列表信息,最后通过发送广播ACTION_WIFI_DISPLAY_STATUS_CHANGED通知WifiDisplaySettings,将从MediaRouter中获取设备信息展现给用户。

(四)WifiDisplay连接

当用户点击设备列表,发起连接,从WifiDisplaySettings调用WifiDisplayManager的connectWifiDisplay()方法,再通过一系列的调用,主要连接的逻辑实现在WifiDisplayController的updateConnection()方法中:

方法里的code非常多,分为六个步骤:

1. private void updateConnection() {

2. // Step 0. Stop scans if necessary to prevent interference while connected.

3. // Resume scans later when no longer attempting to connect.

4. updateScanState();

5.

6. // Step 1. Before we try to connect to a new device, tell the system we

7. // have disconnected from the old one.

8. //在尝试连接到新设备时,需要通知系统这里已经与旧的设备断开连接

9. if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {

10. ...

11. mRemoteDisplay.dispose(); //释放NativeRemoteDisplay资源停止监听

12. mRemoteDisplay = null; //监听返回对象置为空

13. mRemoteDisplayInterface = null; //监听端口置为空

14. mRemoteDisplayConnected = false; //连接标识为未连接

15. mHandler.removeCallbacks(mRtspTimeout);//将挂起的mRtspTimeout线程从消息队列中移除

16.

17. setRemoteSubmixOn(false); //关闭远程混音重建模式

18. unadvertiseDisplay();

19. }

20. // Step 2. Before we try to connect to a new device, disconnect from the old one.

21. if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {

22. ...

23. unadvertiseDisplay();

24.

25. final WifiP2pDevice oldDevice = mConnectedDevice;

26. mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {

27. @Override

28. public void onSuccess() {

29. ...

30. next();

31. }

32.

33. @Override

34. public void onFailure(int reason) {

35. ...

36. next();

37. }

38.

39. private void next() {

40. if (mConnectedDevice == oldDevice) { //确保连接设备已经不是旧的设备否则递归调用该函数

41. mConnectedDevice = null;

42. updateConnection();

43. }

44. }

45. });

46. return;

47. }

48. // Step 3. Before we try to connect to a new device, stop trying to connect

49. // to the old one.

50. if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {

51. ...

52. unadvertiseDisplay();

53. mHandler.removeCallbacks(mConnectionTimeout);

54.

55. final WifiP2pDevice oldDevice = mConnectingDevice;

56. mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() { //在尝试连接到新设备之前,取消正在进行的p2p连接

57. @Override

58. public void onSuccess() {

59. ...

60. next();

61. }

62.

63. @Override

64. public void onFailure(int reason) {

65. ...

66. next();

67. }

68.

69. private void next() {

70. if (mConnectingDevice == oldDevice) {

71. mConnectingDevice = null;

72. updateConnection();

73. }

74. }

75. });

76. return;

77. }

78. // Step 4. If we wanted to disconnect, or we're updating after starting an

79. // autonomous GO, then mission accomplished.

80. // 如果想断开连接,则任务结束

81. if (mDesiredDevice == null) {

82. unadvertiseDisplay();

83. return;

84. }

85.

86. // Step 5. Try to connect.

87. if (mConnectedDevice == null && mConnectingDevice == null) {

88. Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);

89. mConnectingDevice = mDesiredDevice;

90. WifiP2pConfig config = new WifiP2pConfig();

91. config.deviceAddress = mConnectingDevice.deviceAddress;

92. config.groupOwnerIntent = WifiP2pConfig.MIN_GROUP_OWNER_INTENT;

93.

94. WifiDisplay display = createWifiDisplay(mConnectingDevice);

95. advertiseDisplay(display, null, 0, 0, 0);

96.

97. final WifiP2pDevice newDevice = mDesiredDevice;

98. mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {

99. //以特定的配置信息开启P2P连接,如果当前设备不是P2P组的一部分,会建立P2P小组并发起连接请求;如果当前设备是现存P2P组的一部分,则加入该组的邀请会发送至该配对设备。

100.

101. @Override

102. public void onSuccess() {

103. //为了防止连接还没有建立成功,这里设定了等待处理函数,如果在定长时间内还没有接受到WIFI_P2P_CONNECTION_CHANGED_ACTION广播,则按照handleConnectionFailure(true)处理。

104. Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);

105. mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);

106. }

107.

108. @Override

109. public void onFailure(int reason) {

110. if (mConnectingDevice == newDevice) {

111. Slog.i(TAG, "Failed to initiate connection to Wifi display: "

112. + newDevice.deviceName + ", reason=" + reason);

113. mConnectingDevice = null;

114. handleConnectionFailure(false);

115. }

116. }

117. });

118. return;

119. }

120. // Step 6. Listen for incoming RTSP connection.

121. // 根据连接的网络地址和端口号监听Rtsp流连接

122. if (mConnectedDevice != null && mRemoteDisplay == null) {

123. Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);

124. if (addr == null) {

125. Slog.i(TAG, "Failed to get local interface address for communicating "

126. + "with Wifi display: " + mConnectedDevice.deviceName);

127. handleConnectionFailure(false);

128. return; // done

129. }

130.

131. setRemoteSubmixOn(true);

132.

133. final WifiP2pDevice oldDevice = mConnectedDevice;

134. final int port = getPortNumber(mConnectedDevice);

135. final String iface = addr.getHostAddress() + ":" + port;

136. mRemoteDisplayInterface = iface;

137.

138. Slog.i(TAG, "Listening for RTSP connection on " + iface

139. + " from Wifi display: " + mConnectedDevice.deviceName);

140.

141. mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {

142. //开始监听连接上的接口

143. @Override

144. public void onDisplayConnected(Surface surface,

145. int width, int height, int flags) {

146. if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) {

147. Slog.i(TAG, "Opened RTSP connection with Wifi display: "

148. + mConnectedDevice.deviceName);

149. mRemoteDisplayConnected = true;

150. mHandler.removeCallbacks(mRtspTimeout);

151.

152. final WifiDisplay display = createWifiDisplay(mConnectedDevice);

153. advertiseDisplay(display, surface, width, height, flags);

154. }

155. }

156.

157. @Override

158. public void onDisplayDisconnected() {

159. if (mConnectedDevice == oldDevice) {

160. Slog.i(TAG, "Closed RTSP connection with Wifi display: "

161. + mConnectedDevice.deviceName);

162. mHandler.removeCallbacks(mRtspTimeout);

163. disconnect();

164. }

165. }

166.

167. @Override

168. public void onDisplayError(int error) {

169. if (mConnectedDevice == oldDevice) {

170. Slog.i(TAG, "Lost RTSP connection with Wifi display due to error "

171. + error + ": " + mConnectedDevice.deviceName);

172. mHandler.removeCallbacks(mRtspTimeout);

173. handleConnectionFailure(false);

174. }

175. }

176. }, mHandler);

177.

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

179. }

180. }

站点统计