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