1/*
2 * Copyright (C) 2011 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 android.net.wifi.p2p;
18
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.content.Context;
22import android.net.ConnectivityManager;
23import android.net.IConnectivityManager;
24import android.os.Binder;
25import android.os.IBinder;
26import android.os.Handler;
27import android.os.Looper;
28import android.os.Message;
29import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.os.WorkSource;
32import android.os.Messenger;
33import android.util.Log;
34
35import com.android.internal.util.AsyncChannel;
36import com.android.internal.util.Protocol;
37
38import java.util.HashMap;
39
40/**
41 * This class provides the API for managing Wi-Fi peer-to-peer connectivity. This lets an
42 * application discover available peers, setup connection to peers and query for the list of peers.
43 * When a p2p connection is formed over wifi, the device continues to maintain the uplink
44 * connection over mobile or any other available network for internet connectivity on the device.
45 *
46 * <p> The API is asynchronous and responses to requests from an application are on listener
47 * callbacks provided by the application. The application needs to do an initialization with
48 * {@link #initialize} before doing any p2p operation.
49 *
50 * <p> Application actions {@link #discoverPeers}, {@link #connect}, {@link #cancelConnect},
51 * {@link #createGroup} and {@link #removeGroup} need a {@link ActionListener} instance for
52 * receiving callbacks {@link ActionListener#onSuccess} or {@link ActionListener#onFailure}.
53 * Action callbacks indicate whether the initiation of the action was a success or a failure.
54 * Upon failure, the reason of failure can be one of {@link #ERROR}, {@link #P2P_UNSUPPORTED}
55 * or {@link #BUSY}.
56 *
57 * <p> An application can initiate discovery of peers with {@link #discoverPeers}. An initiated
58 * discovery request from an application stays active until the device starts connecting to a peer
59 * or forms a p2p group. The {@link ActionListener} callbacks provide feedback on whether the
60 * discovery initiation was successful or failure. Additionally, applications can listen
61 * to {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent action to know when the peer list changes.
62 *
63 * <p> When the peer list change intent {@link #WIFI_P2P_PEERS_CHANGED_ACTION} is received
64 * or when an application needs to fetch the current list of peers, it can request the list
65 * of peers with {@link #requestPeers}. When the peer list is available
66 * {@link PeerListListener#onPeersAvailable} is called with the device list.
67 *
68 * <p> An application can initiate a connection request to a peer through {@link #connect}. See
69 * {@link WifiP2pConfig} for details on setting up the configuration. For communication with legacy
70 * Wi-Fi devices that do not support p2p, an app can create a group using {@link #createGroup}
71 * which creates an access point whose details can be fetched with {@link #requestGroupInfo}.
72*
73 * <p> After a successful group formation through {@link #createGroup} or through {@link #connect},
74 * use {@link #requestConnectionInfo} to fetch the connection details. The connection info
75 * {@link WifiP2pInfo} contains the address of the group owner
76 * {@link WifiP2pInfo#groupOwnerAddress} and a flag {@link WifiP2pInfo#isGroupOwner} to indicate
77 * if the current device is a p2p group owner. A p2p client can thus communicate with
78 * the p2p group owner through a socket connection.
79 *
80 * <p> Android has no platform support for service discovery yet, so applications could
81 * run a service discovery protocol to discover services on the peer-to-peer netework.
82 *
83 * <p class="note"><strong>Note:</strong>
84 * Registering an application handler with {@link #initialize} requires the permissions
85 * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and
86 * {@link android.Manifest.permission#CHANGE_WIFI_STATE} to perform any further peer-to-peer
87 * operations.
88 *
89 * Get an instance of this class by calling {@link android.content.Context#getSystemService(String)
90 * Context.getSystemService(Context.WIFI_P2P_SERVICE)}.
91 *
92 * {@see WifiP2pConfig}
93 * {@see WifiP2pInfo}
94 * {@see WifiP2pGroup}
95 * {@see WifiP2pDevice}
96 * {@see WifiP2pDeviceList}
97 * {@see android.net.wifi.WpsInfo}
98 */
99public class WifiP2pManager {
100    private static final String TAG = "WifiP2pManager";
101    /**
102     * Broadcast intent action to indicate whether Wi-Fi p2p is enabled or disabled. An
103     * extra {@link #EXTRA_WIFI_STATE} provides the state information as int.
104     *
105     * @see #EXTRA_WIFI_STATE
106     */
107    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
108    public static final String WIFI_P2P_STATE_CHANGED_ACTION =
109        "android.net.wifi.p2p.STATE_CHANGED";
110
111    /**
112     * The lookup key for an int that indicates whether Wi-Fi p2p is enabled or disabled.
113     * Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}.
114     *
115     * @see #WIFI_P2P_STATE_DISABLED
116     * @see #WIFI_P2P_STATE_ENABLED
117     */
118    public static final String EXTRA_WIFI_STATE = "wifi_p2p_state";
119
120    /**
121     * Wi-Fi p2p is disabled.
122     *
123     * @see #WIFI_P2P_STATE_CHANGED_ACTION
124     */
125    public static final int WIFI_P2P_STATE_DISABLED = 1;
126
127    /**
128     * Wi-Fi p2p is enabled.
129     *
130     * @see #WIFI_P2P_STATE_CHANGED_ACTION
131     */
132    public static final int WIFI_P2P_STATE_ENABLED = 2;
133
134    /**
135     * Broadcast intent action indicating that the state of Wi-Fi p2p connectivity
136     * has changed. One extra {@link #EXTRA_WIFI_P2P_INFO} provides the p2p connection info in
137     * the form of a {@link WifiP2pInfo} object. Another extra {@link #EXTRA_NETWORK_INFO} provides
138     * the network info in the form of a {@link android.net.NetworkInfo}.
139     *
140     * @see #EXTRA_WIFI_P2P_INFO
141     * @see #EXTRA_NETWORK_INFO
142     */
143    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
144    public static final String WIFI_P2P_CONNECTION_CHANGED_ACTION =
145        "android.net.wifi.p2p.CONNECTION_STATE_CHANGE";
146
147    /**
148     * The lookup key for a {@link android.net.wifi.p2p.WifiP2pInfo} object
149     * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
150     */
151    public static final String EXTRA_WIFI_P2P_INFO = "wifiP2pInfo";
152
153    /**
154     * The lookup key for a {@link android.net.NetworkInfo} object associated with the
155     * Wi-Fi network. Retrieve with
156     * {@link android.content.Intent#getParcelableExtra(String)}.
157     */
158    public static final String EXTRA_NETWORK_INFO = "networkInfo";
159
160    /**
161     * The lookup key for a {@link android.net.LinkProperties} object associated with the
162     * network. Retrieve with
163     * {@link android.content.Intent#getParcelableExtra(String)}.
164     * @hide
165     */
166    public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
167
168    /**
169     * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
170     * network. Retrieve with
171     * {@link android.content.Intent#getParcelableExtra(String)}.
172     * @hide
173     */
174    public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
175
176    /**
177     * Broadcast intent action indicating that the available peer list has changed. Fetch
178     * the changed list of peers with {@link #requestPeers}
179     */
180    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
181    public static final String WIFI_P2P_PEERS_CHANGED_ACTION =
182        "android.net.wifi.p2p.PEERS_CHANGED";
183
184    /**
185     * Broadcast intent action indicating that this device details have changed.
186     */
187    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
188    public static final String WIFI_P2P_THIS_DEVICE_CHANGED_ACTION =
189        "android.net.wifi.p2p.THIS_DEVICE_CHANGED";
190
191    /**
192     * The lookup key for a {@link android.net.wifi.p2p.WifiP2pDevice} object
193     * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
194     */
195    public static final String EXTRA_WIFI_P2P_DEVICE = "wifiP2pDevice";
196
197    IWifiP2pManager mService;
198
199    private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
200
201    /** @hide */
202    public static final int ENABLE_P2P                              = BASE + 1;
203    /** @hide */
204    public static final int ENABLE_P2P_FAILED                       = BASE + 2;
205    /** @hide */
206    public static final int ENABLE_P2P_SUCCEEDED                    = BASE + 3;
207
208    /** @hide */
209    public static final int DISABLE_P2P                             = BASE + 4;
210    /** @hide */
211    public static final int DISABLE_P2P_FAILED                      = BASE + 5;
212    /** @hide */
213    public static final int DISABLE_P2P_SUCCEEDED                   = BASE + 6;
214
215    /** @hide */
216    public static final int DISCOVER_PEERS                          = BASE + 7;
217    /** @hide */
218    public static final int DISCOVER_PEERS_FAILED                   = BASE + 8;
219    /** @hide */
220    public static final int DISCOVER_PEERS_SUCCEEDED                = BASE + 9;
221
222    /** @hide */
223    public static final int CONNECT                                 = BASE + 10;
224    /** @hide */
225    public static final int CONNECT_FAILED                          = BASE + 11;
226    /** @hide */
227    public static final int CONNECT_SUCCEEDED                       = BASE + 12;
228
229    /** @hide */
230    public static final int CANCEL_CONNECT                          = BASE + 13;
231    /** @hide */
232    public static final int CANCEL_CONNECT_FAILED                   = BASE + 14;
233    /** @hide */
234    public static final int CANCEL_CONNECT_SUCCEEDED                = BASE + 15;
235
236    /** @hide */
237    public static final int CREATE_GROUP                            = BASE + 16;
238    /** @hide */
239    public static final int CREATE_GROUP_FAILED                     = BASE + 17;
240    /** @hide */
241    public static final int CREATE_GROUP_SUCCEEDED                  = BASE + 18;
242
243    /** @hide */
244    public static final int REMOVE_GROUP                            = BASE + 19;
245    /** @hide */
246    public static final int REMOVE_GROUP_FAILED                     = BASE + 20;
247    /** @hide */
248    public static final int REMOVE_GROUP_SUCCEEDED                  = BASE + 21;
249
250    /** @hide */
251    public static final int REQUEST_PEERS                           = BASE + 22;
252    /** @hide */
253    public static final int RESPONSE_PEERS                          = BASE + 23;
254
255    /** @hide */
256    public static final int REQUEST_CONNECTION_INFO                 = BASE + 24;
257    /** @hide */
258    public static final int RESPONSE_CONNECTION_INFO                = BASE + 25;
259
260    /** @hide */
261    public static final int REQUEST_GROUP_INFO                      = BASE + 26;
262    /** @hide */
263    public static final int RESPONSE_GROUP_INFO                     = BASE + 27;
264
265    /**
266     * Create a new WifiP2pManager instance. Applications use
267     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
268     * the standard {@link android.content.Context#WIFI_P2P_SERVICE Context.WIFI_P2P_SERVICE}.
269     * @param service the Binder interface
270     * @hide - hide this because it takes in a parameter of type IWifiP2pManager, which
271     * is a system private class.
272     */
273    public WifiP2pManager(IWifiP2pManager service) {
274        mService = service;
275    }
276
277    /**
278     * Passed with {@link ActionListener#onFailure}.
279     * Indicates that the operation failed due to an internal error.
280     */
281    public static final int ERROR               = 0;
282
283    /**
284     * Passed with {@link ActionListener#onFailure}.
285     * Indicates that the operation failed because p2p is unsupported on the device.
286     */
287    public static final int P2P_UNSUPPORTED     = 1;
288
289    /**
290     * Passed with {@link ActionListener#onFailure}.
291     * Indicates that the operation failed because the framework is busy and
292     * unable to service the request
293     */
294    public static final int BUSY                = 2;
295
296    /** Interface for callback invocation when framework channel is lost */
297    public interface ChannelListener {
298        /**
299         * The channel to the framework has been disconnected.
300         * Application could try re-initializing using {@link #initialize}
301         */
302        public void onChannelDisconnected();
303    }
304
305    /** Interface for callback invocation on an application action */
306    public interface ActionListener {
307        /** The operation succeeded */
308        public void onSuccess();
309        /**
310         * The operation failed
311         * @param reason The reason for failure could be one of {@link #P2P_UNSUPPORTED},
312         * {@link #ERROR} or {@link #BUSY}
313         */
314        public void onFailure(int reason);
315    }
316
317    /** Interface for callback invocation when peer list is available */
318    public interface PeerListListener {
319        /**
320         * The requested peer list is available
321         * @param peers List of available peers
322         */
323        public void onPeersAvailable(WifiP2pDeviceList peers);
324    }
325
326    /** Interface for callback invocation when connection info is available */
327    public interface ConnectionInfoListener {
328        /**
329         * The requested connection info is available
330         * @param info Wi-Fi p2p connection info
331         */
332        public void onConnectionInfoAvailable(WifiP2pInfo info);
333    }
334
335    /** Interface for callback invocation when group info is available */
336    public interface GroupInfoListener {
337        /**
338         * The requested p2p group info is available
339         * @param group Wi-Fi p2p group info
340         */
341        public void onGroupInfoAvailable(WifiP2pGroup group);
342    }
343
344    /**
345     * A channel that connects the application to the Wifi p2p framework.
346     * Most p2p operations require a Channel as an argument. An instance of Channel is obtained
347     * by doing a call on {@link #initialize}
348     */
349    public static class Channel {
350        Channel(Looper looper, ChannelListener l) {
351            mAsyncChannel = new AsyncChannel();
352            mHandler = new P2pHandler(looper);
353            mChannelListener = l;
354        }
355        private ChannelListener mChannelListener;
356        private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
357        private Object mListenerMapLock = new Object();
358        private int mListenerKey = 0;
359
360        AsyncChannel mAsyncChannel;
361        P2pHandler mHandler;
362        class P2pHandler extends Handler {
363            P2pHandler(Looper looper) {
364                super(looper);
365            }
366
367            @Override
368            public void handleMessage(Message message) {
369                Object listener = getListener(message.arg2);
370                switch (message.what) {
371                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
372                        if (mChannelListener != null) {
373                            mChannelListener.onChannelDisconnected();
374                            mChannelListener = null;
375                        }
376                        break;
377                    /* ActionListeners grouped together */
378                    case WifiP2pManager.DISCOVER_PEERS_FAILED:
379                    case WifiP2pManager.CONNECT_FAILED:
380                    case WifiP2pManager.CANCEL_CONNECT_FAILED:
381                    case WifiP2pManager.CREATE_GROUP_FAILED:
382                    case WifiP2pManager.REMOVE_GROUP_FAILED:
383                        if (listener != null) {
384                            ((ActionListener) listener).onFailure(message.arg1);
385                        }
386                        break;
387                    /* ActionListeners grouped together */
388                    case WifiP2pManager.DISCOVER_PEERS_SUCCEEDED:
389                    case WifiP2pManager.CONNECT_SUCCEEDED:
390                    case WifiP2pManager.CANCEL_CONNECT_SUCCEEDED:
391                    case WifiP2pManager.CREATE_GROUP_SUCCEEDED:
392                    case WifiP2pManager.REMOVE_GROUP_SUCCEEDED:
393                        if (listener != null) {
394                            ((ActionListener) listener).onSuccess();
395                        }
396                        break;
397                    case WifiP2pManager.RESPONSE_PEERS:
398                        WifiP2pDeviceList peers = (WifiP2pDeviceList) message.obj;
399                        if (listener != null) {
400                            ((PeerListListener) listener).onPeersAvailable(peers);
401                        }
402                        break;
403                    case WifiP2pManager.RESPONSE_CONNECTION_INFO:
404                        WifiP2pInfo wifiP2pInfo = (WifiP2pInfo) message.obj;
405                        if (listener != null) {
406                            ((ConnectionInfoListener) listener).onConnectionInfoAvailable(wifiP2pInfo);
407                        }
408                        break;
409                    case WifiP2pManager.RESPONSE_GROUP_INFO:
410                        WifiP2pGroup group = (WifiP2pGroup) message.obj;
411                        if (listener != null) {
412                            ((GroupInfoListener) listener).onGroupInfoAvailable(group);
413                        }
414                        break;
415                   default:
416                        Log.d(TAG, "Ignored " + message);
417                        break;
418                }
419            }
420        }
421
422        int putListener(Object listener) {
423            if (listener == null) return 0;
424            int key;
425            synchronized (mListenerMapLock) {
426                key = mListenerKey++;
427                mListenerMap.put(key, listener);
428            }
429            return key;
430        }
431
432        Object getListener(int key) {
433            synchronized (mListenerMapLock) {
434                return mListenerMap.remove(key);
435            }
436        }
437    }
438
439    /**
440     * Registers the application with the Wi-Fi framework. This function
441     * must be the first to be called before any p2p operations are performed.
442     *
443     * @param srcContext is the context of the source
444     * @param srcLooper is the Looper on which the callbacks are receivied
445     * @param listener for callback at loss of framework communication. Can be null.
446     * @return Channel instance that is necessary for performing any further p2p operations
447     */
448    public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
449        Messenger messenger = getMessenger();
450        if (messenger == null) return null;
451
452        Channel c = new Channel(srcLooper, listener);
453        if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
454                == AsyncChannel.STATUS_SUCCESSFUL) {
455            return c;
456        } else {
457            return null;
458        }
459    }
460
461    /**
462     * Sends in a request to the system to enable p2p. This will pop up a dialog
463     * to the user and upon authorization will enable p2p.
464     * @hide
465     */
466    public void enableP2p(Channel c) {
467        if (c == null) return;
468        c.mAsyncChannel.sendMessage(ENABLE_P2P);
469    }
470
471    /**
472     * Sends in a request to the system to disable p2p. This will pop up a dialog
473     * to the user and upon authorization will enable p2p.
474     * @hide
475     */
476    public void disableP2p(Channel c) {
477        if (c == null) return;
478        c.mAsyncChannel.sendMessage(DISABLE_P2P);
479    }
480
481    /**
482     * Initiate peer discovery. A discovery process involves scanning for available Wi-Fi peers
483     * for the purpose of establishing a connection.
484     *
485     * <p> The function call immediately returns after sending a discovery request
486     * to the framework. The application is notified of a success or failure to initiate
487     * discovery through listener callbacks {@link ActionListener#onSuccess} or
488     * {@link ActionListener#onFailure}.
489     *
490     * <p> The discovery remains active until a connection is initiated or
491     * a p2p group is formed. Register for {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent to
492     * determine when the framework notifies of a change as peers are discovered.
493     *
494     * <p> Upon receiving a {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent, an application
495     * can request for the list of peers using {@link #requestPeers}.
496     *
497     * @param c is the channel created at {@link #initialize}
498     * @param listener for callbacks on success or failure. Can be null.
499     */
500    public void discoverPeers(Channel c, ActionListener listener) {
501        if (c == null) return;
502        c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener));
503    }
504
505    /**
506     * Start a p2p connection to a device with the specified configuration.
507     *
508     * <p> The function call immediately returns after sending a connection request
509     * to the framework. The application is notified of a success or failure to initiate
510     * connect through listener callbacks {@link ActionListener#onSuccess} or
511     * {@link ActionListener#onFailure}.
512     *
513     * <p> Register for {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION} intent to
514     * determine when the framework notifies of a change in connectivity.
515     *
516     * <p> If the current device is not part of a p2p group, a connect request initiates
517     * a group negotiation with the peer.
518     *
519     * <p> If the current device is part of an existing p2p group or has created
520     * a p2p group with {@link #createGroup}, an invitation to join the group is sent to
521     * the peer device.
522     *
523     * @param c is the channel created at {@link #initialize}
524     * @param config options as described in {@link WifiP2pConfig} class
525     * @param listener for callbacks on success or failure. Can be null.
526     */
527    public void connect(Channel c, WifiP2pConfig config, ActionListener listener) {
528        if (c == null) return;
529        c.mAsyncChannel.sendMessage(CONNECT, 0, c.putListener(listener), config);
530    }
531
532    /**
533     * Cancel any ongoing p2p group negotiation
534     *
535     * <p> The function call immediately returns after sending a connection cancellation request
536     * to the framework. The application is notified of a success or failure to initiate
537     * cancellation through listener callbacks {@link ActionListener#onSuccess} or
538     * {@link ActionListener#onFailure}.
539     *
540     * @param c is the channel created at {@link #initialize}
541     * @param listener for callbacks on success or failure. Can be null.
542     */
543    public void cancelConnect(Channel c, ActionListener listener) {
544        if (c == null) return;
545        c.mAsyncChannel.sendMessage(CANCEL_CONNECT, 0, c.putListener(listener));
546    }
547
548    /**
549     * Create a p2p group with the current device as the group owner. This essentially creates
550     * an access point that can accept connections from legacy clients as well as other p2p
551     * devices.
552     *
553     * <p class="note"><strong>Note:</strong>
554     * This function would normally not be used unless the current device needs
555     * to form a p2p connection with a legacy client
556     *
557     * <p> The function call immediately returns after sending a group creation request
558     * to the framework. The application is notified of a success or failure to initiate
559     * group creation through listener callbacks {@link ActionListener#onSuccess} or
560     * {@link ActionListener#onFailure}.
561     *
562     * <p> Application can request for the group details with {@link #requestGroupInfo}.
563     *
564     * @param c is the channel created at {@link #initialize}
565     * @param listener for callbacks on success or failure. Can be null.
566     */
567    public void createGroup(Channel c, ActionListener listener) {
568        if (c == null) return;
569        c.mAsyncChannel.sendMessage(CREATE_GROUP, 0, c.putListener(listener));
570    }
571
572    /**
573     * Remove the current p2p group.
574     *
575     * <p> The function call immediately returns after sending a group removal request
576     * to the framework. The application is notified of a success or failure to initiate
577     * group removal through listener callbacks {@link ActionListener#onSuccess} or
578     * {@link ActionListener#onFailure}.
579     *
580     * @param c is the channel created at {@link #initialize}
581     * @param listener for callbacks on success or failure. Can be null.
582     */
583    public void removeGroup(Channel c, ActionListener listener) {
584        if (c == null) return;
585        c.mAsyncChannel.sendMessage(REMOVE_GROUP, 0, c.putListener(listener));
586    }
587
588    /**
589     * Request the current list of peers.
590     *
591     * @param c is the channel created at {@link #initialize}
592     * @param listener for callback when peer list is available. Can be null.
593     */
594    public void requestPeers(Channel c, PeerListListener listener) {
595        if (c == null) return;
596        c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener));
597    }
598
599    /**
600     * Request device connection info.
601     *
602     * @param c is the channel created at {@link #initialize}
603     * @param listener for callback when connection info is available. Can be null.
604     */
605    public void requestConnectionInfo(Channel c, ConnectionInfoListener listener) {
606        if (c == null) return;
607        c.mAsyncChannel.sendMessage(REQUEST_CONNECTION_INFO, 0, c.putListener(listener));
608    }
609
610    /**
611     * Request p2p group info.
612     *
613     * @param c is the channel created at {@link #initialize}
614     * @param listener for callback when group info is available. Can be null.
615     */
616    public void requestGroupInfo(Channel c, GroupInfoListener listener) {
617        if (c == null) return;
618        c.mAsyncChannel.sendMessage(REQUEST_GROUP_INFO, 0, c.putListener(listener));
619    }
620
621    /**
622     * Get a reference to WifiP2pService handler. This is used to establish
623     * an AsyncChannel communication with WifiService
624     *
625     * @return Messenger pointing to the WifiP2pService handler
626     * @hide
627     */
628    public Messenger getMessenger() {
629        try {
630            return mService.getMessenger();
631        } catch (RemoteException e) {
632            return null;
633        }
634    }
635
636}
637