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