WifiP2pServiceImpl.java revision 3b26801d62a06475b722bbf29cba7f48f376654e
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 com.android.server.wifi.p2p;
18
19import android.app.AlertDialog;
20import android.app.Notification;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.DialogInterface.OnClickListener;
24import android.content.Intent;
25import android.content.pm.PackageManager;
26import android.content.res.Configuration;
27import android.content.res.Resources;
28import android.net.ConnectivityManager;
29import android.net.DhcpResults;
30import android.net.DhcpStateMachine;
31import android.net.InterfaceConfiguration;
32import android.net.LinkAddress;
33import android.net.NetworkInfo;
34import android.net.NetworkUtils;
35import android.net.wifi.WpsInfo;
36import android.net.wifi.p2p.IWifiP2pManager;
37import android.net.wifi.p2p.WifiP2pConfig;
38import android.net.wifi.p2p.WifiP2pDevice;
39import android.net.wifi.p2p.WifiP2pDeviceList;
40import android.net.wifi.p2p.WifiP2pGroup;
41import android.net.wifi.p2p.WifiP2pGroupList;
42import android.net.wifi.p2p.WifiP2pGroupList.GroupDeleteListener;
43import android.net.wifi.p2p.WifiP2pInfo;
44import android.net.wifi.p2p.WifiP2pManager;
45import android.net.wifi.p2p.WifiP2pProvDiscEvent;
46import android.net.wifi.p2p.WifiP2pWfdInfo;
47import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
48import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
49import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
50import android.os.Binder;
51import android.os.Bundle;
52import android.os.Handler;
53import android.os.HandlerThread;
54import android.os.IBinder;
55import android.os.INetworkManagementService;
56import android.os.Looper;
57import android.os.Message;
58import android.os.Messenger;
59import android.os.RemoteException;
60import android.os.ServiceManager;
61import android.os.UserHandle;
62import android.provider.Settings;
63import android.text.TextUtils;
64import android.util.Slog;
65import android.util.SparseArray;
66import android.view.KeyEvent;
67import android.view.LayoutInflater;
68import android.view.View;
69import android.view.ViewGroup;
70import android.view.WindowManager;
71import android.widget.EditText;
72import android.widget.TextView;
73
74import com.android.internal.R;
75import com.android.internal.util.AsyncChannel;
76import com.android.internal.util.Protocol;
77import com.android.internal.util.State;
78import com.android.internal.util.StateMachine;
79import com.android.server.wifi.WifiMonitor;
80import com.android.server.wifi.WifiNative;
81import com.android.server.wifi.WifiStateMachine;
82
83import java.io.FileDescriptor;
84import java.io.PrintWriter;
85import java.net.InetAddress;
86import java.util.ArrayList;
87import java.util.Collection;
88import java.util.HashMap;
89import java.util.List;
90import java.util.Locale;
91
92
93/**
94 * WifiP2pService includes a state machine to perform Wi-Fi p2p operations. Applications
95 * communicate with this service to issue device discovery and connectivity requests
96 * through the WifiP2pManager interface. The state machine communicates with the wifi
97 * driver through wpa_supplicant and handles the event responses through WifiMonitor.
98 *
99 * Note that the term Wifi when used without a p2p suffix refers to the client mode
100 * of Wifi operation
101 * @hide
102 */
103public final class WifiP2pServiceImpl extends IWifiP2pManager.Stub {
104    private static final String TAG = "WifiP2pService";
105    private static final boolean DBG = false;
106    private static final String NETWORKTYPE = "WIFI_P2P";
107
108    private Context mContext;
109    private String mInterface;
110    private Notification mNotification;
111
112    INetworkManagementService mNwService;
113    private DhcpStateMachine mDhcpStateMachine;
114
115    private P2pStateMachine mP2pStateMachine;
116    private AsyncChannel mReplyChannel = new AsyncChannel();
117    private AsyncChannel mWifiChannel;
118
119    private static final Boolean JOIN_GROUP = true;
120    private static final Boolean FORM_GROUP = false;
121
122    private static final Boolean RELOAD = true;
123    private static final Boolean NO_RELOAD = false;
124
125    /* Two minutes comes from the wpa_supplicant setting */
126    private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
127    private static int mGroupCreatingTimeoutIndex = 0;
128
129    private static final int DISABLE_P2P_WAIT_TIME_MS = 5 * 1000;
130    private static int mDisableP2pTimeoutIndex = 0;
131
132    /* Set a two minute discover timeout to avoid STA scans from being blocked */
133    private static final int DISCOVER_TIMEOUT_S = 120;
134
135    /* Idle time after a peer is gone when the group is torn down */
136    private static final int GROUP_IDLE_TIME_S = 10;
137
138    private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
139
140    /* Delayed message to timeout group creation */
141    public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 1;
142
143    /* User accepted a peer request */
144    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 2;
145    /* User rejected a peer request */
146    private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 3;
147    /* User wants to disconnect wifi in favour of p2p */
148    private static final int DROP_WIFI_USER_ACCEPT          =   BASE + 4;
149    /* User wants to keep his wifi connection and drop p2p */
150    private static final int DROP_WIFI_USER_REJECT          =   BASE + 5;
151    /* Delayed message to timeout p2p disable */
152    public static final int DISABLE_P2P_TIMED_OUT           =   BASE + 6;
153
154
155    /* Commands to the WifiStateMachine */
156    public static final int P2P_CONNECTION_CHANGED          =   BASE + 11;
157
158    /* These commands are used to temporarily disconnect wifi when we detect
159     * a frequency conflict which would make it impossible to have with p2p
160     * and wifi active at the same time.
161     *
162     * If the user chooses to disable wifi temporarily, we keep wifi disconnected
163     * until the p2p connection is done and terminated at which point we will
164     * bring back wifi up
165     *
166     * DISCONNECT_WIFI_REQUEST
167     *      msg.arg1 = 1 enables temporary disconnect and 0 disables it.
168     */
169    public static final int DISCONNECT_WIFI_REQUEST         =   BASE + 12;
170    public static final int DISCONNECT_WIFI_RESPONSE        =   BASE + 13;
171
172    public static final int SET_MIRACAST_MODE               =   BASE + 14;
173
174    // During dhcp (and perhaps other times) we can't afford to drop packets
175    // but Discovery will switch our channel enough we will.
176    //   msg.arg1 = ENABLED for blocking, DISABLED for resumed.
177    //   msg.arg2 = msg to send when blocked
178    //   msg.obj  = StateMachine to send to when blocked
179    public static final int BLOCK_DISCOVERY                 =   BASE + 15;
180
181    // set country code
182    public static final int SET_COUNTRY_CODE                =   BASE + 16;
183
184    public static final int ENABLED                         = 1;
185    public static final int DISABLED                        = 0;
186
187    private final boolean mP2pSupported;
188
189    private WifiP2pDevice mThisDevice = new WifiP2pDevice();
190
191    /* When a group has been explicitly created by an app, we persist the group
192     * even after all clients have been disconnected until an explicit remove
193     * is invoked */
194    private boolean mAutonomousGroup;
195
196    /* Invitation to join an existing p2p group */
197    private boolean mJoinExistingGroup;
198
199    /* Track whether we are in p2p discovery. This is used to avoid sending duplicate
200     * broadcasts
201     */
202    private boolean mDiscoveryStarted;
203    /* Track whether servcice/peer discovery is blocked in favor of other wifi actions
204     * (notably dhcp)
205     */
206    private boolean mDiscoveryBlocked;
207
208    // Supplicant doesn't like setting the same country code multiple times (it may drop
209    // current connected network), so we save the country code here to avoid redundency
210    private String mLastSetCountryCode;
211
212    /*
213     * remember if we were in a scan when it had to be stopped
214     */
215    private boolean mDiscoveryPostponed = false;
216
217    private NetworkInfo mNetworkInfo;
218
219    private boolean mTemporarilyDisconnectedWifi = false;
220
221    /* The transaction Id of service discovery request */
222    private byte mServiceTransactionId = 0;
223
224    /* Service discovery request ID of wpa_supplicant.
225     * null means it's not set yet. */
226    private String mServiceDiscReqId;
227
228    /* clients(application) information list. */
229    private HashMap<Messenger, ClientInfo> mClientInfoList = new HashMap<Messenger, ClientInfo>();
230
231    /* Is chosen as a unique address to avoid conflict with
232       the ranges defined in Tethering.java */
233    private static final String SERVER_ADDRESS = "192.168.49.1";
234
235    /**
236     * Error code definition.
237     * see the Table.8 in the WiFi Direct specification for the detail.
238     */
239    public static enum P2pStatus {
240        /* Success. */
241        SUCCESS,
242
243        /* The target device is currently unavailable. */
244        INFORMATION_IS_CURRENTLY_UNAVAILABLE,
245
246        /* Protocol error. */
247        INCOMPATIBLE_PARAMETERS,
248
249        /* The target device reached the limit of the number of the connectable device.
250         * For example, device limit or group limit is set. */
251        LIMIT_REACHED,
252
253        /* Protocol error. */
254        INVALID_PARAMETER,
255
256        /* Unable to accommodate request. */
257        UNABLE_TO_ACCOMMODATE_REQUEST,
258
259        /* Previous protocol error, or disruptive behavior. */
260        PREVIOUS_PROTOCOL_ERROR,
261
262        /* There is no common channels the both devices can use. */
263        NO_COMMON_CHANNEL,
264
265        /* Unknown p2p group. For example, Device A tries to invoke the previous persistent group,
266         *  but device B has removed the specified credential already. */
267        UNKNOWN_P2P_GROUP,
268
269        /* Both p2p devices indicated an intent of 15 in group owner negotiation. */
270        BOTH_GO_INTENT_15,
271
272        /* Incompatible provisioning method. */
273        INCOMPATIBLE_PROVISIONING_METHOD,
274
275        /* Rejected by user */
276        REJECTED_BY_USER,
277
278        /* Unknown error */
279        UNKNOWN;
280
281        public static P2pStatus valueOf(int error) {
282            switch(error) {
283            case 0 :
284                return SUCCESS;
285            case 1:
286                return INFORMATION_IS_CURRENTLY_UNAVAILABLE;
287            case 2:
288                return INCOMPATIBLE_PARAMETERS;
289            case 3:
290                return LIMIT_REACHED;
291            case 4:
292                return INVALID_PARAMETER;
293            case 5:
294                return UNABLE_TO_ACCOMMODATE_REQUEST;
295            case 6:
296                return PREVIOUS_PROTOCOL_ERROR;
297            case 7:
298                return NO_COMMON_CHANNEL;
299            case 8:
300                return UNKNOWN_P2P_GROUP;
301            case 9:
302                return BOTH_GO_INTENT_15;
303            case 10:
304                return INCOMPATIBLE_PROVISIONING_METHOD;
305            case 11:
306                return REJECTED_BY_USER;
307            default:
308                return UNKNOWN;
309            }
310        }
311    }
312
313    /**
314     * Handles client connections
315     */
316    private class ClientHandler extends Handler {
317
318        ClientHandler(android.os.Looper looper) {
319            super(looper);
320        }
321
322        @Override
323        public void handleMessage(Message msg) {
324            switch (msg.what) {
325              case WifiP2pManager.SET_DEVICE_NAME:
326              case WifiP2pManager.SET_WFD_INFO:
327              case WifiP2pManager.DISCOVER_PEERS:
328              case WifiP2pManager.STOP_DISCOVERY:
329              case WifiP2pManager.CONNECT:
330              case WifiP2pManager.CANCEL_CONNECT:
331              case WifiP2pManager.CREATE_GROUP:
332              case WifiP2pManager.REMOVE_GROUP:
333              case WifiP2pManager.START_LISTEN:
334              case WifiP2pManager.STOP_LISTEN:
335              case WifiP2pManager.SET_CHANNEL:
336              case WifiP2pManager.START_WPS:
337              case WifiP2pManager.ADD_LOCAL_SERVICE:
338              case WifiP2pManager.REMOVE_LOCAL_SERVICE:
339              case WifiP2pManager.CLEAR_LOCAL_SERVICES:
340              case WifiP2pManager.DISCOVER_SERVICES:
341              case WifiP2pManager.ADD_SERVICE_REQUEST:
342              case WifiP2pManager.REMOVE_SERVICE_REQUEST:
343              case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
344              case WifiP2pManager.REQUEST_PEERS:
345              case WifiP2pManager.REQUEST_CONNECTION_INFO:
346              case WifiP2pManager.REQUEST_GROUP_INFO:
347              case WifiP2pManager.DELETE_PERSISTENT_GROUP:
348              case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
349                mP2pStateMachine.sendMessage(Message.obtain(msg));
350                break;
351              default:
352                Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg);
353                break;
354            }
355        }
356    }
357    private ClientHandler mClientHandler;
358
359    public WifiP2pServiceImpl(Context context) {
360        mContext = context;
361
362        //STOPSHIP: get this from native side
363        mInterface = "p2p0";
364        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
365
366        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
367                PackageManager.FEATURE_WIFI_DIRECT);
368
369        mThisDevice.primaryDeviceType = mContext.getResources().getString(
370                com.android.internal.R.string.config_wifi_p2p_device_type);
371
372        HandlerThread wifiP2pThread = new HandlerThread("WifiP2pService");
373        wifiP2pThread.start();
374        mClientHandler = new ClientHandler(wifiP2pThread.getLooper());
375
376        mP2pStateMachine = new P2pStateMachine(TAG, wifiP2pThread.getLooper(), mP2pSupported);
377        mP2pStateMachine.start();
378    }
379
380    public void connectivityServiceReady() {
381        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
382        mNwService = INetworkManagementService.Stub.asInterface(b);
383    }
384
385    private void enforceAccessPermission() {
386        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
387                "WifiP2pService");
388    }
389
390    private void enforceChangePermission() {
391        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
392                "WifiP2pService");
393    }
394
395    private void enforceConnectivityInternalPermission() {
396        mContext.enforceCallingOrSelfPermission(
397                android.Manifest.permission.CONNECTIVITY_INTERNAL,
398                "WifiP2pService");
399    }
400
401    /**
402     * Get a reference to handler. This is used by a client to establish
403     * an AsyncChannel communication with WifiP2pService
404     */
405    public Messenger getMessenger() {
406        enforceAccessPermission();
407        enforceChangePermission();
408        return new Messenger(mClientHandler);
409    }
410
411    /**
412     * Get a reference to handler. This is used by a WifiStateMachine to establish
413     * an AsyncChannel communication with P2pStateMachine
414     * @hide
415     */
416    public Messenger getP2pStateMachineMessenger() {
417        enforceConnectivityInternalPermission();
418        enforceAccessPermission();
419        enforceChangePermission();
420        return new Messenger(mP2pStateMachine.getHandler());
421    }
422
423    /** This is used to provide information to drivers to optimize performance depending
424     * on the current mode of operation.
425     * 0 - disabled
426     * 1 - source operation
427     * 2 - sink operation
428     *
429     * As an example, the driver could reduce the channel dwell time during scanning
430     * when acting as a source or sink to minimize impact on miracast.
431     */
432    public void setMiracastMode(int mode) {
433        enforceConnectivityInternalPermission();
434        mP2pStateMachine.sendMessage(SET_MIRACAST_MODE, mode);
435    }
436
437    @Override
438    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
439        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
440                != PackageManager.PERMISSION_GRANTED) {
441            pw.println("Permission Denial: can't dump WifiP2pService from from pid="
442                    + Binder.getCallingPid()
443                    + ", uid=" + Binder.getCallingUid());
444            return;
445        }
446        mP2pStateMachine.dump(fd, pw, args);
447        pw.println("mAutonomousGroup " + mAutonomousGroup);
448        pw.println("mJoinExistingGroup " + mJoinExistingGroup);
449        pw.println("mDiscoveryStarted " + mDiscoveryStarted);
450        pw.println("mNetworkInfo " + mNetworkInfo);
451        pw.println("mTemporarilyDisconnectedWifi " + mTemporarilyDisconnectedWifi);
452        pw.println("mServiceDiscReqId " + mServiceDiscReqId);
453        pw.println();
454    }
455
456
457    /**
458     * Handles interaction with WifiStateMachine
459     */
460    private class P2pStateMachine extends StateMachine {
461
462        private DefaultState mDefaultState = new DefaultState();
463        private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
464        private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
465        private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
466        private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
467        private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
468        // Inactive is when p2p is enabled with no connectivity
469        private InactiveState mInactiveState = new InactiveState();
470        private GroupCreatingState mGroupCreatingState = new GroupCreatingState();
471        private UserAuthorizingInviteRequestState mUserAuthorizingInviteRequestState
472                = new UserAuthorizingInviteRequestState();
473        private UserAuthorizingNegotiationRequestState mUserAuthorizingNegotiationRequestState
474                = new UserAuthorizingNegotiationRequestState();
475        private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState();
476        private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
477        private FrequencyConflictState mFrequencyConflictState =new FrequencyConflictState();
478
479        private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
480        private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState();
481        private OngoingGroupRemovalState mOngoingGroupRemovalState = new OngoingGroupRemovalState();
482
483        private WifiNative mWifiNative = new WifiNative(mInterface);
484        private WifiMonitor mWifiMonitor = new WifiMonitor(this, mWifiNative);
485
486        private final WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
487        /* During a connection, supplicant can tell us that a device was lost. From a supplicant's
488         * perspective, the discovery stops during connection and it purges device since it does
489         * not get latest updates about the device without being in discovery state.
490         *
491         * From the framework perspective, the device is still there since we are connecting or
492         * connected to it. so we keep these devices in a separate list, so that they are removed
493         * when connection is cancelled or lost
494         */
495        private final WifiP2pDeviceList mPeersLostDuringConnection = new WifiP2pDeviceList();
496        private final WifiP2pGroupList mGroups = new WifiP2pGroupList(null,
497                new GroupDeleteListener() {
498            @Override
499            public void onDeleteGroup(int netId) {
500                if (DBG) logd("called onDeleteGroup() netId=" + netId);
501                mWifiNative.removeNetwork(netId);
502                mWifiNative.saveConfig();
503                sendP2pPersistentGroupsChangedBroadcast();
504            }
505        });
506        private final WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
507        private WifiP2pGroup mGroup;
508
509        // Saved WifiP2pConfig for an ongoing peer connection. This will never be null.
510        // The deviceAddress will be an empty string when the device is inactive
511        // or if it is connected without any ongoing join request
512        private WifiP2pConfig mSavedPeerConfig = new WifiP2pConfig();
513
514        // Saved WifiP2pGroup from invitation request
515        private WifiP2pGroup mSavedP2pGroup;
516
517        P2pStateMachine(String name, Looper looper, boolean p2pSupported) {
518            super(name, looper);
519
520            addState(mDefaultState);
521                addState(mP2pNotSupportedState, mDefaultState);
522                addState(mP2pDisablingState, mDefaultState);
523                addState(mP2pDisabledState, mDefaultState);
524                addState(mP2pEnablingState, mDefaultState);
525                addState(mP2pEnabledState, mDefaultState);
526                    addState(mInactiveState, mP2pEnabledState);
527                    addState(mGroupCreatingState, mP2pEnabledState);
528                        addState(mUserAuthorizingInviteRequestState, mGroupCreatingState);
529                        addState(mUserAuthorizingNegotiationRequestState, mGroupCreatingState);
530                        addState(mProvisionDiscoveryState, mGroupCreatingState);
531                        addState(mGroupNegotiationState, mGroupCreatingState);
532                        addState(mFrequencyConflictState, mGroupCreatingState);
533                    addState(mGroupCreatedState, mP2pEnabledState);
534                        addState(mUserAuthorizingJoinState, mGroupCreatedState);
535                        addState(mOngoingGroupRemovalState, mGroupCreatedState);
536
537            if (p2pSupported) {
538                setInitialState(mP2pDisabledState);
539            } else {
540                setInitialState(mP2pNotSupportedState);
541            }
542            setLogRecSize(50);
543            setLogOnlyTransitions(true);
544        }
545
546    class DefaultState extends State {
547        @Override
548        public boolean processMessage(Message message) {
549            if (DBG) logd(getName() + message.toString());
550            switch (message.what) {
551                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
552                    if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
553                        if (DBG) logd("Full connection with WifiStateMachine established");
554                        mWifiChannel = (AsyncChannel) message.obj;
555                    } else {
556                        loge("Full connection failure, error = " + message.arg1);
557                        mWifiChannel = null;
558                    }
559                    break;
560
561                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
562                    if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
563                        loge("Send failed, client connection lost");
564                    } else {
565                        loge("Client connection lost with reason: " + message.arg1);
566                    }
567                    mWifiChannel = null;
568                    break;
569
570                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
571                    AsyncChannel ac = new AsyncChannel();
572                    ac.connect(mContext, getHandler(), message.replyTo);
573                    break;
574                case BLOCK_DISCOVERY:
575                    mDiscoveryBlocked = (message.arg1 == ENABLED ? true : false);
576                    // always reset this - we went to a state that doesn't support discovery so
577                    // it would have stopped regardless
578                    mDiscoveryPostponed = false;
579                    if (mDiscoveryBlocked) {
580                        try {
581                            StateMachine m = (StateMachine)message.obj;
582                            m.sendMessage(message.arg2);
583                        } catch (Exception e) {
584                            loge("unable to send BLOCK_DISCOVERY response: " + e);
585                        }
586                    }
587                    break;
588                case WifiP2pManager.DISCOVER_PEERS:
589                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
590                            WifiP2pManager.BUSY);
591                    break;
592                case WifiP2pManager.STOP_DISCOVERY:
593                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
594                            WifiP2pManager.BUSY);
595                    break;
596                case WifiP2pManager.DISCOVER_SERVICES:
597                    replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
598                            WifiP2pManager.BUSY);
599                    break;
600                case WifiP2pManager.CONNECT:
601                    replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
602                            WifiP2pManager.BUSY);
603                    break;
604                case WifiP2pManager.CANCEL_CONNECT:
605                    replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
606                            WifiP2pManager.BUSY);
607                    break;
608                case WifiP2pManager.CREATE_GROUP:
609                    replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
610                            WifiP2pManager.BUSY);
611                    break;
612                case WifiP2pManager.REMOVE_GROUP:
613                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
614                            WifiP2pManager.BUSY);
615                    break;
616                case WifiP2pManager.ADD_LOCAL_SERVICE:
617                    replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
618                            WifiP2pManager.BUSY);
619                    break;
620                case WifiP2pManager.REMOVE_LOCAL_SERVICE:
621                    replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
622                            WifiP2pManager.BUSY);
623                    break;
624                case WifiP2pManager.CLEAR_LOCAL_SERVICES:
625                    replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
626                            WifiP2pManager.BUSY);
627                    break;
628                case WifiP2pManager.ADD_SERVICE_REQUEST:
629                    replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
630                            WifiP2pManager.BUSY);
631                    break;
632                case WifiP2pManager.REMOVE_SERVICE_REQUEST:
633                    replyToMessage(message,
634                            WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
635                            WifiP2pManager.BUSY);
636                    break;
637                case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
638                    replyToMessage(message,
639                            WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
640                            WifiP2pManager.BUSY);
641                    break;
642                case WifiP2pManager.SET_DEVICE_NAME:
643                    replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
644                            WifiP2pManager.BUSY);
645                    break;
646                case WifiP2pManager.DELETE_PERSISTENT_GROUP:
647                    replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
648                            WifiP2pManager.BUSY);
649                    break;
650                case WifiP2pManager.SET_WFD_INFO:
651                    replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
652                            WifiP2pManager.BUSY);
653                    break;
654                case WifiP2pManager.REQUEST_PEERS:
655                    replyToMessage(message, WifiP2pManager.RESPONSE_PEERS,
656                            new WifiP2pDeviceList(mPeers));
657                    break;
658                case WifiP2pManager.REQUEST_CONNECTION_INFO:
659                    replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO,
660                            new WifiP2pInfo(mWifiP2pInfo));
661                    break;
662                case WifiP2pManager.REQUEST_GROUP_INFO:
663                    replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO,
664                            mGroup != null ? new WifiP2pGroup(mGroup) : null);
665                    break;
666                case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
667                    replyToMessage(message, WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO,
668                            new WifiP2pGroupList(mGroups, null));
669                    break;
670                case WifiP2pManager.START_WPS:
671                    replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
672                        WifiP2pManager.BUSY);
673                    break;
674                case WifiP2pManager.GET_HANDOVER_REQUEST:
675                case WifiP2pManager.GET_HANDOVER_SELECT:
676                    replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE, null);
677                    break;
678                case WifiP2pManager.INITIATOR_REPORT_NFC_HANDOVER:
679                case WifiP2pManager.RESPONDER_REPORT_NFC_HANDOVER:
680                    replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED,
681                            WifiP2pManager.BUSY);
682                    break;
683                    // Ignore
684                case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
685                case WifiMonitor.SCAN_RESULTS_EVENT:
686                case WifiMonitor.SUP_CONNECTION_EVENT:
687                case WifiMonitor.SUP_DISCONNECTION_EVENT:
688                case WifiMonitor.NETWORK_CONNECTION_EVENT:
689                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
690                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
691                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
692                case WifiMonitor.WPS_SUCCESS_EVENT:
693                case WifiMonitor.WPS_FAIL_EVENT:
694                case WifiMonitor.WPS_OVERLAP_EVENT:
695                case WifiMonitor.WPS_TIMEOUT_EVENT:
696                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
697                case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
698                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
699                case WifiMonitor.P2P_FIND_STOPPED_EVENT:
700                case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
701                case PEER_CONNECTION_USER_ACCEPT:
702                case PEER_CONNECTION_USER_REJECT:
703                case DISCONNECT_WIFI_RESPONSE:
704                case DROP_WIFI_USER_ACCEPT:
705                case DROP_WIFI_USER_REJECT:
706                case GROUP_CREATING_TIMED_OUT:
707                case DISABLE_P2P_TIMED_OUT:
708                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
709                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
710                case DhcpStateMachine.CMD_ON_QUIT:
711                case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
712                case SET_MIRACAST_MODE:
713                case WifiP2pManager.START_LISTEN:
714                case WifiP2pManager.STOP_LISTEN:
715                case WifiP2pManager.SET_CHANNEL:
716                case SET_COUNTRY_CODE:
717                    break;
718                case WifiStateMachine.CMD_ENABLE_P2P:
719                    // Enable is lazy and has no response
720                    break;
721                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
722                    // If we end up handling in default, p2p is not enabled
723                    mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
724                    break;
725                    /* unexpected group created, remove */
726                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
727                    mGroup = (WifiP2pGroup) message.obj;
728                    loge("Unexpected group creation, remove " + mGroup);
729                    mWifiNative.p2pGroupRemove(mGroup.getInterface());
730                    break;
731                // A group formation failure is always followed by
732                // a group removed event. Flushing things at group formation
733                // failure causes supplicant issues. Ignore right now.
734                case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
735                    break;
736                default:
737                    loge("Unhandled message " + message);
738                    return NOT_HANDLED;
739            }
740            return HANDLED;
741        }
742    }
743
744    class P2pNotSupportedState extends State {
745        @Override
746        public boolean processMessage(Message message) {
747            switch (message.what) {
748               case WifiP2pManager.DISCOVER_PEERS:
749                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
750                            WifiP2pManager.P2P_UNSUPPORTED);
751                    break;
752                case WifiP2pManager.STOP_DISCOVERY:
753                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
754                            WifiP2pManager.P2P_UNSUPPORTED);
755                    break;
756                case WifiP2pManager.DISCOVER_SERVICES:
757                    replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
758                            WifiP2pManager.P2P_UNSUPPORTED);
759                    break;
760                case WifiP2pManager.CONNECT:
761                    replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
762                            WifiP2pManager.P2P_UNSUPPORTED);
763                    break;
764                case WifiP2pManager.CANCEL_CONNECT:
765                    replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
766                            WifiP2pManager.P2P_UNSUPPORTED);
767                    break;
768               case WifiP2pManager.CREATE_GROUP:
769                    replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
770                            WifiP2pManager.P2P_UNSUPPORTED);
771                    break;
772                case WifiP2pManager.REMOVE_GROUP:
773                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
774                            WifiP2pManager.P2P_UNSUPPORTED);
775                    break;
776                case WifiP2pManager.ADD_LOCAL_SERVICE:
777                    replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
778                            WifiP2pManager.P2P_UNSUPPORTED);
779                    break;
780                case WifiP2pManager.REMOVE_LOCAL_SERVICE:
781                    replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
782                            WifiP2pManager.P2P_UNSUPPORTED);
783                    break;
784                case WifiP2pManager.CLEAR_LOCAL_SERVICES:
785                    replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
786                            WifiP2pManager.P2P_UNSUPPORTED);
787                    break;
788                case WifiP2pManager.ADD_SERVICE_REQUEST:
789                    replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
790                            WifiP2pManager.P2P_UNSUPPORTED);
791                    break;
792                case WifiP2pManager.REMOVE_SERVICE_REQUEST:
793                    replyToMessage(message,
794                            WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
795                            WifiP2pManager.P2P_UNSUPPORTED);
796                    break;
797                case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
798                    replyToMessage(message,
799                            WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
800                            WifiP2pManager.P2P_UNSUPPORTED);
801                    break;
802                case WifiP2pManager.SET_DEVICE_NAME:
803                    replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
804                            WifiP2pManager.P2P_UNSUPPORTED);
805                    break;
806                case WifiP2pManager.DELETE_PERSISTENT_GROUP:
807                    replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
808                            WifiP2pManager.P2P_UNSUPPORTED);
809                    break;
810                case WifiP2pManager.SET_WFD_INFO:
811                    replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
812                            WifiP2pManager.P2P_UNSUPPORTED);
813                    break;
814                case WifiP2pManager.START_WPS:
815                    replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
816                            WifiP2pManager.P2P_UNSUPPORTED);
817                    break;
818                case WifiP2pManager.START_LISTEN:
819                    replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED,
820                            WifiP2pManager.P2P_UNSUPPORTED);
821                    break;
822                case WifiP2pManager.STOP_LISTEN:
823                    replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED,
824                            WifiP2pManager.P2P_UNSUPPORTED);
825                    break;
826
827                default:
828                    return NOT_HANDLED;
829            }
830            return HANDLED;
831        }
832    }
833
834    class P2pDisablingState extends State {
835        @Override
836        public void enter() {
837            if (DBG) logd(getName());
838            sendMessageDelayed(obtainMessage(DISABLE_P2P_TIMED_OUT,
839                    ++mDisableP2pTimeoutIndex, 0), DISABLE_P2P_WAIT_TIME_MS);
840        }
841
842        @Override
843        public boolean processMessage(Message message) {
844            if (DBG) logd(getName() + message.toString());
845            switch (message.what) {
846                case WifiMonitor.SUP_DISCONNECTION_EVENT:
847                    if (DBG) logd("p2p socket connection lost");
848                    transitionTo(mP2pDisabledState);
849                    break;
850                case WifiStateMachine.CMD_ENABLE_P2P:
851                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
852                    deferMessage(message);
853                    break;
854                case DISABLE_P2P_TIMED_OUT:
855                    if (mDisableP2pTimeoutIndex == message.arg1) {
856                        loge("P2p disable timed out");
857                        transitionTo(mP2pDisabledState);
858                    }
859                    break;
860                default:
861                    return NOT_HANDLED;
862            }
863            return HANDLED;
864        }
865
866        @Override
867        public void exit() {
868            mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
869        }
870    }
871
872    class P2pDisabledState extends State {
873       @Override
874        public void enter() {
875            if (DBG) logd(getName());
876        }
877
878        @Override
879        public boolean processMessage(Message message) {
880            if (DBG) logd(getName() + message.toString());
881            switch (message.what) {
882                case WifiStateMachine.CMD_ENABLE_P2P:
883                    try {
884                        mNwService.setInterfaceUp(mInterface);
885                    } catch (RemoteException re) {
886                        loge("Unable to change interface settings: " + re);
887                    } catch (IllegalStateException ie) {
888                        loge("Unable to change interface settings: " + ie);
889                    }
890                    mWifiMonitor.startMonitoring();
891                    transitionTo(mP2pEnablingState);
892                    break;
893                default:
894                    return NOT_HANDLED;
895            }
896            return HANDLED;
897        }
898    }
899
900    class P2pEnablingState extends State {
901        @Override
902        public void enter() {
903            if (DBG) logd(getName());
904        }
905
906        @Override
907        public boolean processMessage(Message message) {
908            if (DBG) logd(getName() + message.toString());
909            switch (message.what) {
910                case WifiMonitor.SUP_CONNECTION_EVENT:
911                    if (DBG) logd("P2p socket connection successful");
912                    transitionTo(mInactiveState);
913                    break;
914                case WifiMonitor.SUP_DISCONNECTION_EVENT:
915                    loge("P2p socket connection failed");
916                    transitionTo(mP2pDisabledState);
917                    break;
918                case WifiStateMachine.CMD_ENABLE_P2P:
919                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
920                    deferMessage(message);
921                    break;
922                default:
923                    return NOT_HANDLED;
924            }
925            return HANDLED;
926        }
927    }
928
929    class P2pEnabledState extends State {
930        @Override
931        public void enter() {
932            if (DBG) logd(getName());
933            sendP2pStateChangedBroadcast(true);
934            mNetworkInfo.setIsAvailable(true);
935            sendP2pConnectionChangedBroadcast();
936            initializeP2pSettings();
937        }
938
939        @Override
940        public boolean processMessage(Message message) {
941            if (DBG) logd(getName() + message.toString());
942            switch (message.what) {
943                case WifiMonitor.SUP_DISCONNECTION_EVENT:
944                    loge("Unexpected loss of p2p socket connection");
945                    transitionTo(mP2pDisabledState);
946                    break;
947                case WifiStateMachine.CMD_ENABLE_P2P:
948                    //Nothing to do
949                    break;
950                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
951                    if (mPeers.clear()) {
952                        sendPeersChangedBroadcast();
953                    }
954                    if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
955
956                    mWifiMonitor.stopMonitoring();
957                    transitionTo(mP2pDisablingState);
958                    break;
959                case WifiP2pManager.SET_DEVICE_NAME:
960                {
961                    WifiP2pDevice d = (WifiP2pDevice) message.obj;
962                    if (d != null && setAndPersistDeviceName(d.deviceName)) {
963                        if (DBG) logd("set device name " + d.deviceName);
964                        replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED);
965                    } else {
966                        replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
967                                WifiP2pManager.ERROR);
968                    }
969                    break;
970                }
971                case WifiP2pManager.SET_WFD_INFO:
972                {
973                    WifiP2pWfdInfo d = (WifiP2pWfdInfo) message.obj;
974                    if (d != null && setWfdInfo(d)) {
975                        replyToMessage(message, WifiP2pManager.SET_WFD_INFO_SUCCEEDED);
976                    } else {
977                        replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
978                                WifiP2pManager.ERROR);
979                    }
980                    break;
981                }
982                case BLOCK_DISCOVERY:
983                    boolean blocked = (message.arg1 == ENABLED ? true : false);
984                    if (mDiscoveryBlocked == blocked) break;
985                    mDiscoveryBlocked = blocked;
986                    if (blocked && mDiscoveryStarted) {
987                        mWifiNative.p2pStopFind();
988                        mDiscoveryPostponed = true;
989                    }
990                    if (!blocked && mDiscoveryPostponed) {
991                        mDiscoveryPostponed = false;
992                        mWifiNative.p2pFind(DISCOVER_TIMEOUT_S);
993                    }
994                    if (blocked) {
995                        try {
996                            StateMachine m = (StateMachine)message.obj;
997                            m.sendMessage(message.arg2);
998                        } catch (Exception e) {
999                            loge("unable to send BLOCK_DISCOVERY response: " + e);
1000                        }
1001                    }
1002                    break;
1003                case WifiP2pManager.DISCOVER_PEERS:
1004                    if (mDiscoveryBlocked) {
1005                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
1006                                WifiP2pManager.BUSY);
1007                        break;
1008                    }
1009                    // do not send service discovery request while normal find operation.
1010                    clearSupplicantServiceRequest();
1011                    if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
1012                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
1013                        sendP2pDiscoveryChangedBroadcast(true);
1014                    } else {
1015                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
1016                                WifiP2pManager.ERROR);
1017                    }
1018                    break;
1019                case WifiMonitor.P2P_FIND_STOPPED_EVENT:
1020                    sendP2pDiscoveryChangedBroadcast(false);
1021                    break;
1022                case WifiP2pManager.STOP_DISCOVERY:
1023                    if (mWifiNative.p2pStopFind()) {
1024                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
1025                    } else {
1026                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
1027                                WifiP2pManager.ERROR);
1028                    }
1029                    break;
1030                case WifiP2pManager.DISCOVER_SERVICES:
1031                    if (mDiscoveryBlocked) {
1032                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
1033                                WifiP2pManager.BUSY);
1034                        break;
1035                    }
1036                    if (DBG) logd(getName() + " discover services");
1037                    if (!updateSupplicantServiceRequest()) {
1038                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
1039                                WifiP2pManager.NO_SERVICE_REQUESTS);
1040                        break;
1041                    }
1042                    if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
1043                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED);
1044                    } else {
1045                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
1046                                WifiP2pManager.ERROR);
1047                    }
1048                    break;
1049                case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
1050                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
1051                    if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
1052                    mPeers.updateSupplicantDetails(device);
1053                    sendPeersChangedBroadcast();
1054                    break;
1055                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
1056                    device = (WifiP2pDevice) message.obj;
1057                    // Gets current details for the one removed
1058                    device = mPeers.remove(device.deviceAddress);
1059                    if (device != null) {
1060                        sendPeersChangedBroadcast();
1061                    }
1062                    break;
1063                case WifiP2pManager.ADD_LOCAL_SERVICE:
1064                    if (DBG) logd(getName() + " add service");
1065                    WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo)message.obj;
1066                    if (addLocalService(message.replyTo, servInfo)) {
1067                        replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_SUCCEEDED);
1068                    } else {
1069                        replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED);
1070                    }
1071                    break;
1072                case WifiP2pManager.REMOVE_LOCAL_SERVICE:
1073                    if (DBG) logd(getName() + " remove service");
1074                    servInfo = (WifiP2pServiceInfo)message.obj;
1075                    removeLocalService(message.replyTo, servInfo);
1076                    replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_SUCCEEDED);
1077                    break;
1078                case WifiP2pManager.CLEAR_LOCAL_SERVICES:
1079                    if (DBG) logd(getName() + " clear service");
1080                    clearLocalServices(message.replyTo);
1081                    replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_SUCCEEDED);
1082                    break;
1083                case WifiP2pManager.ADD_SERVICE_REQUEST:
1084                    if (DBG) logd(getName() + " add service request");
1085                    if (!addServiceRequest(message.replyTo, (WifiP2pServiceRequest)message.obj)) {
1086                        replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED);
1087                        break;
1088                    }
1089                    replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_SUCCEEDED);
1090                    break;
1091                case WifiP2pManager.REMOVE_SERVICE_REQUEST:
1092                    if (DBG) logd(getName() + " remove service request");
1093                    removeServiceRequest(message.replyTo, (WifiP2pServiceRequest)message.obj);
1094                    replyToMessage(message, WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED);
1095                    break;
1096                case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
1097                    if (DBG) logd(getName() + " clear service request");
1098                    clearServiceRequests(message.replyTo);
1099                    replyToMessage(message, WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED);
1100                    break;
1101                case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
1102                    if (DBG) logd(getName() + " receive service response");
1103                    List<WifiP2pServiceResponse> sdRespList =
1104                        (List<WifiP2pServiceResponse>) message.obj;
1105                    for (WifiP2pServiceResponse resp : sdRespList) {
1106                        WifiP2pDevice dev =
1107                            mPeers.get(resp.getSrcDevice().deviceAddress);
1108                        resp.setSrcDevice(dev);
1109                        sendServiceResponse(resp);
1110                    }
1111                    break;
1112                case WifiP2pManager.DELETE_PERSISTENT_GROUP:
1113                   if (DBG) logd(getName() + " delete persistent group");
1114                   mGroups.remove(message.arg1);
1115                   replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED);
1116                   break;
1117                case SET_MIRACAST_MODE:
1118                    mWifiNative.setMiracastMode(message.arg1);
1119                    break;
1120                case WifiP2pManager.START_LISTEN:
1121                    if (DBG) logd(getName() + " start listen mode");
1122                    mWifiNative.p2pFlush();
1123                    if (mWifiNative.p2pExtListen(true, 500, 500)) {
1124                        replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
1125                    } else {
1126                        replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
1127                    }
1128                    break;
1129                case WifiP2pManager.STOP_LISTEN:
1130                    if (DBG) logd(getName() + " stop listen mode");
1131                    if (mWifiNative.p2pExtListen(false, 0, 0)) {
1132                        replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
1133                    } else {
1134                        replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
1135                    }
1136                    mWifiNative.p2pFlush();
1137                    break;
1138                case WifiP2pManager.SET_CHANNEL:
1139                    Bundle p2pChannels = (Bundle) message.obj;
1140                    int lc = p2pChannels.getInt("lc", 0);
1141                    int oc = p2pChannels.getInt("oc", 0);
1142                    if (DBG) logd(getName() + " set listen and operating channel");
1143                    if (mWifiNative.p2pSetChannel(lc, oc)) {
1144                        replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
1145                    } else {
1146                        replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
1147                    }
1148                    break;
1149                case SET_COUNTRY_CODE:
1150                    String countryCode = (String) message.obj;
1151                    countryCode = countryCode.toUpperCase(Locale.ROOT);
1152                    if (mLastSetCountryCode == null ||
1153                            countryCode.equals(mLastSetCountryCode) == false) {
1154                        if (mWifiNative.setCountryCode(countryCode)) {
1155                            mLastSetCountryCode = countryCode;
1156                        }
1157                    }
1158                    break;
1159                case WifiP2pManager.GET_HANDOVER_REQUEST:
1160                    Bundle requestBundle = new Bundle();
1161                    requestBundle.putString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE,
1162                            mWifiNative.getNfcHandoverRequest());
1163                    replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE,
1164                            requestBundle);
1165                    break;
1166                case WifiP2pManager.GET_HANDOVER_SELECT:
1167                    Bundle selectBundle = new Bundle();
1168                    selectBundle.putString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE,
1169                            mWifiNative.getNfcHandoverSelect());
1170                    replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE,
1171                            selectBundle);
1172                    break;
1173                default:
1174                   return NOT_HANDLED;
1175            }
1176            return HANDLED;
1177        }
1178
1179        @Override
1180        public void exit() {
1181            sendP2pDiscoveryChangedBroadcast(false);
1182            sendP2pStateChangedBroadcast(false);
1183            mNetworkInfo.setIsAvailable(false);
1184
1185            mLastSetCountryCode = null;
1186        }
1187    }
1188
1189    class InactiveState extends State {
1190        @Override
1191        public void enter() {
1192            if (DBG) logd(getName());
1193            mSavedPeerConfig.invalidate();
1194        }
1195
1196        @Override
1197        public boolean processMessage(Message message) {
1198            if (DBG) logd(getName() + message.toString());
1199            switch (message.what) {
1200                case WifiP2pManager.CONNECT:
1201                    if (DBG) logd(getName() + " sending connect");
1202                    WifiP2pConfig config = (WifiP2pConfig) message.obj;
1203                    if (isConfigInvalid(config)) {
1204                        loge("Dropping connect requeset " + config);
1205                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
1206                        break;
1207                    }
1208
1209                    mAutonomousGroup = false;
1210                    mWifiNative.p2pStopFind();
1211                    if (reinvokePersistentGroup(config)) {
1212                        transitionTo(mGroupNegotiationState);
1213                    } else {
1214                        transitionTo(mProvisionDiscoveryState);
1215                    }
1216                    mSavedPeerConfig = config;
1217                    mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1218                    sendPeersChangedBroadcast();
1219                    replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
1220                    break;
1221                case WifiP2pManager.STOP_DISCOVERY:
1222                    if (mWifiNative.p2pStopFind()) {
1223                        // When discovery stops in inactive state, flush to clear
1224                        // state peer data
1225                        mWifiNative.p2pFlush();
1226                        mServiceDiscReqId = null;
1227                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
1228                    } else {
1229                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
1230                                WifiP2pManager.ERROR);
1231                    }
1232                    break;
1233                case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
1234                    config = (WifiP2pConfig) message.obj;
1235                    if (isConfigInvalid(config)) {
1236                        loge("Dropping GO neg request " + config);
1237                        break;
1238                    }
1239                    mSavedPeerConfig = config;
1240                    mAutonomousGroup = false;
1241                    mJoinExistingGroup = false;
1242                    transitionTo(mUserAuthorizingNegotiationRequestState);
1243                    break;
1244                case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
1245                    WifiP2pGroup group = (WifiP2pGroup) message.obj;
1246                    WifiP2pDevice owner = group.getOwner();
1247
1248                    if (owner == null) {
1249                        loge("Ignored invitation from null owner");
1250                        break;
1251                    }
1252
1253                    config = new WifiP2pConfig();
1254                    config.deviceAddress = group.getOwner().deviceAddress;
1255
1256                    if (isConfigInvalid(config)) {
1257                        loge("Dropping invitation request " + config);
1258                        break;
1259                    }
1260                    mSavedPeerConfig = config;
1261
1262                    //Check if we have the owner in peer list and use appropriate
1263                    //wps method. Default is to use PBC.
1264                    if ((owner = mPeers.get(owner.deviceAddress)) != null) {
1265                        if (owner.wpsPbcSupported()) {
1266                            mSavedPeerConfig.wps.setup = WpsInfo.PBC;
1267                        } else if (owner.wpsKeypadSupported()) {
1268                            mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
1269                        } else if (owner.wpsDisplaySupported()) {
1270                            mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
1271                        }
1272                    }
1273
1274                    mAutonomousGroup = false;
1275                    mJoinExistingGroup = true;
1276                    transitionTo(mUserAuthorizingInviteRequestState);
1277                    break;
1278                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1279                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1280                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1281                    //We let the supplicant handle the provision discovery response
1282                    //and wait instead for the GO_NEGOTIATION_REQUEST_EVENT.
1283                    //Handling provision discovery and issuing a p2p_connect before
1284                    //group negotiation comes through causes issues
1285                    break;
1286                case WifiP2pManager.CREATE_GROUP:
1287                    mAutonomousGroup = true;
1288                    int netId = message.arg1;
1289                    boolean ret = false;
1290                    if (netId == WifiP2pGroup.PERSISTENT_NET_ID) {
1291                        // check if the go persistent group is present.
1292                        netId = mGroups.getNetworkId(mThisDevice.deviceAddress);
1293                        if (netId != -1) {
1294                            ret = mWifiNative.p2pGroupAdd(netId);
1295                        } else {
1296                            ret = mWifiNative.p2pGroupAdd(true);
1297                        }
1298                    } else {
1299                        ret = mWifiNative.p2pGroupAdd(false);
1300                    }
1301
1302                    if (ret) {
1303                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
1304                        transitionTo(mGroupNegotiationState);
1305                    } else {
1306                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
1307                                WifiP2pManager.ERROR);
1308                        // remain at this state.
1309                    }
1310                    break;
1311                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1312                    mGroup = (WifiP2pGroup) message.obj;
1313                    if (DBG) logd(getName() + " group started");
1314
1315                    // We hit this scenario when a persistent group is reinvoked
1316                    if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
1317                        mAutonomousGroup = false;
1318                        deferMessage(message);
1319                        transitionTo(mGroupNegotiationState);
1320                    } else {
1321                        loge("Unexpected group creation, remove " + mGroup);
1322                        mWifiNative.p2pGroupRemove(mGroup.getInterface());
1323                    }
1324                    break;
1325                case WifiP2pManager.START_LISTEN:
1326                    if (DBG) logd(getName() + " start listen mode");
1327                    mWifiNative.p2pFlush();
1328                    if (mWifiNative.p2pExtListen(true, 500, 500)) {
1329                        replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
1330                    } else {
1331                        replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
1332                    }
1333                    break;
1334                case WifiP2pManager.STOP_LISTEN:
1335                    if (DBG) logd(getName() + " stop listen mode");
1336                    if (mWifiNative.p2pExtListen(false, 0, 0)) {
1337                        replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
1338                    } else {
1339                        replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
1340                    }
1341                    mWifiNative.p2pFlush();
1342                    break;
1343                case WifiP2pManager.SET_CHANNEL:
1344                    Bundle p2pChannels = (Bundle) message.obj;
1345                    int lc = p2pChannels.getInt("lc", 0);
1346                    int oc = p2pChannels.getInt("oc", 0);
1347                    if (DBG) logd(getName() + " set listen and operating channel");
1348                    if (mWifiNative.p2pSetChannel(lc, oc)) {
1349                        replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
1350                    } else {
1351                        replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
1352                    }
1353                    break;
1354                case WifiP2pManager.INITIATOR_REPORT_NFC_HANDOVER:
1355                    String handoverSelect = null;
1356
1357                    if (message.obj != null) {
1358                        handoverSelect = ((Bundle) message.obj)
1359                                .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE);
1360                    }
1361
1362                    if (handoverSelect != null
1363                            && mWifiNative.initiatorReportNfcHandover(handoverSelect)) {
1364                        replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED);
1365                        transitionTo(mGroupCreatingState);
1366                    } else {
1367                        replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED);
1368                    }
1369                    break;
1370                case WifiP2pManager.RESPONDER_REPORT_NFC_HANDOVER:
1371                    String handoverRequest = null;
1372
1373                    if (message.obj != null) {
1374                        handoverRequest = ((Bundle) message.obj)
1375                                .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE);
1376                    }
1377
1378                    if (handoverRequest != null
1379                            && mWifiNative.responderReportNfcHandover(handoverRequest)) {
1380                        replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED);
1381                        transitionTo(mGroupCreatingState);
1382                    } else {
1383                        replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED);
1384                    }
1385                    break;
1386                default:
1387                    return NOT_HANDLED;
1388            }
1389            return HANDLED;
1390        }
1391    }
1392
1393    class GroupCreatingState extends State {
1394        @Override
1395        public void enter() {
1396            if (DBG) logd(getName());
1397            sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,
1398                    ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);
1399        }
1400
1401        @Override
1402        public boolean processMessage(Message message) {
1403            if (DBG) logd(getName() + message.toString());
1404            boolean ret = HANDLED;
1405            switch (message.what) {
1406               case GROUP_CREATING_TIMED_OUT:
1407                    if (mGroupCreatingTimeoutIndex == message.arg1) {
1408                        if (DBG) logd("Group negotiation timed out");
1409                        handleGroupCreationFailure();
1410                        transitionTo(mInactiveState);
1411                    }
1412                    break;
1413                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
1414                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
1415                    if (!mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
1416                        if (DBG) {
1417                            logd("mSavedPeerConfig " + mSavedPeerConfig.deviceAddress +
1418                                "device " + device.deviceAddress);
1419                        }
1420                        // Do the regular device lost handling
1421                        ret = NOT_HANDLED;
1422                        break;
1423                    }
1424                    // Do nothing
1425                    if (DBG) logd("Add device to lost list " + device);
1426                    mPeersLostDuringConnection.updateSupplicantDetails(device);
1427                    break;
1428                case WifiP2pManager.DISCOVER_PEERS:
1429                    /* Discovery will break negotiation */
1430                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
1431                            WifiP2pManager.BUSY);
1432                    break;
1433                case WifiP2pManager.CANCEL_CONNECT:
1434                    //Do a supplicant p2p_cancel which only cancels an ongoing
1435                    //group negotiation. This will fail for a pending provision
1436                    //discovery or for a pending user action, but at the framework
1437                    //level, we always treat cancel as succeeded and enter
1438                    //an inactive state
1439                    mWifiNative.p2pCancelConnect();
1440                    handleGroupCreationFailure();
1441                    transitionTo(mInactiveState);
1442                    replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
1443                    break;
1444                case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1445                    // We hit this scenario when NFC handover is invoked.
1446                    mAutonomousGroup = false;
1447                    transitionTo(mGroupNegotiationState);
1448                    break;
1449                default:
1450                    ret = NOT_HANDLED;
1451            }
1452            return ret;
1453        }
1454    }
1455
1456    class UserAuthorizingNegotiationRequestState extends State {
1457        @Override
1458        public void enter() {
1459            if (DBG) logd(getName());
1460            notifyInvitationReceived();
1461        }
1462
1463        @Override
1464        public boolean processMessage(Message message) {
1465            if (DBG) logd(getName() + message.toString());
1466            boolean ret = HANDLED;
1467            switch (message.what) {
1468                case PEER_CONNECTION_USER_ACCEPT:
1469                    mWifiNative.p2pStopFind();
1470                    p2pConnectWithPinDisplay(mSavedPeerConfig);
1471                    mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1472                    sendPeersChangedBroadcast();
1473                    transitionTo(mGroupNegotiationState);
1474                   break;
1475                case PEER_CONNECTION_USER_REJECT:
1476                    if (DBG) logd("User rejected negotiation " + mSavedPeerConfig);
1477                    transitionTo(mInactiveState);
1478                    break;
1479                default:
1480                    return NOT_HANDLED;
1481            }
1482            return ret;
1483        }
1484
1485        @Override
1486        public void exit() {
1487            //TODO: dismiss dialog if not already done
1488        }
1489    }
1490
1491    class UserAuthorizingInviteRequestState extends State {
1492        @Override
1493        public void enter() {
1494            if (DBG) logd(getName());
1495            notifyInvitationReceived();
1496        }
1497
1498        @Override
1499        public boolean processMessage(Message message) {
1500            if (DBG) logd(getName() + message.toString());
1501            boolean ret = HANDLED;
1502            switch (message.what) {
1503                case PEER_CONNECTION_USER_ACCEPT:
1504                    mWifiNative.p2pStopFind();
1505                    if (!reinvokePersistentGroup(mSavedPeerConfig)) {
1506                        // Do negotiation when persistence fails
1507                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1508                    }
1509                    mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1510                    sendPeersChangedBroadcast();
1511                    transitionTo(mGroupNegotiationState);
1512                   break;
1513                case PEER_CONNECTION_USER_REJECT:
1514                    if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
1515                    transitionTo(mInactiveState);
1516                    break;
1517                default:
1518                    return NOT_HANDLED;
1519            }
1520            return ret;
1521        }
1522
1523        @Override
1524        public void exit() {
1525            //TODO: dismiss dialog if not already done
1526        }
1527    }
1528
1529
1530
1531    class ProvisionDiscoveryState extends State {
1532        @Override
1533        public void enter() {
1534            if (DBG) logd(getName());
1535            mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
1536        }
1537
1538        @Override
1539        public boolean processMessage(Message message) {
1540            if (DBG) logd(getName() + message.toString());
1541            WifiP2pProvDiscEvent provDisc;
1542            WifiP2pDevice device;
1543            switch (message.what) {
1544                case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
1545                    provDisc = (WifiP2pProvDiscEvent) message.obj;
1546                    device = provDisc.device;
1547                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1548
1549                    if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
1550                        if (DBG) logd("Found a match " + mSavedPeerConfig);
1551                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1552                        transitionTo(mGroupNegotiationState);
1553                    }
1554                    break;
1555                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1556                    provDisc = (WifiP2pProvDiscEvent) message.obj;
1557                    device = provDisc.device;
1558                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1559
1560                    if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) {
1561                        if (DBG) logd("Found a match " + mSavedPeerConfig);
1562                        /* we already have the pin */
1563                        if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
1564                            p2pConnectWithPinDisplay(mSavedPeerConfig);
1565                            transitionTo(mGroupNegotiationState);
1566                        } else {
1567                            mJoinExistingGroup = false;
1568                            transitionTo(mUserAuthorizingNegotiationRequestState);
1569                        }
1570                    }
1571                    break;
1572                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1573                    provDisc = (WifiP2pProvDiscEvent) message.obj;
1574                    device = provDisc.device;
1575                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1576
1577                    if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
1578                        if (DBG) logd("Found a match " + mSavedPeerConfig);
1579                        mSavedPeerConfig.wps.pin = provDisc.pin;
1580                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1581                        notifyInvitationSent(provDisc.pin, device.deviceAddress);
1582                        transitionTo(mGroupNegotiationState);
1583                    }
1584                    break;
1585                case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
1586                    loge("provision discovery failed");
1587                    handleGroupCreationFailure();
1588                    transitionTo(mInactiveState);
1589                    break;
1590                default:
1591                    return NOT_HANDLED;
1592            }
1593            return HANDLED;
1594        }
1595    }
1596
1597    class GroupNegotiationState extends State {
1598        @Override
1599        public void enter() {
1600            if (DBG) logd(getName());
1601        }
1602
1603        @Override
1604        public boolean processMessage(Message message) {
1605            if (DBG) logd(getName() + message.toString());
1606            switch (message.what) {
1607                // We ignore these right now, since we get a GROUP_STARTED notification
1608                // afterwards
1609                case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1610                case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
1611                    if (DBG) logd(getName() + " go success");
1612                    break;
1613                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1614                    mGroup = (WifiP2pGroup) message.obj;
1615                    if (DBG) logd(getName() + " group started");
1616
1617                    if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
1618                        /*
1619                         * update cache information and set network id to mGroup.
1620                         */
1621                        updatePersistentNetworks(NO_RELOAD);
1622                        String devAddr = mGroup.getOwner().deviceAddress;
1623                        mGroup.setNetworkId(mGroups.getNetworkId(devAddr,
1624                                mGroup.getNetworkName()));
1625                    }
1626
1627                    if (mGroup.isGroupOwner()) {
1628                        /* Setting an idle time out on GO causes issues with certain scenarios
1629                         * on clients where it can be off-channel for longer and with the power
1630                         * save modes used.
1631                         *
1632                         * TODO: Verify multi-channel scenarios and supplicant behavior are
1633                         * better before adding a time out in future
1634                         */
1635                        //Set group idle timeout of 10 sec, to avoid GO beaconing incase of any
1636                        //failure during 4-way Handshake.
1637                        if (!mAutonomousGroup) {
1638                            mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
1639                        }
1640                        startDhcpServer(mGroup.getInterface());
1641                    } else {
1642                        mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
1643                        mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
1644                                P2pStateMachine.this, mGroup.getInterface());
1645                        // TODO: We should use DHCP state machine PRE message like WifiStateMachine
1646                        mWifiNative.setP2pPowerSave(mGroup.getInterface(), false);
1647                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
1648                        WifiP2pDevice groupOwner = mGroup.getOwner();
1649                        WifiP2pDevice peer = mPeers.get(groupOwner.deviceAddress);
1650                        if (peer != null) {
1651                            // update group owner details with peer details found at discovery
1652                            groupOwner.updateSupplicantDetails(peer);
1653                            mPeers.updateStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
1654                            sendPeersChangedBroadcast();
1655                        } else {
1656                            // A supplicant bug can lead to reporting an invalid
1657                            // group owner address (all zeroes) at times. Avoid a
1658                            // crash, but continue group creation since it is not
1659                            // essential.
1660                            logw("Unknown group owner " + groupOwner);
1661                        }
1662                    }
1663                    transitionTo(mGroupCreatedState);
1664                    break;
1665                case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
1666                    P2pStatus status = (P2pStatus) message.obj;
1667                    if (status == P2pStatus.NO_COMMON_CHANNEL) {
1668                        transitionTo(mFrequencyConflictState);
1669                        break;
1670                    }
1671                    /* continue with group removal handling */
1672                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1673                    if (DBG) logd(getName() + " go failure");
1674                    handleGroupCreationFailure();
1675                    transitionTo(mInactiveState);
1676                    break;
1677                // A group formation failure is always followed by
1678                // a group removed event. Flushing things at group formation
1679                // failure causes supplicant issues. Ignore right now.
1680                case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
1681                    status = (P2pStatus) message.obj;
1682                    if (status == P2pStatus.NO_COMMON_CHANNEL) {
1683                        transitionTo(mFrequencyConflictState);
1684                        break;
1685                    }
1686                    break;
1687                case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
1688                    status = (P2pStatus)message.obj;
1689                    if (status == P2pStatus.SUCCESS) {
1690                        // invocation was succeeded.
1691                        // wait P2P_GROUP_STARTED_EVENT.
1692                        break;
1693                    }
1694                    loge("Invitation result " + status);
1695                    if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
1696                        // target device has already removed the credential.
1697                        // So, remove this credential accordingly.
1698                        int netId = mSavedPeerConfig.netId;
1699                        if (netId >= 0) {
1700                            if (DBG) logd("Remove unknown client from the list");
1701                            removeClientFromList(netId, mSavedPeerConfig.deviceAddress, true);
1702                        }
1703
1704                        // Reinvocation has failed, try group negotiation
1705                        mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
1706                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1707                    } else if (status == P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE) {
1708
1709                        // Devices setting persistent_reconnect to 0 in wpa_supplicant
1710                        // always defer the invocation request and return
1711                        // "information is currently unable" error.
1712                        // So, try another way to connect for interoperability.
1713                        mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
1714                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1715                    } else if (status == P2pStatus.NO_COMMON_CHANNEL) {
1716                        transitionTo(mFrequencyConflictState);
1717                    } else {
1718                        handleGroupCreationFailure();
1719                        transitionTo(mInactiveState);
1720                    }
1721                    break;
1722                default:
1723                    return NOT_HANDLED;
1724            }
1725            return HANDLED;
1726        }
1727    }
1728
1729    class FrequencyConflictState extends State {
1730        private AlertDialog mFrequencyConflictDialog;
1731        @Override
1732        public void enter() {
1733            if (DBG) logd(getName());
1734            notifyFrequencyConflict();
1735        }
1736
1737        private void notifyFrequencyConflict() {
1738            logd("Notify frequency conflict");
1739            Resources r = Resources.getSystem();
1740
1741            AlertDialog dialog = new AlertDialog.Builder(mContext)
1742                .setMessage(r.getString(R.string.wifi_p2p_frequency_conflict_message,
1743                        getDeviceName(mSavedPeerConfig.deviceAddress)))
1744                .setPositiveButton(r.getString(R.string.dlg_ok), new OnClickListener() {
1745                        @Override
1746                        public void onClick(DialogInterface dialog, int which) {
1747                            sendMessage(DROP_WIFI_USER_ACCEPT);
1748                        }
1749                    })
1750                .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
1751                        @Override
1752                        public void onClick(DialogInterface dialog, int which) {
1753                            sendMessage(DROP_WIFI_USER_REJECT);
1754                        }
1755                    })
1756                .setOnCancelListener(new DialogInterface.OnCancelListener() {
1757                        @Override
1758                        public void onCancel(DialogInterface arg0) {
1759                            sendMessage(DROP_WIFI_USER_REJECT);
1760                        }
1761                    })
1762                .create();
1763
1764            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1765            dialog.show();
1766            mFrequencyConflictDialog = dialog;
1767        }
1768
1769        @Override
1770        public boolean processMessage(Message message) {
1771            if (DBG) logd(getName() + message.toString());
1772            switch (message.what) {
1773                case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1774                case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
1775                    loge(getName() + "group sucess during freq conflict!");
1776                    break;
1777                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1778                    loge(getName() + "group started after freq conflict, handle anyway");
1779                    deferMessage(message);
1780                    transitionTo(mGroupNegotiationState);
1781                    break;
1782                case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
1783                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1784                case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
1785                    // Ignore failures since we retry again
1786                    break;
1787                case DROP_WIFI_USER_REJECT:
1788                    // User rejected dropping wifi in favour of p2p
1789                    handleGroupCreationFailure();
1790                    transitionTo(mInactiveState);
1791                    break;
1792                case DROP_WIFI_USER_ACCEPT:
1793                    // User accepted dropping wifi in favour of p2p
1794                    mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 1);
1795                    mTemporarilyDisconnectedWifi = true;
1796                    break;
1797                case DISCONNECT_WIFI_RESPONSE:
1798                    // Got a response from wifistatemachine, retry p2p
1799                    if (DBG) logd(getName() + "Wifi disconnected, retry p2p");
1800                    transitionTo(mInactiveState);
1801                    sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
1802                    break;
1803                default:
1804                    return NOT_HANDLED;
1805            }
1806            return HANDLED;
1807        }
1808
1809        public void exit() {
1810            if (mFrequencyConflictDialog != null) mFrequencyConflictDialog.dismiss();
1811        }
1812    }
1813
1814    class GroupCreatedState extends State {
1815        @Override
1816        public void enter() {
1817            if (DBG) logd(getName());
1818            // Once connected, peer config details are invalid
1819            mSavedPeerConfig.invalidate();
1820            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
1821
1822            updateThisDevice(WifiP2pDevice.CONNECTED);
1823
1824            //DHCP server has already been started if I am a group owner
1825            if (mGroup.isGroupOwner()) {
1826                setWifiP2pInfoOnGroupFormation(NetworkUtils.numericToInetAddress(SERVER_ADDRESS));
1827            }
1828
1829            // In case of a negotiation group, connection changed is sent
1830            // after a client joins. For autonomous, send now
1831            if (mAutonomousGroup) {
1832                sendP2pConnectionChangedBroadcast();
1833            }
1834        }
1835
1836        @Override
1837        public boolean processMessage(Message message) {
1838            if (DBG) logd(getName() + message.toString());
1839            switch (message.what) {
1840                case WifiMonitor.AP_STA_CONNECTED_EVENT:
1841                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
1842                    String deviceAddress = device.deviceAddress;
1843                    // Clear timeout that was set when group was started.
1844                    mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
1845                    if (deviceAddress != null) {
1846                        if (mPeers.get(deviceAddress) != null) {
1847                            mGroup.addClient(mPeers.get(deviceAddress));
1848                        } else {
1849                            mGroup.addClient(deviceAddress);
1850                        }
1851                        mPeers.updateStatus(deviceAddress, WifiP2pDevice.CONNECTED);
1852                        if (DBG) logd(getName() + " ap sta connected");
1853                        sendPeersChangedBroadcast();
1854                    } else {
1855                        loge("Connect on null device address, ignore");
1856                    }
1857                    sendP2pConnectionChangedBroadcast();
1858                    break;
1859                case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
1860                    device = (WifiP2pDevice) message.obj;
1861                    deviceAddress = device.deviceAddress;
1862                    if (deviceAddress != null) {
1863                        mPeers.updateStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
1864                        if (mGroup.removeClient(deviceAddress)) {
1865                            if (DBG) logd("Removed client " + deviceAddress);
1866                            if (!mAutonomousGroup && mGroup.isClientListEmpty()) {
1867                                logd("Client list empty, remove non-persistent p2p group");
1868                                mWifiNative.p2pGroupRemove(mGroup.getInterface());
1869                                // We end up sending connection changed broadcast
1870                                // when this happens at exit()
1871                            } else {
1872                                // Notify when a client disconnects from group
1873                                sendP2pConnectionChangedBroadcast();
1874                            }
1875                        } else {
1876                            if (DBG) logd("Failed to remove client " + deviceAddress);
1877                            for (WifiP2pDevice c : mGroup.getClientList()) {
1878                                if (DBG) logd("client " + c.deviceAddress);
1879                            }
1880                        }
1881                        sendPeersChangedBroadcast();
1882                        if (DBG) logd(getName() + " ap sta disconnected");
1883                    } else {
1884                        loge("Disconnect on unknown device: " + device);
1885                    }
1886                    break;
1887                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
1888                    DhcpResults dhcpResults = (DhcpResults) message.obj;
1889                    if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS &&
1890                            dhcpResults != null) {
1891                        if (DBG) logd("DhcpResults: " + dhcpResults);
1892                        setWifiP2pInfoOnGroupFormation(dhcpResults.serverAddress);
1893                        sendP2pConnectionChangedBroadcast();
1894                        //Turn on power save on client
1895                        mWifiNative.setP2pPowerSave(mGroup.getInterface(), true);
1896                        try {
1897                            String iface = mGroup.getInterface();
1898                            mNwService.addInterfaceToLocalNetwork(iface,
1899                                                                  dhcpResults.getRoutes(iface));
1900                        } catch (RemoteException e) {
1901                            loge("Failed to add iface to local network " + e);
1902                        }
1903                    } else {
1904                        loge("DHCP failed");
1905                        mWifiNative.p2pGroupRemove(mGroup.getInterface());
1906                    }
1907                    break;
1908                case WifiP2pManager.REMOVE_GROUP:
1909                    if (DBG) logd(getName() + " remove group");
1910                    if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
1911                        transitionTo(mOngoingGroupRemovalState);
1912                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
1913                    } else {
1914                        handleGroupRemoved();
1915                        transitionTo(mInactiveState);
1916                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
1917                                WifiP2pManager.ERROR);
1918                    }
1919                    break;
1920                /* We do not listen to NETWORK_DISCONNECTION_EVENT for group removal
1921                 * handling since supplicant actually tries to reconnect after a temporary
1922                 * disconnect until group idle time out. Eventually, a group removal event
1923                 * will come when group has been removed.
1924                 *
1925                 * When there are connectivity issues during temporary disconnect, the application
1926                 * will also just remove the group.
1927                 *
1928                 * Treating network disconnection as group removal causes race conditions since
1929                 * supplicant would still maintain the group at that stage.
1930                 */
1931                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1932                    if (DBG) logd(getName() + " group removed");
1933                    handleGroupRemoved();
1934                    transitionTo(mInactiveState);
1935                    break;
1936                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
1937                    device = (WifiP2pDevice) message.obj;
1938                    //Device loss for a connected device indicates it is not in discovery any more
1939                    if (mGroup.contains(device)) {
1940                        if (DBG) logd("Add device to lost list " + device);
1941                        mPeersLostDuringConnection.updateSupplicantDetails(device);
1942                        return HANDLED;
1943                    }
1944                    // Do the regular device lost handling
1945                    return NOT_HANDLED;
1946                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
1947                    sendMessage(WifiP2pManager.REMOVE_GROUP);
1948                    deferMessage(message);
1949                    break;
1950                    // This allows any client to join the GO during the
1951                    // WPS window
1952                case WifiP2pManager.START_WPS:
1953                    WpsInfo wps = (WpsInfo) message.obj;
1954                    if (wps == null) {
1955                        replyToMessage(message, WifiP2pManager.START_WPS_FAILED);
1956                        break;
1957                    }
1958                    boolean ret = true;
1959                    if (wps.setup == WpsInfo.PBC) {
1960                        ret = mWifiNative.startWpsPbc(mGroup.getInterface(), null);
1961                    } else {
1962                        if (wps.pin == null) {
1963                            String pin = mWifiNative.startWpsPinDisplay(mGroup.getInterface());
1964                            try {
1965                                Integer.parseInt(pin);
1966                                notifyInvitationSent(pin, "any");
1967                            } catch (NumberFormatException ignore) {
1968                                ret = false;
1969                            }
1970                        } else {
1971                            ret = mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
1972                                    wps.pin);
1973                        }
1974                    }
1975                    replyToMessage(message, ret ? WifiP2pManager.START_WPS_SUCCEEDED :
1976                            WifiP2pManager.START_WPS_FAILED);
1977                    break;
1978                case WifiP2pManager.CONNECT:
1979                    WifiP2pConfig config = (WifiP2pConfig) message.obj;
1980                    if (isConfigInvalid(config)) {
1981                        loge("Dropping connect requeset " + config);
1982                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
1983                        break;
1984                    }
1985                    logd("Inviting device : " + config.deviceAddress);
1986                    mSavedPeerConfig = config;
1987                    if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
1988                        mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
1989                        sendPeersChangedBroadcast();
1990                        replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
1991                    } else {
1992                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
1993                                WifiP2pManager.ERROR);
1994                    }
1995                    // TODO: figure out updating the status to declined when invitation is rejected
1996                    break;
1997                case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
1998                    P2pStatus status = (P2pStatus)message.obj;
1999                    if (status == P2pStatus.SUCCESS) {
2000                        // invocation was succeeded.
2001                        break;
2002                    }
2003                    loge("Invitation result " + status);
2004                    if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
2005                        // target device has already removed the credential.
2006                        // So, remove this credential accordingly.
2007                        int netId = mGroup.getNetworkId();
2008                        if (netId >= 0) {
2009                            if (DBG) logd("Remove unknown client from the list");
2010                            if (!removeClientFromList(netId,
2011                                    mSavedPeerConfig.deviceAddress, false)) {
2012                                // not found the client on the list
2013                                loge("Already removed the client, ignore");
2014                                break;
2015                            }
2016                            // try invitation.
2017                            sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
2018                        }
2019                    }
2020                    break;
2021                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
2022                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
2023                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
2024                    WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
2025                    mSavedPeerConfig = new WifiP2pConfig();
2026                    mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
2027                    if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
2028                        mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
2029                    } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
2030                        mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
2031                        mSavedPeerConfig.wps.pin = provDisc.pin;
2032                    } else {
2033                        mSavedPeerConfig.wps.setup = WpsInfo.PBC;
2034                    }
2035                    transitionTo(mUserAuthorizingJoinState);
2036                    break;
2037                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
2038                    loge("Duplicate group creation event notice, ignore");
2039                    break;
2040                default:
2041                    return NOT_HANDLED;
2042            }
2043            return HANDLED;
2044        }
2045
2046        public void exit() {
2047            updateThisDevice(WifiP2pDevice.AVAILABLE);
2048            resetWifiP2pInfo();
2049            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
2050            sendP2pConnectionChangedBroadcast();
2051        }
2052    }
2053
2054    class UserAuthorizingJoinState extends State {
2055        @Override
2056        public void enter() {
2057            if (DBG) logd(getName());
2058            notifyInvitationReceived();
2059        }
2060
2061        @Override
2062        public boolean processMessage(Message message) {
2063            if (DBG) logd(getName() + message.toString());
2064            switch (message.what) {
2065                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
2066                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
2067                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
2068                    //Ignore more client requests
2069                    break;
2070                case PEER_CONNECTION_USER_ACCEPT:
2071                    //Stop discovery to avoid failure due to channel switch
2072                    mWifiNative.p2pStopFind();
2073                    if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
2074                        mWifiNative.startWpsPbc(mGroup.getInterface(), null);
2075                    } else {
2076                        mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
2077                                mSavedPeerConfig.wps.pin);
2078                    }
2079                    transitionTo(mGroupCreatedState);
2080                    break;
2081                case PEER_CONNECTION_USER_REJECT:
2082                    if (DBG) logd("User rejected incoming request");
2083                    transitionTo(mGroupCreatedState);
2084                    break;
2085                default:
2086                    return NOT_HANDLED;
2087            }
2088            return HANDLED;
2089        }
2090
2091        @Override
2092        public void exit() {
2093            //TODO: dismiss dialog if not already done
2094        }
2095    }
2096
2097    class OngoingGroupRemovalState extends State {
2098        @Override
2099        public void enter() {
2100            if (DBG) logd(getName());
2101        }
2102
2103        @Override
2104        public boolean processMessage(Message message) {
2105            if (DBG) logd(getName() + message.toString());
2106            switch (message.what) {
2107                // Group removal ongoing. Multiple calls
2108                // end up removing persisted network. Do nothing.
2109                case WifiP2pManager.REMOVE_GROUP:
2110                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
2111                    break;
2112                // Parent state will transition out of this state
2113                // when removal is complete
2114                default:
2115                    return NOT_HANDLED;
2116            }
2117            return HANDLED;
2118        }
2119    }
2120
2121    @Override
2122    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2123        super.dump(fd, pw, args);
2124        pw.println("mWifiP2pInfo " + mWifiP2pInfo);
2125        pw.println("mGroup " + mGroup);
2126        pw.println("mSavedPeerConfig " + mSavedPeerConfig);
2127        pw.println("mSavedP2pGroup " + mSavedP2pGroup);
2128        pw.println();
2129    }
2130
2131    private void sendP2pStateChangedBroadcast(boolean enabled) {
2132        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
2133        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2134        if (enabled) {
2135            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
2136                    WifiP2pManager.WIFI_P2P_STATE_ENABLED);
2137        } else {
2138            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
2139                    WifiP2pManager.WIFI_P2P_STATE_DISABLED);
2140        }
2141        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2142    }
2143
2144    private void sendP2pDiscoveryChangedBroadcast(boolean started) {
2145        if (mDiscoveryStarted == started) return;
2146        mDiscoveryStarted = started;
2147
2148        if (DBG) logd("discovery change broadcast " + started);
2149
2150        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
2151        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2152        intent.putExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, started ?
2153                WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED :
2154                WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
2155        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2156    }
2157
2158    private void sendThisDeviceChangedBroadcast() {
2159        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
2160        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2161        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
2162        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2163    }
2164
2165    private void sendPeersChangedBroadcast() {
2166        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
2167        intent.putExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, new WifiP2pDeviceList(mPeers));
2168        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2169        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2170    }
2171
2172    private void sendP2pConnectionChangedBroadcast() {
2173        if (DBG) logd("sending p2p connection changed broadcast");
2174        Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
2175        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2176                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
2177        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
2178        intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
2179        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, new WifiP2pGroup(mGroup));
2180        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2181        mWifiChannel.sendMessage(WifiP2pServiceImpl.P2P_CONNECTION_CHANGED,
2182                new NetworkInfo(mNetworkInfo));
2183    }
2184
2185    private void sendP2pPersistentGroupsChangedBroadcast() {
2186        if (DBG) logd("sending p2p persistent groups changed broadcast");
2187        Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
2188        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2189        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2190    }
2191
2192    private void startDhcpServer(String intf) {
2193        InterfaceConfiguration ifcg = null;
2194        try {
2195            ifcg = mNwService.getInterfaceConfig(intf);
2196            ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(
2197                        SERVER_ADDRESS), 24));
2198            ifcg.setInterfaceUp();
2199            mNwService.setInterfaceConfig(intf, ifcg);
2200            /* This starts the dnsmasq server */
2201            ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
2202                    Context.CONNECTIVITY_SERVICE);
2203            String[] tetheringDhcpRanges = cm.getTetheredDhcpRanges();
2204            if (mNwService.isTetheringStarted()) {
2205                if (DBG) logd("Stop existing tethering and restart it");
2206                mNwService.stopTethering();
2207            }
2208            mNwService.tetherInterface(intf);
2209            mNwService.startTethering(tetheringDhcpRanges);
2210        } catch (Exception e) {
2211            loge("Error configuring interface " + intf + ", :" + e);
2212            return;
2213        }
2214
2215        logd("Started Dhcp server on " + intf);
2216   }
2217
2218    private void stopDhcpServer(String intf) {
2219        try {
2220            mNwService.untetherInterface(intf);
2221            for (String temp : mNwService.listTetheredInterfaces()) {
2222                logd("List all interfaces " + temp);
2223                if (temp.compareTo(intf) != 0) {
2224                    logd("Found other tethering interfaces, so keep tethering alive");
2225                    return;
2226                }
2227            }
2228            mNwService.stopTethering();
2229        } catch (Exception e) {
2230            loge("Error stopping Dhcp server" + e);
2231            return;
2232        } finally {
2233            logd("Stopped Dhcp server");
2234        }
2235    }
2236
2237    private void notifyP2pEnableFailure() {
2238        Resources r = Resources.getSystem();
2239        AlertDialog dialog = new AlertDialog.Builder(mContext)
2240            .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
2241            .setMessage(r.getString(R.string.wifi_p2p_failed_message))
2242            .setPositiveButton(r.getString(R.string.ok), null)
2243            .create();
2244        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
2245        dialog.show();
2246    }
2247
2248    private void addRowToDialog(ViewGroup group, int stringId, String value) {
2249        Resources r = Resources.getSystem();
2250        View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row,
2251                group, false);
2252        ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId));
2253        ((TextView) row.findViewById(R.id.value)).setText(value);
2254        group.addView(row);
2255    }
2256
2257    private void notifyInvitationSent(String pin, String peerAddress) {
2258        Resources r = Resources.getSystem();
2259
2260        final View textEntryView = LayoutInflater.from(mContext)
2261                .inflate(R.layout.wifi_p2p_dialog, null);
2262
2263        ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
2264        addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress));
2265        addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin);
2266
2267        AlertDialog dialog = new AlertDialog.Builder(mContext)
2268            .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title))
2269            .setView(textEntryView)
2270            .setPositiveButton(r.getString(R.string.ok), null)
2271            .create();
2272        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
2273        dialog.show();
2274    }
2275
2276    private void notifyInvitationReceived() {
2277        Resources r = Resources.getSystem();
2278        final WpsInfo wps = mSavedPeerConfig.wps;
2279        final View textEntryView = LayoutInflater.from(mContext)
2280                .inflate(R.layout.wifi_p2p_dialog, null);
2281
2282        ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
2283        addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName(
2284                mSavedPeerConfig.deviceAddress));
2285
2286        final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin);
2287
2288        AlertDialog dialog = new AlertDialog.Builder(mContext)
2289            .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title))
2290            .setView(textEntryView)
2291            .setPositiveButton(r.getString(R.string.accept), new OnClickListener() {
2292                        public void onClick(DialogInterface dialog, int which) {
2293                            if (wps.setup == WpsInfo.KEYPAD) {
2294                                mSavedPeerConfig.wps.pin = pin.getText().toString();
2295                            }
2296                            if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig);
2297                            sendMessage(PEER_CONNECTION_USER_ACCEPT);
2298                        }
2299                    })
2300            .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
2301                        @Override
2302                        public void onClick(DialogInterface dialog, int which) {
2303                            if (DBG) logd(getName() + " ignore connect");
2304                            sendMessage(PEER_CONNECTION_USER_REJECT);
2305                        }
2306                    })
2307            .setOnCancelListener(new DialogInterface.OnCancelListener() {
2308                        @Override
2309                        public void onCancel(DialogInterface arg0) {
2310                            if (DBG) logd(getName() + " ignore connect");
2311                            sendMessage(PEER_CONNECTION_USER_REJECT);
2312                        }
2313                    })
2314            .create();
2315
2316        //make the enter pin area or the display pin area visible
2317        switch (wps.setup) {
2318            case WpsInfo.KEYPAD:
2319                if (DBG) logd("Enter pin section visible");
2320                textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE);
2321                break;
2322            case WpsInfo.DISPLAY:
2323                if (DBG) logd("Shown pin section visible");
2324                addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin);
2325                break;
2326            default:
2327                break;
2328        }
2329
2330        if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_APPLIANCE) ==
2331                Configuration.UI_MODE_TYPE_APPLIANCE) {
2332            // For appliance devices, add a key listener which accepts.
2333            dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
2334
2335                @Override
2336                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
2337                    // TODO: make the actual key come from a config value.
2338                    if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
2339                        sendMessage(PEER_CONNECTION_USER_ACCEPT);
2340                        dialog.dismiss();
2341                        return true;
2342                    }
2343                    return false;
2344                }
2345            });
2346            // TODO: add timeout for this dialog.
2347            // TODO: update UI in appliance mode to tell user what to do.
2348        }
2349
2350        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
2351        dialog.show();
2352    }
2353
2354    /**
2355     * Synchronize the persistent group list between
2356     * wpa_supplicant and mGroups.
2357     */
2358    private void updatePersistentNetworks(boolean reload) {
2359        String listStr = mWifiNative.listNetworks();
2360        if (listStr == null) return;
2361
2362        boolean isSaveRequired = false;
2363        String[] lines = listStr.split("\n");
2364        if (lines == null) return;
2365
2366        if (reload) mGroups.clear();
2367
2368        // Skip the first line, which is a header
2369        for (int i = 1; i < lines.length; i++) {
2370            String[] result = lines[i].split("\t");
2371            if (result == null || result.length < 4) {
2372                continue;
2373            }
2374            // network-id | ssid | bssid | flags
2375            int netId = -1;
2376            String ssid = result[1];
2377            String bssid = result[2];
2378            String flags = result[3];
2379            try {
2380                netId = Integer.parseInt(result[0]);
2381            } catch(NumberFormatException e) {
2382                e.printStackTrace();
2383                continue;
2384            }
2385
2386            if (flags.indexOf("[CURRENT]") != -1) {
2387                continue;
2388            }
2389            if (flags.indexOf("[P2P-PERSISTENT]") == -1) {
2390                /*
2391                 * The unused profile is sometimes remained when the p2p group formation is failed.
2392                 * So, we clean up the p2p group here.
2393                 */
2394                if (DBG) logd("clean up the unused persistent group. netId=" + netId);
2395                mWifiNative.removeNetwork(netId);
2396                isSaveRequired = true;
2397                continue;
2398            }
2399
2400            if (mGroups.contains(netId)) {
2401                continue;
2402            }
2403
2404            WifiP2pGroup group = new WifiP2pGroup();
2405            group.setNetworkId(netId);
2406            group.setNetworkName(ssid);
2407            String mode = mWifiNative.getNetworkVariable(netId, "mode");
2408            if (mode != null && mode.equals("3")) {
2409                group.setIsGroupOwner(true);
2410            }
2411            if (bssid.equalsIgnoreCase(mThisDevice.deviceAddress)) {
2412                group.setOwner(mThisDevice);
2413            } else {
2414                WifiP2pDevice device = new WifiP2pDevice();
2415                device.deviceAddress = bssid;
2416                group.setOwner(device);
2417            }
2418            mGroups.add(group);
2419            isSaveRequired = true;
2420        }
2421
2422        if (reload || isSaveRequired) {
2423            mWifiNative.saveConfig();
2424            sendP2pPersistentGroupsChangedBroadcast();
2425        }
2426    }
2427
2428    /**
2429     * A config is valid if it has a peer address that has already been
2430     * discovered
2431     * @return true if it is invalid, false otherwise
2432     */
2433    private boolean isConfigInvalid(WifiP2pConfig config) {
2434        if (config == null) return true;
2435        if (TextUtils.isEmpty(config.deviceAddress)) return true;
2436        if (mPeers.get(config.deviceAddress) == null) return true;
2437        return false;
2438    }
2439
2440    /* TODO: The supplicant does not provide group capability changes as an event.
2441     * Having it pushed as an event would avoid polling for this information right
2442     * before a connection
2443     */
2444    private WifiP2pDevice fetchCurrentDeviceDetails(WifiP2pConfig config) {
2445        /* Fetch & update group capability from supplicant on the device */
2446        int gc = mWifiNative.getGroupCapability(config.deviceAddress);
2447        mPeers.updateGroupCapability(config.deviceAddress, gc);
2448        return mPeers.get(config.deviceAddress);
2449    }
2450
2451    /**
2452     * Start a p2p group negotiation and display pin if necessary
2453     * @param config for the peer
2454     */
2455    private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
2456        WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
2457
2458        String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
2459        try {
2460            Integer.parseInt(pin);
2461            notifyInvitationSent(pin, config.deviceAddress);
2462        } catch (NumberFormatException ignore) {
2463            // do nothing if p2pConnect did not return a pin
2464        }
2465    }
2466
2467    /**
2468     * Reinvoke a persistent group.
2469     *
2470     * @param config for the peer
2471     * @return true on success, false on failure
2472     */
2473    private boolean reinvokePersistentGroup(WifiP2pConfig config) {
2474        WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
2475
2476        boolean join = dev.isGroupOwner();
2477        String ssid = mWifiNative.p2pGetSsid(dev.deviceAddress);
2478        if (DBG) logd("target ssid is " + ssid + " join:" + join);
2479
2480        if (join && dev.isGroupLimit()) {
2481            if (DBG) logd("target device reaches group limit.");
2482
2483            // if the target group has reached the limit,
2484            // try group formation.
2485            join = false;
2486        } else if (join) {
2487            int netId = mGroups.getNetworkId(dev.deviceAddress, ssid);
2488            if (netId >= 0) {
2489                // Skip WPS and start 4way handshake immediately.
2490                if (!mWifiNative.p2pGroupAdd(netId)) {
2491                    return false;
2492                }
2493                return true;
2494            }
2495        }
2496
2497        if (!join && dev.isDeviceLimit()) {
2498            loge("target device reaches the device limit.");
2499            return false;
2500        }
2501
2502        if (!join && dev.isInvitationCapable()) {
2503            int netId = WifiP2pGroup.PERSISTENT_NET_ID;
2504            if (config.netId >= 0) {
2505                if (config.deviceAddress.equals(mGroups.getOwnerAddr(config.netId))) {
2506                    netId = config.netId;
2507                }
2508            } else {
2509                netId = mGroups.getNetworkId(dev.deviceAddress);
2510            }
2511            if (netId < 0) {
2512                netId = getNetworkIdFromClientList(dev.deviceAddress);
2513            }
2514            if (DBG) logd("netId related with " + dev.deviceAddress + " = " + netId);
2515            if (netId >= 0) {
2516                // Invoke the persistent group.
2517                if (mWifiNative.p2pReinvoke(netId, dev.deviceAddress)) {
2518                    // Save network id. It'll be used when an invitation result event is received.
2519                    config.netId = netId;
2520                    return true;
2521                } else {
2522                    loge("p2pReinvoke() failed, update networks");
2523                    updatePersistentNetworks(RELOAD);
2524                    return false;
2525                }
2526            }
2527        }
2528
2529        return false;
2530    }
2531
2532    /**
2533     * Return the network id of the group owner profile which has the p2p client with
2534     * the specified device address in it's client list.
2535     * If more than one persistent group of the same address is present in its client
2536     * lists, return the first one.
2537     *
2538     * @param deviceAddress p2p device address.
2539     * @return the network id. if not found, return -1.
2540     */
2541    private int getNetworkIdFromClientList(String deviceAddress) {
2542        if (deviceAddress == null) return -1;
2543
2544        Collection<WifiP2pGroup> groups = mGroups.getGroupList();
2545        for (WifiP2pGroup group : groups) {
2546            int netId = group.getNetworkId();
2547            String[] p2pClientList = getClientList(netId);
2548            if (p2pClientList == null) continue;
2549            for (String client : p2pClientList) {
2550                if (deviceAddress.equalsIgnoreCase(client)) {
2551                    return netId;
2552                }
2553            }
2554        }
2555        return -1;
2556    }
2557
2558    /**
2559     * Return p2p client list associated with the specified network id.
2560     * @param netId network id.
2561     * @return p2p client list. if not found, return null.
2562     */
2563    private String[] getClientList(int netId) {
2564        String p2pClients = mWifiNative.getNetworkVariable(netId, "p2p_client_list");
2565        if (p2pClients == null) {
2566            return null;
2567        }
2568        return p2pClients.split(" ");
2569    }
2570
2571    /**
2572     * Remove the specified p2p client from the specified profile.
2573     * @param netId network id of the profile.
2574     * @param addr p2p client address to be removed.
2575     * @param isRemovable if true, remove the specified profile if its client list becomes empty.
2576     * @return whether removing the specified p2p client is successful or not.
2577     */
2578    private boolean removeClientFromList(int netId, String addr, boolean isRemovable) {
2579        StringBuilder modifiedClientList =  new StringBuilder();
2580        String[] currentClientList = getClientList(netId);
2581        boolean isClientRemoved = false;
2582        if (currentClientList != null) {
2583            for (String client : currentClientList) {
2584                if (!client.equalsIgnoreCase(addr)) {
2585                    modifiedClientList.append(" ");
2586                    modifiedClientList.append(client);
2587                } else {
2588                    isClientRemoved = true;
2589                }
2590            }
2591        }
2592        if (modifiedClientList.length() == 0 && isRemovable) {
2593            // the client list is empty. so remove it.
2594            if (DBG) logd("Remove unknown network");
2595            mGroups.remove(netId);
2596            return true;
2597        }
2598
2599        if (!isClientRemoved) {
2600            // specified p2p client is not found. already removed.
2601            return false;
2602        }
2603
2604        if (DBG) logd("Modified client list: " + modifiedClientList);
2605        if (modifiedClientList.length() == 0) {
2606            modifiedClientList.append("\"\"");
2607        }
2608        mWifiNative.setNetworkVariable(netId,
2609                "p2p_client_list", modifiedClientList.toString());
2610        mWifiNative.saveConfig();
2611        return true;
2612    }
2613
2614    private void setWifiP2pInfoOnGroupFormation(InetAddress serverInetAddress) {
2615        mWifiP2pInfo.groupFormed = true;
2616        mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
2617        mWifiP2pInfo.groupOwnerAddress = serverInetAddress;
2618    }
2619
2620    private void resetWifiP2pInfo() {
2621        mWifiP2pInfo.groupFormed = false;
2622        mWifiP2pInfo.isGroupOwner = false;
2623        mWifiP2pInfo.groupOwnerAddress = null;
2624    }
2625
2626    private String getDeviceName(String deviceAddress) {
2627        WifiP2pDevice d = mPeers.get(deviceAddress);
2628        if (d != null) {
2629                return d.deviceName;
2630        }
2631        //Treat the address as name if there is no match
2632        return deviceAddress;
2633    }
2634
2635    private String getPersistedDeviceName() {
2636        String deviceName = Settings.Global.getString(mContext.getContentResolver(),
2637                Settings.Global.WIFI_P2P_DEVICE_NAME);
2638        if (deviceName == null) {
2639            /* We use the 4 digits of the ANDROID_ID to have a friendly
2640             * default that has low likelihood of collision with a peer */
2641            String id = Settings.Secure.getString(mContext.getContentResolver(),
2642                    Settings.Secure.ANDROID_ID);
2643            return "Android_" + id.substring(0,4);
2644        }
2645        return deviceName;
2646    }
2647
2648    private boolean setAndPersistDeviceName(String devName) {
2649        if (devName == null) return false;
2650
2651        if (!mWifiNative.setDeviceName(devName)) {
2652            loge("Failed to set device name " + devName);
2653            return false;
2654        }
2655
2656        mThisDevice.deviceName = devName;
2657        mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
2658
2659        Settings.Global.putString(mContext.getContentResolver(),
2660                Settings.Global.WIFI_P2P_DEVICE_NAME, devName);
2661        sendThisDeviceChangedBroadcast();
2662        return true;
2663    }
2664
2665    private boolean setWfdInfo(WifiP2pWfdInfo wfdInfo) {
2666        boolean success;
2667
2668        if (!wfdInfo.isWfdEnabled()) {
2669            success = mWifiNative.setWfdEnable(false);
2670        } else {
2671            success =
2672                mWifiNative.setWfdEnable(true)
2673                && mWifiNative.setWfdDeviceInfo(wfdInfo.getDeviceInfoHex());
2674        }
2675
2676        if (!success) {
2677            loge("Failed to set wfd properties");
2678            return false;
2679        }
2680
2681        mThisDevice.wfdInfo = wfdInfo;
2682        sendThisDeviceChangedBroadcast();
2683        return true;
2684    }
2685
2686    private void initializeP2pSettings() {
2687        mWifiNative.setPersistentReconnect(true);
2688        mThisDevice.deviceName = getPersistedDeviceName();
2689        mWifiNative.setDeviceName(mThisDevice.deviceName);
2690        // DIRECT-XY-DEVICENAME (XY is randomly generated)
2691        mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
2692        mWifiNative.setDeviceType(mThisDevice.primaryDeviceType);
2693        // Supplicant defaults to using virtual display with display
2694        // which refers to a remote display. Use physical_display
2695        mWifiNative.setConfigMethods("virtual_push_button physical_display keypad");
2696        // STA has higher priority over P2P
2697        mWifiNative.setConcurrencyPriority("sta");
2698
2699        mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
2700        updateThisDevice(WifiP2pDevice.AVAILABLE);
2701        if (DBG) logd("DeviceAddress: " + mThisDevice.deviceAddress);
2702
2703        mClientInfoList.clear();
2704        mWifiNative.p2pFlush();
2705        mWifiNative.p2pServiceFlush();
2706        mServiceTransactionId = 0;
2707        mServiceDiscReqId = null;
2708
2709        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
2710                Settings.Global.WIFI_COUNTRY_CODE);
2711        if (countryCode != null && !countryCode.isEmpty()) {
2712            mP2pStateMachine.sendMessage(SET_COUNTRY_CODE, countryCode);
2713        }
2714
2715        updatePersistentNetworks(RELOAD);
2716    }
2717
2718    private void updateThisDevice(int status) {
2719        mThisDevice.status = status;
2720        sendThisDeviceChangedBroadcast();
2721    }
2722
2723    private void handleGroupCreationFailure() {
2724        resetWifiP2pInfo();
2725        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.FAILED, null, null);
2726        sendP2pConnectionChangedBroadcast();
2727
2728        // Remove only the peer we failed to connect to so that other devices discovered
2729        // that have not timed out still remain in list for connection
2730        boolean peersChanged = mPeers.remove(mPeersLostDuringConnection);
2731        if (mSavedPeerConfig.deviceAddress != null &&
2732                mPeers.remove(mSavedPeerConfig.deviceAddress) != null) {
2733            peersChanged = true;
2734        }
2735        if (peersChanged) {
2736            sendPeersChangedBroadcast();
2737        }
2738
2739        mPeersLostDuringConnection.clear();
2740        mServiceDiscReqId = null;
2741        sendMessage(WifiP2pManager.DISCOVER_PEERS);
2742    }
2743
2744    private void handleGroupRemoved() {
2745        if (mGroup.isGroupOwner()) {
2746            stopDhcpServer(mGroup.getInterface());
2747        } else {
2748            if (DBG) logd("stop DHCP client");
2749            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
2750            mDhcpStateMachine.doQuit();
2751            mDhcpStateMachine = null;
2752            try {
2753                mNwService.removeInterfaceFromLocalNetwork(mGroup.getInterface());
2754            } catch (RemoteException e) {
2755                loge("Failed to remove iface from local network " + e);
2756            }
2757        }
2758
2759        try {
2760            mNwService.clearInterfaceAddresses(mGroup.getInterface());
2761        } catch (Exception e) {
2762            loge("Failed to clear addresses " + e);
2763        }
2764        NetworkUtils.resetConnections(mGroup.getInterface(), NetworkUtils.RESET_ALL_ADDRESSES);
2765
2766        // Clear any timeout that was set. This is essential for devices
2767        // that reuse the main p2p interface for a created group.
2768        mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
2769
2770        boolean peersChanged = false;
2771        // Remove only peers part of the group, so that other devices discovered
2772        // that have not timed out still remain in list for connection
2773        for (WifiP2pDevice d : mGroup.getClientList()) {
2774            if (mPeers.remove(d)) peersChanged = true;
2775        }
2776        if (mPeers.remove(mGroup.getOwner())) peersChanged = true;
2777        if (mPeers.remove(mPeersLostDuringConnection)) peersChanged = true;
2778        if (peersChanged) {
2779            sendPeersChangedBroadcast();
2780        }
2781
2782        mGroup = null;
2783        mPeersLostDuringConnection.clear();
2784        mServiceDiscReqId = null;
2785
2786        if (mTemporarilyDisconnectedWifi) {
2787            mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 0);
2788            mTemporarilyDisconnectedWifi = false;
2789        }
2790   }
2791
2792    //State machine initiated requests can have replyTo set to null indicating
2793    //there are no recipients, we ignore those reply actions
2794    private void replyToMessage(Message msg, int what) {
2795        if (msg.replyTo == null) return;
2796        Message dstMsg = obtainMessage(msg);
2797        dstMsg.what = what;
2798        mReplyChannel.replyToMessage(msg, dstMsg);
2799    }
2800
2801    private void replyToMessage(Message msg, int what, int arg1) {
2802        if (msg.replyTo == null) return;
2803        Message dstMsg = obtainMessage(msg);
2804        dstMsg.what = what;
2805        dstMsg.arg1 = arg1;
2806        mReplyChannel.replyToMessage(msg, dstMsg);
2807    }
2808
2809    private void replyToMessage(Message msg, int what, Object obj) {
2810        if (msg.replyTo == null) return;
2811        Message dstMsg = obtainMessage(msg);
2812        dstMsg.what = what;
2813        dstMsg.obj = obj;
2814        mReplyChannel.replyToMessage(msg, dstMsg);
2815    }
2816
2817    /* arg2 on the source message has a hash code that needs to be retained in replies
2818     * see WifiP2pManager for details */
2819    private Message obtainMessage(Message srcMsg) {
2820        Message msg = Message.obtain();
2821        msg.arg2 = srcMsg.arg2;
2822        return msg;
2823    }
2824
2825    @Override
2826    protected void logd(String s) {
2827        Slog.d(TAG, s);
2828    }
2829
2830    @Override
2831    protected void loge(String s) {
2832        Slog.e(TAG, s);
2833    }
2834
2835    /**
2836     * Update service discovery request to wpa_supplicant.
2837     */
2838    private boolean updateSupplicantServiceRequest() {
2839        clearSupplicantServiceRequest();
2840
2841        StringBuffer sb = new StringBuffer();
2842        for (ClientInfo c: mClientInfoList.values()) {
2843            int key;
2844            WifiP2pServiceRequest req;
2845            for (int i=0; i < c.mReqList.size(); i++) {
2846                req = c.mReqList.valueAt(i);
2847                if (req != null) {
2848                    sb.append(req.getSupplicantQuery());
2849                }
2850            }
2851        }
2852
2853        if (sb.length() == 0) {
2854            return false;
2855        }
2856
2857        mServiceDiscReqId = mWifiNative.p2pServDiscReq("00:00:00:00:00:00", sb.toString());
2858        if (mServiceDiscReqId == null) {
2859            return false;
2860        }
2861        return true;
2862    }
2863
2864    /**
2865     * Clear service discovery request in wpa_supplicant
2866     */
2867    private void clearSupplicantServiceRequest() {
2868        if (mServiceDiscReqId == null) return;
2869
2870        mWifiNative.p2pServDiscCancelReq(mServiceDiscReqId);
2871        mServiceDiscReqId = null;
2872    }
2873
2874    /* TODO: We could track individual service adds separately and avoid
2875     * having to do update all service requests on every new request
2876     */
2877    private boolean addServiceRequest(Messenger m, WifiP2pServiceRequest req) {
2878        clearClientDeadChannels();
2879        ClientInfo clientInfo = getClientInfo(m, true);
2880        if (clientInfo == null) {
2881            return false;
2882        }
2883
2884        ++mServiceTransactionId;
2885        //The Wi-Fi p2p spec says transaction id should be non-zero
2886        if (mServiceTransactionId == 0) ++mServiceTransactionId;
2887        req.setTransactionId(mServiceTransactionId);
2888        clientInfo.mReqList.put(mServiceTransactionId, req);
2889
2890        if (mServiceDiscReqId == null) {
2891            return true;
2892        }
2893
2894        return updateSupplicantServiceRequest();
2895    }
2896
2897    private void removeServiceRequest(Messenger m, WifiP2pServiceRequest req) {
2898        ClientInfo clientInfo = getClientInfo(m, false);
2899        if (clientInfo == null) {
2900            return;
2901        }
2902
2903        //Application does not have transaction id information
2904        //go through stored requests to remove
2905        boolean removed = false;
2906        for (int i=0; i<clientInfo.mReqList.size(); i++) {
2907            if (req.equals(clientInfo.mReqList.valueAt(i))) {
2908                removed = true;
2909                clientInfo.mReqList.removeAt(i);
2910                break;
2911            }
2912        }
2913
2914        if (!removed) return;
2915
2916        if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
2917            if (DBG) logd("remove client information from framework");
2918            mClientInfoList.remove(clientInfo.mMessenger);
2919        }
2920
2921        if (mServiceDiscReqId == null) {
2922            return;
2923        }
2924
2925        updateSupplicantServiceRequest();
2926    }
2927
2928    private void clearServiceRequests(Messenger m) {
2929
2930        ClientInfo clientInfo = getClientInfo(m, false);
2931        if (clientInfo == null) {
2932            return;
2933        }
2934
2935        if (clientInfo.mReqList.size() == 0) {
2936            return;
2937        }
2938
2939        clientInfo.mReqList.clear();
2940
2941        if (clientInfo.mServList.size() == 0) {
2942            if (DBG) logd("remove channel information from framework");
2943            mClientInfoList.remove(clientInfo.mMessenger);
2944        }
2945
2946        if (mServiceDiscReqId == null) {
2947            return;
2948        }
2949
2950        updateSupplicantServiceRequest();
2951    }
2952
2953    private boolean addLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
2954        clearClientDeadChannels();
2955        ClientInfo clientInfo = getClientInfo(m, true);
2956        if (clientInfo == null) {
2957            return false;
2958        }
2959
2960        if (!clientInfo.mServList.add(servInfo)) {
2961            return false;
2962        }
2963
2964        if (!mWifiNative.p2pServiceAdd(servInfo)) {
2965            clientInfo.mServList.remove(servInfo);
2966            return false;
2967        }
2968
2969        return true;
2970    }
2971
2972    private void removeLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
2973        ClientInfo clientInfo = getClientInfo(m, false);
2974        if (clientInfo == null) {
2975            return;
2976        }
2977
2978        mWifiNative.p2pServiceDel(servInfo);
2979
2980        clientInfo.mServList.remove(servInfo);
2981        if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
2982            if (DBG) logd("remove client information from framework");
2983            mClientInfoList.remove(clientInfo.mMessenger);
2984        }
2985    }
2986
2987    private void clearLocalServices(Messenger m) {
2988        ClientInfo clientInfo = getClientInfo(m, false);
2989        if (clientInfo == null) {
2990            return;
2991        }
2992
2993        for (WifiP2pServiceInfo servInfo: clientInfo.mServList) {
2994            mWifiNative.p2pServiceDel(servInfo);
2995        }
2996
2997        clientInfo.mServList.clear();
2998        if (clientInfo.mReqList.size() == 0) {
2999            if (DBG) logd("remove client information from framework");
3000            mClientInfoList.remove(clientInfo.mMessenger);
3001        }
3002    }
3003
3004    private void clearClientInfo(Messenger m) {
3005        clearLocalServices(m);
3006        clearServiceRequests(m);
3007    }
3008
3009    /**
3010     * Send the service response to the WifiP2pManager.Channel.
3011     *
3012     * @param resp
3013     */
3014    private void sendServiceResponse(WifiP2pServiceResponse resp) {
3015        for (ClientInfo c : mClientInfoList.values()) {
3016            WifiP2pServiceRequest req = c.mReqList.get(resp.getTransactionId());
3017            if (req != null) {
3018                Message msg = Message.obtain();
3019                msg.what = WifiP2pManager.RESPONSE_SERVICE;
3020                msg.arg1 = 0;
3021                msg.arg2 = 0;
3022                msg.obj = resp;
3023                try {
3024                    c.mMessenger.send(msg);
3025                } catch (RemoteException e) {
3026                    if (DBG) logd("detect dead channel");
3027                    clearClientInfo(c.mMessenger);
3028                    return;
3029                }
3030            }
3031        }
3032    }
3033
3034    /**
3035     * We dont get notifications of clients that have gone away.
3036     * We detect this actively when services are added and throw
3037     * them away.
3038     *
3039     * TODO: This can be done better with full async channels.
3040     */
3041    private void clearClientDeadChannels() {
3042        ArrayList<Messenger> deadClients = new ArrayList<Messenger>();
3043
3044        for (ClientInfo c : mClientInfoList.values()) {
3045            Message msg = Message.obtain();
3046            msg.what = WifiP2pManager.PING;
3047            msg.arg1 = 0;
3048            msg.arg2 = 0;
3049            msg.obj = null;
3050            try {
3051                c.mMessenger.send(msg);
3052            } catch (RemoteException e) {
3053                if (DBG) logd("detect dead channel");
3054                deadClients.add(c.mMessenger);
3055            }
3056        }
3057
3058        for (Messenger m : deadClients) {
3059            clearClientInfo(m);
3060        }
3061    }
3062
3063    /**
3064     * Return the specified ClientInfo.
3065     * @param m Messenger
3066     * @param createIfNotExist if true and the specified channel info does not exist,
3067     * create new client info.
3068     * @return the specified ClientInfo.
3069     */
3070    private ClientInfo getClientInfo(Messenger m, boolean createIfNotExist) {
3071        ClientInfo clientInfo = mClientInfoList.get(m);
3072
3073        if (clientInfo == null && createIfNotExist) {
3074            if (DBG) logd("add a new client");
3075            clientInfo = new ClientInfo(m);
3076            mClientInfoList.put(m, clientInfo);
3077        }
3078
3079        return clientInfo;
3080    }
3081
3082    }
3083
3084    /**
3085     * Information about a particular client and we track the service discovery requests
3086     * and the local services registered by the client.
3087     */
3088    private class ClientInfo {
3089
3090        /*
3091         * A reference to WifiP2pManager.Channel handler.
3092         * The response of this request is notified to WifiP2pManager.Channel handler
3093         */
3094        private Messenger mMessenger;
3095
3096        /*
3097         * A service discovery request list.
3098         */
3099        private SparseArray<WifiP2pServiceRequest> mReqList;
3100
3101        /*
3102         * A local service information list.
3103         */
3104        private List<WifiP2pServiceInfo> mServList;
3105
3106        private ClientInfo(Messenger m) {
3107            mMessenger = m;
3108            mReqList = new SparseArray();
3109            mServList = new ArrayList<WifiP2pServiceInfo>();
3110        }
3111    }
3112}
3113