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