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