WifiDisplayController.java revision c5df37c285221d0fb113f55b9e78b35632241d3f
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.display; 18 19import com.android.internal.util.DumpUtils; 20 21import android.content.BroadcastReceiver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.hardware.display.WifiDisplay; 26import android.media.RemoteDisplay; 27import android.net.NetworkInfo; 28import android.net.wifi.p2p.WifiP2pConfig; 29import android.net.wifi.p2p.WifiP2pDevice; 30import android.net.wifi.p2p.WifiP2pDeviceList; 31import android.net.wifi.p2p.WifiP2pGroup; 32import android.net.wifi.p2p.WifiP2pManager; 33import android.net.wifi.p2p.WifiP2pWfdInfo; 34import android.net.wifi.p2p.WifiP2pManager.ActionListener; 35import android.net.wifi.p2p.WifiP2pManager.Channel; 36import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener; 37import android.net.wifi.p2p.WifiP2pManager.PeerListListener; 38import android.os.Handler; 39import android.util.Slog; 40import android.view.Surface; 41 42import java.io.PrintWriter; 43import java.net.Inet4Address; 44import java.net.InetAddress; 45import java.net.NetworkInterface; 46import java.net.SocketException; 47import java.util.ArrayList; 48import java.util.Enumeration; 49 50/** 51 * Manages all of the various asynchronous interactions with the {@link WifiP2pManager} 52 * on behalf of {@link WifiDisplayAdapter}. 53 * <p> 54 * This code is isolated from {@link WifiDisplayAdapter} so that we can avoid 55 * accidentally introducing any deadlocks due to the display manager calling 56 * outside of itself while holding its lock. It's also way easier to write this 57 * asynchronous code if we can assume that it is single-threaded. 58 * </p><p> 59 * The controller must be instantiated on the handler thread. 60 * </p> 61 */ 62final class WifiDisplayController implements DumpUtils.Dump { 63 private static final String TAG = "WifiDisplayController"; 64 private static final boolean DEBUG = false; 65 66 private static final int DEFAULT_CONTROL_PORT = 7236; 67 private static final int MAX_THROUGHPUT = 50; 68 private static final int CONNECTION_TIMEOUT_SECONDS = 30; 69 private static final int RTSP_TIMEOUT_SECONDS = 15; 70 71 private static final int DISCOVER_PEERS_MAX_RETRIES = 10; 72 private static final int DISCOVER_PEERS_RETRY_DELAY_MILLIS = 500; 73 74 private static final int CONNECT_MAX_RETRIES = 3; 75 private static final int CONNECT_RETRY_DELAY_MILLIS = 500; 76 77 private final Context mContext; 78 private final Handler mHandler; 79 private final Listener mListener; 80 private final WifiP2pManager mWifiP2pManager; 81 private final Channel mWifiP2pChannel; 82 83 private boolean mWifiP2pEnabled; 84 private boolean mWfdEnabled; 85 private boolean mWfdEnabling; 86 private NetworkInfo mNetworkInfo; 87 88 private final ArrayList<WifiP2pDevice> mKnownWifiDisplayPeers = 89 new ArrayList<WifiP2pDevice>(); 90 91 // True if there is a call to discoverPeers in progress. 92 private boolean mDiscoverPeersInProgress; 93 94 // Number of discover peers retries remaining. 95 private int mDiscoverPeersRetriesLeft; 96 97 // The device to which we want to connect, or null if we want to be disconnected. 98 private WifiP2pDevice mDesiredDevice; 99 100 // The device to which we are currently connecting, or null if we have already connected 101 // or are not trying to connect. 102 private WifiP2pDevice mConnectingDevice; 103 104 // The device to which we are currently connected, which means we have an active P2P group. 105 private WifiP2pDevice mConnectedDevice; 106 107 // The group info obtained after connecting. 108 private WifiP2pGroup mConnectedDeviceGroupInfo; 109 110 // Number of connection retries remaining. 111 private int mConnectionRetriesLeft; 112 113 // The remote display that is listening on the connection. 114 // Created after the Wifi P2P network is connected. 115 private RemoteDisplay mRemoteDisplay; 116 117 // The remote display interface. 118 private String mRemoteDisplayInterface; 119 120 // True if RTSP has connected. 121 private boolean mRemoteDisplayConnected; 122 123 public WifiDisplayController(Context context, Handler handler, Listener listener) { 124 mContext = context; 125 mHandler = handler; 126 mListener = listener; 127 128 mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE); 129 mWifiP2pChannel = mWifiP2pManager.initialize(context, handler.getLooper(), null); 130 131 IntentFilter intentFilter = new IntentFilter(); 132 intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); 133 intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); 134 intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); 135 context.registerReceiver(mWifiP2pReceiver, intentFilter); 136 } 137 138 public void dump(PrintWriter pw) { 139 pw.println("mWifiP2pEnabled=" + mWifiP2pEnabled); 140 pw.println("mWfdEnabled=" + mWfdEnabled); 141 pw.println("mWfdEnabling=" + mWfdEnabling); 142 pw.println("mNetworkInfo=" + mNetworkInfo); 143 pw.println("mDiscoverPeersInProgress=" + mDiscoverPeersInProgress); 144 pw.println("mDiscoverPeersRetriesLeft=" + mDiscoverPeersRetriesLeft); 145 pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice)); 146 pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice)); 147 pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice)); 148 pw.println("mConnectionRetriesLeft=" + mConnectionRetriesLeft); 149 pw.println("mRemoteDisplay=" + mRemoteDisplay); 150 pw.println("mRemoteDisplayInterface=" + mRemoteDisplayInterface); 151 pw.println("mRemoteDisplayConnected=" + mRemoteDisplayConnected); 152 153 pw.println("mKnownWifiDisplayPeers: size=" + mKnownWifiDisplayPeers.size()); 154 for (WifiP2pDevice device : mKnownWifiDisplayPeers) { 155 pw.println(" " + describeWifiP2pDevice(device)); 156 } 157 } 158 159 public void requestScan() { 160 discoverPeers(); 161 } 162 163 public void requestConnect(String address) { 164 for (WifiP2pDevice device : mKnownWifiDisplayPeers) { 165 if (device.deviceAddress.equals(address)) { 166 connect(device); 167 } 168 } 169 } 170 171 public void requestDisconnect() { 172 disconnect(); 173 } 174 175 private void enableWfd() { 176 if (!mWfdEnabled && !mWfdEnabling) { 177 mWfdEnabling = true; 178 179 WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo(); 180 wfdInfo.setWfdEnabled(true); 181 wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE); 182 wfdInfo.setSessionAvailable(true); 183 wfdInfo.setControlPort(DEFAULT_CONTROL_PORT); 184 wfdInfo.setMaxThroughput(MAX_THROUGHPUT); 185 mWifiP2pManager.setWFDInfo(mWifiP2pChannel, wfdInfo, new ActionListener() { 186 @Override 187 public void onSuccess() { 188 if (DEBUG) { 189 Slog.d(TAG, "Successfully set WFD info."); 190 } 191 if (mWfdEnabling) { 192 mWfdEnabling = false; 193 setWfdEnabled(true); 194 } 195 } 196 197 @Override 198 public void onFailure(int reason) { 199 if (DEBUG) { 200 Slog.d(TAG, "Failed to set WFD info with reason " + reason + "."); 201 } 202 mWfdEnabling = false; 203 } 204 }); 205 } 206 } 207 208 private void setWfdEnabled(final boolean enabled) { 209 if (mWfdEnabled != enabled) { 210 mWfdEnabled = enabled; 211 mHandler.post(new Runnable() { 212 @Override 213 public void run() { 214 mListener.onEnablementChanged(enabled); 215 } 216 }); 217 } 218 } 219 220 private void discoverPeers() { 221 if (!mDiscoverPeersInProgress) { 222 mDiscoverPeersInProgress = true; 223 mDiscoverPeersRetriesLeft = DISCOVER_PEERS_MAX_RETRIES; 224 handleScanStarted(); 225 tryDiscoverPeers(); 226 } 227 } 228 229 private void tryDiscoverPeers() { 230 mWifiP2pManager.discoverPeers(mWifiP2pChannel, new ActionListener() { 231 @Override 232 public void onSuccess() { 233 if (DEBUG) { 234 Slog.d(TAG, "Discover peers succeeded. Requesting peers now."); 235 } 236 237 mDiscoverPeersInProgress = false; 238 requestPeers(); 239 } 240 241 @Override 242 public void onFailure(int reason) { 243 if (DEBUG) { 244 Slog.d(TAG, "Discover peers failed with reason " + reason + "."); 245 } 246 247 if (mDiscoverPeersInProgress) { 248 if (reason == 0 && mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) { 249 mHandler.postDelayed(new Runnable() { 250 @Override 251 public void run() { 252 if (mDiscoverPeersInProgress) { 253 if (mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) { 254 mDiscoverPeersRetriesLeft -= 1; 255 if (DEBUG) { 256 Slog.d(TAG, "Retrying discovery. Retries left: " 257 + mDiscoverPeersRetriesLeft); 258 } 259 tryDiscoverPeers(); 260 } else { 261 handleScanFinished(); 262 mDiscoverPeersInProgress = false; 263 } 264 } 265 } 266 }, DISCOVER_PEERS_RETRY_DELAY_MILLIS); 267 } else { 268 handleScanFinished(); 269 mDiscoverPeersInProgress = false; 270 } 271 } 272 } 273 }); 274 } 275 276 private void requestPeers() { 277 mWifiP2pManager.requestPeers(mWifiP2pChannel, new PeerListListener() { 278 @Override 279 public void onPeersAvailable(WifiP2pDeviceList peers) { 280 if (DEBUG) { 281 Slog.d(TAG, "Received list of peers."); 282 } 283 284 mKnownWifiDisplayPeers.clear(); 285 for (WifiP2pDevice device : peers.getDeviceList()) { 286 if (DEBUG) { 287 Slog.d(TAG, " " + describeWifiP2pDevice(device)); 288 } 289 290 if (isWifiDisplay(device)) { 291 mKnownWifiDisplayPeers.add(device); 292 } 293 } 294 295 handleScanFinished(); 296 } 297 }); 298 } 299 300 private void handleScanStarted() { 301 mHandler.post(new Runnable() { 302 @Override 303 public void run() { 304 mListener.onScanStarted(); 305 } 306 }); 307 } 308 309 private void handleScanFinished() { 310 final int count = mKnownWifiDisplayPeers.size(); 311 final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count); 312 for (int i = 0; i < count; i++) { 313 displays[i] = createWifiDisplay(mKnownWifiDisplayPeers.get(i)); 314 } 315 316 mHandler.post(new Runnable() { 317 @Override 318 public void run() { 319 mListener.onScanFinished(displays); 320 } 321 }); 322 } 323 324 private void connect(final WifiP2pDevice device) { 325 if (mDesiredDevice != null 326 && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) { 327 if (DEBUG) { 328 Slog.d(TAG, "connect: nothing to do, already connecting to " 329 + describeWifiP2pDevice(device)); 330 } 331 return; 332 } 333 334 if (mConnectedDevice != null 335 && !mConnectedDevice.deviceAddress.equals(device.deviceAddress) 336 && mDesiredDevice == null) { 337 if (DEBUG) { 338 Slog.d(TAG, "connect: nothing to do, already connected to " 339 + describeWifiP2pDevice(device) + " and not part way through " 340 + "connecting to a different device."); 341 } 342 return; 343 } 344 345 mDesiredDevice = device; 346 mConnectionRetriesLeft = CONNECT_MAX_RETRIES; 347 updateConnection(); 348 } 349 350 private void disconnect() { 351 mDesiredDevice = null; 352 updateConnection(); 353 } 354 355 private void retryConnection() { 356 if (mDesiredDevice != null && mConnectedDevice != mDesiredDevice 357 && mConnectionRetriesLeft > 0) { 358 mConnectionRetriesLeft -= 1; 359 Slog.i(TAG, "Retrying Wifi display connection. Retries left: " 360 + mConnectionRetriesLeft); 361 362 // Cheap hack. Make a new instance of the device object so that we 363 // can distinguish it from the previous connection attempt. 364 // This will cause us to tear everything down before we try again. 365 mDesiredDevice = new WifiP2pDevice(mDesiredDevice); 366 updateConnection(); 367 } 368 } 369 370 /** 371 * This function is called repeatedly after each asynchronous operation 372 * until all preconditions for the connection have been satisfied and the 373 * connection is established (or not). 374 */ 375 private void updateConnection() { 376 // Step 1. Before we try to connect to a new device, tell the system we 377 // have disconnected from the old one. 378 if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) { 379 Slog.i(TAG, "Stopped listening for RTSP connection on " + mRemoteDisplayInterface 380 + " from Wifi display: " + mConnectedDevice.deviceName); 381 382 mRemoteDisplay.dispose(); 383 mRemoteDisplay = null; 384 mRemoteDisplayInterface = null; 385 mRemoteDisplayConnected = false; 386 mHandler.removeCallbacks(mRtspTimeout); 387 388 mHandler.post(new Runnable() { 389 @Override 390 public void run() { 391 mListener.onDisplayDisconnected(); 392 } 393 }); 394 395 // continue to next step 396 } 397 398 // Step 2. Before we try to connect to a new device, disconnect from the old one. 399 if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) { 400 Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName); 401 402 final WifiP2pDevice oldDevice = mConnectedDevice; 403 mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() { 404 @Override 405 public void onSuccess() { 406 Slog.i(TAG, "Disconnected from Wifi display: " + oldDevice.deviceName); 407 next(); 408 } 409 410 @Override 411 public void onFailure(int reason) { 412 Slog.i(TAG, "Failed to disconnect from Wifi display: " 413 + oldDevice.deviceName + ", reason=" + reason); 414 next(); 415 } 416 417 private void next() { 418 if (mConnectedDevice == oldDevice) { 419 mConnectedDevice = null; 420 updateConnection(); 421 } 422 } 423 }); 424 return; // wait for asynchronous callback 425 } 426 427 // Step 3. Before we try to connect to a new device, stop trying to connect 428 // to the old one. 429 if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) { 430 Slog.i(TAG, "Canceling connection to Wifi display: " + mConnectingDevice.deviceName); 431 432 mHandler.removeCallbacks(mConnectionTimeout); 433 434 final WifiP2pDevice oldDevice = mConnectingDevice; 435 mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() { 436 @Override 437 public void onSuccess() { 438 Slog.i(TAG, "Canceled connection to Wifi display: " + oldDevice.deviceName); 439 next(); 440 } 441 442 @Override 443 public void onFailure(int reason) { 444 Slog.i(TAG, "Failed to cancel connection to Wifi display: " 445 + oldDevice.deviceName + ", reason=" + reason); 446 next(); 447 } 448 449 private void next() { 450 if (mConnectingDevice == oldDevice) { 451 mConnectingDevice = null; 452 updateConnection(); 453 } 454 } 455 }); 456 return; // wait for asynchronous callback 457 } 458 459 // Step 4. If we wanted to disconnect, then mission accomplished. 460 if (mDesiredDevice == null) { 461 return; // done 462 } 463 464 // Step 5. Try to connect. 465 if (mConnectedDevice == null && mConnectingDevice == null) { 466 Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName); 467 468 mConnectingDevice = mDesiredDevice; 469 WifiP2pConfig config = new WifiP2pConfig(); 470 config.deviceAddress = mConnectingDevice.deviceAddress; 471 472 final WifiDisplay display = createWifiDisplay(mConnectingDevice); 473 mHandler.post(new Runnable() { 474 @Override 475 public void run() { 476 mListener.onDisplayConnecting(display); 477 } 478 }); 479 480 final WifiP2pDevice newDevice = mDesiredDevice; 481 mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() { 482 @Override 483 public void onSuccess() { 484 // The connection may not yet be established. We still need to wait 485 // for WIFI_P2P_CONNECTION_CHANGED_ACTION. However, we might never 486 // get that broadcast, so we register a timeout. 487 Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName); 488 489 mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000); 490 } 491 492 @Override 493 public void onFailure(int reason) { 494 if (mConnectingDevice == newDevice) { 495 Slog.i(TAG, "Failed to initiate connection to Wifi display: " 496 + newDevice.deviceName + ", reason=" + reason); 497 mConnectingDevice = null; 498 handleConnectionFailure(false); 499 } 500 } 501 }); 502 return; // wait for asynchronous callback 503 } 504 505 // Step 6. Listen for incoming connections. 506 if (mConnectedDevice != null && mRemoteDisplay == null) { 507 Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo); 508 if (addr == null) { 509 Slog.i(TAG, "Failed to get local interface address for communicating " 510 + "with Wifi display: " + mConnectedDevice.deviceName); 511 handleConnectionFailure(false); 512 return; // done 513 } 514 515 final WifiP2pDevice oldDevice = mConnectedDevice; 516 final int port = getPortNumber(mConnectedDevice); 517 final String iface = addr.getHostAddress() + ":" + port; 518 mRemoteDisplayInterface = iface; 519 520 Slog.i(TAG, "Listening for RTSP connection on " + iface 521 + " from Wifi display: " + mConnectedDevice.deviceName); 522 523 mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() { 524 @Override 525 public void onDisplayConnected(final Surface surface, 526 final int width, final int height, final int flags) { 527 if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) { 528 Slog.i(TAG, "Opened RTSP connection with Wifi display: " 529 + mConnectedDevice.deviceName); 530 mRemoteDisplayConnected = true; 531 mHandler.removeCallbacks(mRtspTimeout); 532 533 final WifiDisplay display = createWifiDisplay(mConnectedDevice); 534 mHandler.post(new Runnable() { 535 @Override 536 public void run() { 537 mListener.onDisplayConnected(display, 538 surface, width, height, flags); 539 } 540 }); 541 } 542 } 543 544 @Override 545 public void onDisplayDisconnected() { 546 if (mConnectedDevice == oldDevice) { 547 Slog.i(TAG, "Closed RTSP connection with Wifi display: " 548 + mConnectedDevice.deviceName); 549 mHandler.removeCallbacks(mRtspTimeout); 550 disconnect(); 551 } 552 } 553 554 @Override 555 public void onDisplayError(int error) { 556 if (mConnectedDevice == oldDevice) { 557 Slog.i(TAG, "Lost RTSP connection with Wifi display due to error " 558 + error + ": " + mConnectedDevice.deviceName); 559 mHandler.removeCallbacks(mRtspTimeout); 560 handleConnectionFailure(false); 561 } 562 } 563 }, mHandler); 564 565 mHandler.postDelayed(mRtspTimeout, RTSP_TIMEOUT_SECONDS * 1000); 566 } 567 } 568 569 private void handleStateChanged(boolean enabled) { 570 if (mWifiP2pEnabled != enabled) { 571 mWifiP2pEnabled = enabled; 572 if (enabled) { 573 if (!mWfdEnabled) { 574 enableWfd(); 575 } 576 } else { 577 setWfdEnabled(false); 578 disconnect(); 579 } 580 } 581 } 582 583 private void handlePeersChanged() { 584 if (mWifiP2pEnabled) { 585 if (mWfdEnabled) { 586 requestPeers(); 587 } else { 588 enableWfd(); 589 } 590 } 591 } 592 593 private void handleConnectionChanged(NetworkInfo networkInfo) { 594 mNetworkInfo = networkInfo; 595 if (mWfdEnabled && networkInfo.isConnected()) { 596 if (mDesiredDevice != null) { 597 mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, new GroupInfoListener() { 598 @Override 599 public void onGroupInfoAvailable(WifiP2pGroup info) { 600 if (DEBUG) { 601 Slog.d(TAG, "Received group info: " + describeWifiP2pGroup(info)); 602 } 603 604 if (mConnectingDevice != null && !info.contains(mConnectingDevice)) { 605 Slog.i(TAG, "Aborting connection to Wifi display because " 606 + "the current P2P group does not contain the device " 607 + "we expected to find: " + mConnectingDevice.deviceName); 608 handleConnectionFailure(false); 609 return; 610 } 611 612 if (mDesiredDevice != null && !info.contains(mDesiredDevice)) { 613 disconnect(); 614 return; 615 } 616 617 if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) { 618 Slog.i(TAG, "Connected to Wifi display: " 619 + mConnectingDevice.deviceName); 620 621 mHandler.removeCallbacks(mConnectionTimeout); 622 mConnectedDeviceGroupInfo = info; 623 mConnectedDevice = mConnectingDevice; 624 mConnectingDevice = null; 625 updateConnection(); 626 } 627 } 628 }); 629 } 630 } else { 631 disconnect(); 632 633 // After disconnection for a group, for some reason we have a tendency 634 // to get a peer change notification with an empty list of peers. 635 // Perform a fresh scan. 636 if (mWfdEnabled) { 637 requestPeers(); 638 } 639 } 640 } 641 642 private final Runnable mConnectionTimeout = new Runnable() { 643 @Override 644 public void run() { 645 if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) { 646 Slog.i(TAG, "Timed out waiting for Wifi display connection after " 647 + CONNECTION_TIMEOUT_SECONDS + " seconds: " 648 + mConnectingDevice.deviceName); 649 handleConnectionFailure(true); 650 } 651 } 652 }; 653 654 private final Runnable mRtspTimeout = new Runnable() { 655 @Override 656 public void run() { 657 if (mConnectedDevice != null 658 && mRemoteDisplay != null && !mRemoteDisplayConnected) { 659 Slog.i(TAG, "Timed out waiting for Wifi display RTSP connection after " 660 + RTSP_TIMEOUT_SECONDS + " seconds: " 661 + mConnectedDevice.deviceName); 662 handleConnectionFailure(true); 663 } 664 } 665 }; 666 667 private void handleConnectionFailure(boolean timeoutOccurred) { 668 Slog.i(TAG, "Wifi display connection failed!"); 669 670 mHandler.post(new Runnable() { 671 @Override 672 public void run() { 673 mListener.onDisplayConnectionFailed(); 674 } 675 }); 676 677 if (mDesiredDevice != null) { 678 if (mConnectionRetriesLeft > 0) { 679 mHandler.postDelayed(new Runnable() { 680 @Override 681 public void run() { 682 retryConnection(); 683 } 684 }, timeoutOccurred ? 0 : CONNECT_RETRY_DELAY_MILLIS); 685 } else { 686 disconnect(); 687 } 688 } 689 } 690 691 private static Inet4Address getInterfaceAddress(WifiP2pGroup info) { 692 NetworkInterface iface; 693 try { 694 iface = NetworkInterface.getByName(info.getInterface()); 695 } catch (SocketException ex) { 696 Slog.w(TAG, "Could not obtain address of network interface " 697 + info.getInterface(), ex); 698 return null; 699 } 700 701 Enumeration<InetAddress> addrs = iface.getInetAddresses(); 702 while (addrs.hasMoreElements()) { 703 InetAddress addr = addrs.nextElement(); 704 if (addr instanceof Inet4Address) { 705 return (Inet4Address)addr; 706 } 707 } 708 709 Slog.w(TAG, "Could not obtain address of network interface " 710 + info.getInterface() + " because it had no IPv4 addresses."); 711 return null; 712 } 713 714 private static int getPortNumber(WifiP2pDevice device) { 715 if (device.deviceName.startsWith("DIRECT-") 716 && device.deviceName.endsWith("Broadcom")) { 717 // These dongles ignore the port we broadcast in our WFD IE. 718 return 8554; 719 } 720 return DEFAULT_CONTROL_PORT; 721 } 722 723 private static boolean isWifiDisplay(WifiP2pDevice device) { 724 return device.wfdInfo != null 725 && device.wfdInfo.isWfdEnabled() 726 && isPrimarySinkDeviceType(device.wfdInfo.getDeviceType()); 727 } 728 729 private static boolean isPrimarySinkDeviceType(int deviceType) { 730 return deviceType == WifiP2pWfdInfo.PRIMARY_SINK 731 || deviceType == WifiP2pWfdInfo.SOURCE_OR_PRIMARY_SINK; 732 } 733 734 private static String describeWifiP2pDevice(WifiP2pDevice device) { 735 return device != null ? device.toString().replace('\n', ',') : "null"; 736 } 737 738 private static String describeWifiP2pGroup(WifiP2pGroup group) { 739 return group != null ? group.toString().replace('\n', ',') : "null"; 740 } 741 742 private static WifiDisplay createWifiDisplay(WifiP2pDevice device) { 743 return new WifiDisplay(device.deviceAddress, device.deviceName); 744 } 745 746 private final BroadcastReceiver mWifiP2pReceiver = new BroadcastReceiver() { 747 @Override 748 public void onReceive(Context context, Intent intent) { 749 final String action = intent.getAction(); 750 if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) { 751 boolean enabled = (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, 752 WifiP2pManager.WIFI_P2P_STATE_DISABLED)) == 753 WifiP2pManager.WIFI_P2P_STATE_ENABLED; 754 if (DEBUG) { 755 Slog.d(TAG, "Received WIFI_P2P_STATE_CHANGED_ACTION: enabled=" 756 + enabled); 757 } 758 759 handleStateChanged(enabled); 760 } else if (action.equals(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)) { 761 if (DEBUG) { 762 Slog.d(TAG, "Received WIFI_P2P_PEERS_CHANGED_ACTION."); 763 } 764 765 handlePeersChanged(); 766 } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) { 767 NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra( 768 WifiP2pManager.EXTRA_NETWORK_INFO); 769 if (DEBUG) { 770 Slog.d(TAG, "Received WIFI_P2P_CONNECTION_CHANGED_ACTION: networkInfo=" 771 + networkInfo); 772 } 773 774 handleConnectionChanged(networkInfo); 775 } 776 } 777 }; 778 779 /** 780 * Called on the handler thread when displays are connected or disconnected. 781 */ 782 public interface Listener { 783 void onEnablementChanged(boolean enabled); 784 785 void onScanStarted(); 786 void onScanFinished(WifiDisplay[] knownDisplays); 787 788 void onDisplayConnecting(WifiDisplay display); 789 void onDisplayConnectionFailed(); 790 void onDisplayConnected(WifiDisplay display, 791 Surface surface, int width, int height, int flags); 792 void onDisplayDisconnected(); 793 } 794} 795