WifiDisplayController.java revision 5e0cc0d7ab29e5fbd11316cd3147ef96d65f8de7
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                        discoverPeers();
183                    }
184                }
185
186                @Override
187                public void onFailure(int reason) {
188                    if (DEBUG) {
189                        Slog.d(TAG, "Failed to set WFD info with reason " + reason + ".");
190                    }
191                    mWfdEnabling = false;
192                }
193            });
194        }
195    }
196
197    private void setWfdEnabled(final boolean enabled) {
198        if (mWfdEnabled != enabled) {
199            mWfdEnabled = enabled;
200            mHandler.post(new Runnable() {
201                @Override
202                public void run() {
203                    mListener.onEnablementChanged(enabled);
204                }
205            });
206        }
207    }
208
209    private void discoverPeers() {
210        if (!mDiscoverPeersInProgress) {
211            mDiscoverPeersInProgress = true;
212            mDiscoverPeersRetriesLeft = DISCOVER_PEERS_MAX_RETRIES;
213            handleScanStarted();
214            tryDiscoverPeers();
215        }
216    }
217
218    private void tryDiscoverPeers() {
219        mWifiP2pManager.discoverPeers(mWifiP2pChannel, new ActionListener() {
220            @Override
221            public void onSuccess() {
222                if (DEBUG) {
223                    Slog.d(TAG, "Discover peers succeeded.  Requesting peers now.");
224                }
225
226                mDiscoverPeersInProgress = false;
227                requestPeers();
228            }
229
230            @Override
231            public void onFailure(int reason) {
232                if (DEBUG) {
233                    Slog.d(TAG, "Discover peers failed with reason " + reason + ".");
234                }
235
236                if (mDiscoverPeersInProgress) {
237                    if (reason == 0 && mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) {
238                        mHandler.postDelayed(new Runnable() {
239                            @Override
240                            public void run() {
241                                if (mDiscoverPeersInProgress) {
242                                    if (mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) {
243                                        mDiscoverPeersRetriesLeft -= 1;
244                                        if (DEBUG) {
245                                            Slog.d(TAG, "Retrying discovery.  Retries left: "
246                                                    + mDiscoverPeersRetriesLeft);
247                                        }
248                                        tryDiscoverPeers();
249                                    } else {
250                                        handleScanFinished();
251                                        mDiscoverPeersInProgress = false;
252                                    }
253                                }
254                            }
255                        }, DISCOVER_PEERS_RETRY_DELAY_MILLIS);
256                    } else {
257                        handleScanFinished();
258                        mDiscoverPeersInProgress = false;
259                    }
260                }
261            }
262        });
263    }
264
265    private void requestPeers() {
266        mWifiP2pManager.requestPeers(mWifiP2pChannel, new PeerListListener() {
267            @Override
268            public void onPeersAvailable(WifiP2pDeviceList peers) {
269                if (DEBUG) {
270                    Slog.d(TAG, "Received list of peers.");
271                }
272
273                mKnownWifiDisplayPeers.clear();
274                for (WifiP2pDevice device : peers.getDeviceList()) {
275                    if (DEBUG) {
276                        Slog.d(TAG, "  " + describeWifiP2pDevice(device));
277                    }
278
279                    if (isWifiDisplay(device)) {
280                        mKnownWifiDisplayPeers.add(device);
281                    }
282                }
283
284                handleScanFinished();
285            }
286        });
287    }
288
289    private void handleScanStarted() {
290        mHandler.post(new Runnable() {
291            @Override
292            public void run() {
293                mListener.onScanStarted();
294            }
295        });
296    }
297
298    private void handleScanFinished() {
299        final int count = mKnownWifiDisplayPeers.size();
300        final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count);
301        for (int i = 0; i < count; i++) {
302            displays[i] = createWifiDisplay(mKnownWifiDisplayPeers.get(i));
303        }
304
305        mHandler.post(new Runnable() {
306            @Override
307            public void run() {
308                mListener.onScanFinished(displays);
309            }
310        });
311    }
312
313    private void connect(final WifiP2pDevice device) {
314        if (mDesiredDevice != null
315                && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) {
316            if (DEBUG) {
317                Slog.d(TAG, "connect: nothing to do, already connecting to "
318                        + describeWifiP2pDevice(device));
319            }
320            return;
321        }
322
323        if (mConnectedDevice != null
324                && !mConnectedDevice.deviceAddress.equals(device.deviceAddress)
325                && mDesiredDevice == null) {
326            if (DEBUG) {
327                Slog.d(TAG, "connect: nothing to do, already connected to "
328                        + describeWifiP2pDevice(device) + " and not part way through "
329                        + "connecting to a different device.");
330            }
331            return;
332        }
333
334        mDesiredDevice = device;
335        mConnectionRetriesLeft = CONNECT_MAX_RETRIES;
336        updateConnection();
337    }
338
339    private void disconnect() {
340        mDesiredDevice = null;
341        updateConnection();
342    }
343
344    private void retryConnection() {
345        if (mDesiredDevice != null && mPublishedDevice != mDesiredDevice
346                && mConnectionRetriesLeft > 0) {
347            mConnectionRetriesLeft -= 1;
348            Slog.i(TAG, "Retrying Wifi display connection.  Retries left: "
349                    + mConnectionRetriesLeft);
350
351            // Cheap hack.  Make a new instance of the device object so that we
352            // can distinguish it from the previous connection attempt.
353            // This will cause us to tear everything down before we try again.
354            mDesiredDevice = new WifiP2pDevice(mDesiredDevice);
355            updateConnection();
356        }
357    }
358
359    /**
360     * This function is called repeatedly after each asynchronous operation
361     * until all preconditions for the connection have been satisfied and the
362     * connection is established (or not).
363     */
364    private void updateConnection() {
365        // Step 1. Before we try to connect to a new device, tell the system we
366        // have disconnected from the old one.
367        if (mPublishedDevice != null && mPublishedDevice != mDesiredDevice) {
368            mHandler.post(new Runnable() {
369                @Override
370                public void run() {
371                    mListener.onDisplayDisconnected();
372                }
373            });
374            mPublishedDevice = null;
375
376            // continue to next step
377        }
378
379        // Step 2. Before we try to connect to a new device, disconnect from the old one.
380        if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
381            Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName);
382
383            final WifiP2pDevice oldDevice = mConnectedDevice;
384            mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
385                @Override
386                public void onSuccess() {
387                    Slog.i(TAG, "Disconnected from Wifi display: " + oldDevice.deviceName);
388                    next();
389                }
390
391                @Override
392                public void onFailure(int reason) {
393                    Slog.i(TAG, "Failed to disconnect from Wifi display: "
394                            + oldDevice.deviceName + ", reason=" + reason);
395                    next();
396                }
397
398                private void next() {
399                    if (mConnectedDevice == oldDevice) {
400                        mConnectedDevice = null;
401                        updateConnection();
402                    }
403                }
404            });
405            return; // wait for asynchronous callback
406        }
407
408        // Step 3. Before we try to connect to a new device, stop trying to connect
409        // to the old one.
410        if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
411            Slog.i(TAG, "Canceling connection to Wifi display: " + mConnectingDevice.deviceName);
412
413            mHandler.removeCallbacks(mConnectionTimeout);
414
415            final WifiP2pDevice oldDevice = mConnectingDevice;
416            mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() {
417                @Override
418                public void onSuccess() {
419                    Slog.i(TAG, "Canceled connection to Wifi display: " + oldDevice.deviceName);
420                    next();
421                }
422
423                @Override
424                public void onFailure(int reason) {
425                    Slog.i(TAG, "Failed to cancel connection to Wifi display: "
426                            + oldDevice.deviceName + ", reason=" + reason);
427                    next();
428                }
429
430                private void next() {
431                    if (mConnectingDevice == oldDevice) {
432                        mConnectingDevice = null;
433                        updateConnection();
434                    }
435                }
436            });
437            return; // wait for asynchronous callback
438        }
439
440        // Step 4. If we wanted to disconnect, then mission accomplished.
441        if (mDesiredDevice == null) {
442            return; // done
443        }
444
445        // Step 5. Try to connect.
446        if (mConnectedDevice == null && mConnectingDevice == null) {
447            Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);
448
449            mConnectingDevice = mDesiredDevice;
450            WifiP2pConfig config = new WifiP2pConfig();
451            config.deviceAddress = mConnectingDevice.deviceAddress;
452
453            final WifiDisplay display = createWifiDisplay(mConnectingDevice);
454            mHandler.post(new Runnable() {
455                @Override
456                public void run() {
457                    mListener.onDisplayConnecting(display);
458                }
459            });
460
461            final WifiP2pDevice newDevice = mDesiredDevice;
462            mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {
463                @Override
464                public void onSuccess() {
465                    // The connection may not yet be established.  We still need to wait
466                    // for WIFI_P2P_CONNECTION_CHANGED_ACTION.  However, we might never
467                    // get that broadcast, so we register a timeout.
468                    Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);
469
470                    mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);
471                }
472
473                @Override
474                public void onFailure(int reason) {
475                    Slog.i(TAG, "Failed to initiate connection to Wifi display: "
476                            + newDevice.deviceName + ", reason=" + reason);
477                    if (mConnectingDevice == newDevice) {
478                        mConnectingDevice = null;
479                        handleConnectionFailure(false);
480                    }
481                }
482            });
483            return; // wait for asynchronous callback
484        }
485
486        // Step 6. Publish the new connection.
487        if (mConnectedDevice != null && mPublishedDevice == null) {
488            Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
489            if (addr == null) {
490                Slog.i(TAG, "Failed to get local interface address for communicating "
491                        + "with Wifi display: " + mConnectedDevice.deviceName);
492                handleConnectionFailure(false);
493                return; // done
494            }
495
496            int port = DEFAULT_CONTROL_PORT;
497            if (mConnectedDevice.deviceName.startsWith("DIRECT-")
498                    && mConnectedDevice.deviceName.endsWith("Broadcom")) {
499                // These dongles ignore the port we broadcast in our WFD IE.
500                port = 8554;
501            }
502
503            final WifiDisplay display = createWifiDisplay(mConnectedDevice);
504            final String iface = addr.getHostAddress() + ":" + port;
505
506            mPublishedDevice = mConnectedDevice;
507            mHandler.post(new Runnable() {
508                @Override
509                public void run() {
510                    mListener.onDisplayConnected(display, iface);
511                }
512            });
513        }
514    }
515
516    private void handleStateChanged(boolean enabled) {
517        if (mWifiP2pEnabled != enabled) {
518            mWifiP2pEnabled = enabled;
519            if (enabled) {
520                if (mWfdEnabled) {
521                    discoverPeers();
522                } else {
523                    enableWfd();
524                }
525            } else {
526                setWfdEnabled(false);
527                disconnect();
528            }
529        }
530    }
531
532    private void handlePeersChanged() {
533        if (mWifiP2pEnabled) {
534            if (mWfdEnabled) {
535                requestPeers();
536            } else {
537                enableWfd();
538            }
539        }
540    }
541
542    private void handleConnectionChanged(NetworkInfo networkInfo) {
543        mNetworkInfo = networkInfo;
544        if (mWfdEnabled && networkInfo.isConnected()) {
545            if (mDesiredDevice != null) {
546                mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, new GroupInfoListener() {
547                    @Override
548                    public void onGroupInfoAvailable(WifiP2pGroup info) {
549                        if (DEBUG) {
550                            Slog.d(TAG, "Received group info: " + describeWifiP2pGroup(info));
551                        }
552
553                        if (mConnectingDevice != null && !info.contains(mConnectingDevice)) {
554                            Slog.i(TAG, "Aborting connection to Wifi display because "
555                                    + "the current P2P group does not contain the device "
556                                    + "we expected to find: " + mConnectingDevice.deviceName);
557                            handleConnectionFailure(false);
558                            return;
559                        }
560
561                        if (mDesiredDevice != null && !info.contains(mDesiredDevice)) {
562                            disconnect();
563                            return;
564                        }
565
566                        if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
567                            Slog.i(TAG, "Connected to Wifi display: "
568                                    + mConnectingDevice.deviceName);
569
570                            mHandler.removeCallbacks(mConnectionTimeout);
571                            mConnectedDeviceGroupInfo = info;
572                            mConnectedDevice = mConnectingDevice;
573                            mConnectingDevice = null;
574                            updateConnection();
575                        }
576                    }
577                });
578            }
579        } else {
580            disconnect();
581
582            // After disconnection for a group, for some reason we have a tendency
583            // to get a peer change notification with an empty list of peers.
584            // Perform a fresh scan.
585            if (mWfdEnabled) {
586                requestPeers();
587            }
588        }
589    }
590
591    private final Runnable mConnectionTimeout = new Runnable() {
592        @Override
593        public void run() {
594            if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
595                Slog.i(TAG, "Timed out waiting for Wifi display connection after "
596                        + CONNECTION_TIMEOUT_SECONDS + " seconds: "
597                        + mConnectingDevice.deviceName);
598                handleConnectionFailure(true);
599            }
600        }
601    };
602
603    private void handleConnectionFailure(boolean timeoutOccurred) {
604        if (mDesiredDevice != null) {
605            Slog.i(TAG, "Wifi display connection failed!");
606
607            mHandler.post(new Runnable() {
608                @Override
609                public void run() {
610                    mListener.onDisplayConnectionFailed();
611                }
612            });
613
614            if (mConnectionRetriesLeft > 0) {
615                mHandler.postDelayed(new Runnable() {
616                    @Override
617                    public void run() {
618                        retryConnection();
619                    }
620                }, timeoutOccurred ? 0 : CONNECT_RETRY_DELAY_MILLIS);
621            } else {
622                disconnect();
623            }
624        }
625    }
626
627    private static Inet4Address getInterfaceAddress(WifiP2pGroup info) {
628        NetworkInterface iface;
629        try {
630            iface = NetworkInterface.getByName(info.getInterface());
631        } catch (SocketException ex) {
632            Slog.w(TAG, "Could not obtain address of network interface "
633                    + info.getInterface(), ex);
634            return null;
635        }
636
637        Enumeration<InetAddress> addrs = iface.getInetAddresses();
638        while (addrs.hasMoreElements()) {
639            InetAddress addr = addrs.nextElement();
640            if (addr instanceof Inet4Address) {
641                return (Inet4Address)addr;
642            }
643        }
644
645        Slog.w(TAG, "Could not obtain address of network interface "
646                + info.getInterface() + " because it had no IPv4 addresses.");
647        return null;
648    }
649
650    private static boolean isWifiDisplay(WifiP2pDevice device) {
651        // FIXME: the wfdInfo API doesn't work yet
652        return device.deviceName.startsWith("DWD-")
653                || device.deviceName.startsWith("DIRECT-")
654                || device.deviceName.startsWith("CAVM-");
655        //device.wfdInfo != null && device.wfdInfo.isWfdEnabled();
656    }
657
658    private static String describeWifiP2pDevice(WifiP2pDevice device) {
659        return device != null ? device.toString().replace('\n', ',') : "null";
660    }
661
662    private static String describeWifiP2pGroup(WifiP2pGroup group) {
663        return group != null ? group.toString().replace('\n', ',') : "null";
664    }
665
666    private static WifiDisplay createWifiDisplay(WifiP2pDevice device) {
667        return new WifiDisplay(device.deviceAddress, device.deviceName);
668    }
669
670    private final BroadcastReceiver mWifiP2pReceiver = new BroadcastReceiver() {
671        @Override
672        public void onReceive(Context context, Intent intent) {
673            final String action = intent.getAction();
674            if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
675                boolean enabled = (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
676                        WifiP2pManager.WIFI_P2P_STATE_DISABLED)) ==
677                        WifiP2pManager.WIFI_P2P_STATE_ENABLED;
678                if (DEBUG) {
679                    Slog.d(TAG, "Received WIFI_P2P_STATE_CHANGED_ACTION: enabled="
680                            + enabled);
681                }
682
683                handleStateChanged(enabled);
684            } else if (action.equals(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)) {
685                if (DEBUG) {
686                    Slog.d(TAG, "Received WIFI_P2P_PEERS_CHANGED_ACTION.");
687                }
688
689                handlePeersChanged();
690            } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
691                NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
692                        WifiP2pManager.EXTRA_NETWORK_INFO);
693                if (DEBUG) {
694                    Slog.d(TAG, "Received WIFI_P2P_CONNECTION_CHANGED_ACTION: networkInfo="
695                            + networkInfo);
696                }
697
698                handleConnectionChanged(networkInfo);
699            }
700        }
701    };
702
703    /**
704     * Called on the handler thread when displays are connected or disconnected.
705     */
706    public interface Listener {
707        void onEnablementChanged(boolean enabled);
708
709        void onScanStarted();
710        void onScanFinished(WifiDisplay[] knownDisplays);
711
712        void onDisplayConnecting(WifiDisplay display);
713        void onDisplayConnectionFailed();
714        void onDisplayConnected(WifiDisplay display, String iface);
715        void onDisplayDisconnected();
716    }
717}
718