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