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