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