WifiP2pServiceImpl.java revision 97a856eb1fda1585316055bd4912f0000deccb52
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi.p2p;
18
19import android.app.AlertDialog;
20import android.app.Notification;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.DialogInterface.OnClickListener;
24import android.content.Intent;
25import android.content.pm.PackageManager;
26import android.content.res.Configuration;
27import android.content.res.Resources;
28import android.net.ConnectivityManager;
29import android.net.DhcpResults;
30import android.net.DhcpStateMachine;
31import android.net.InterfaceConfiguration;
32import android.net.LinkAddress;
33import android.net.NetworkInfo;
34import android.net.NetworkUtils;
35import android.net.wifi.WpsInfo;
36import android.net.wifi.p2p.IWifiP2pManager;
37import android.net.wifi.p2p.WifiP2pConfig;
38import android.net.wifi.p2p.WifiP2pDevice;
39import android.net.wifi.p2p.WifiP2pDeviceList;
40import android.net.wifi.p2p.WifiP2pGroup;
41import android.net.wifi.p2p.WifiP2pGroupList;
42import android.net.wifi.p2p.WifiP2pGroupList.GroupDeleteListener;
43import android.net.wifi.p2p.WifiP2pInfo;
44import android.net.wifi.p2p.WifiP2pManager;
45import android.net.wifi.p2p.WifiP2pProvDiscEvent;
46import android.net.wifi.p2p.WifiP2pWfdInfo;
47import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
48import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
49import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
50import android.os.Binder;
51import android.os.Bundle;
52import android.os.IBinder;
53import android.os.INetworkManagementService;
54import android.os.Message;
55import android.os.Messenger;
56import android.os.RemoteException;
57import android.os.ServiceManager;
58import android.os.UserHandle;
59import android.provider.Settings;
60import android.text.TextUtils;
61import android.util.Slog;
62import android.util.SparseArray;
63import android.view.KeyEvent;
64import android.view.LayoutInflater;
65import android.view.View;
66import android.view.ViewGroup;
67import android.view.WindowManager;
68import android.widget.EditText;
69import android.widget.TextView;
70
71import com.android.internal.R;
72import com.android.internal.util.AsyncChannel;
73import com.android.internal.util.Protocol;
74import com.android.internal.util.State;
75import com.android.internal.util.StateMachine;
76import com.android.server.SystemService;
77import com.android.server.wifi.WifiMonitor;
78import com.android.server.wifi.WifiNative;
79import com.android.server.wifi.WifiStateMachine;
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 final class WifiP2pServiceImpl 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 WifiP2pServiceImpl(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 (mDisableP2pTimeoutIndex == 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            sendP2pDiscoveryChangedBroadcast(false);
1096            sendP2pStateChangedBroadcast(false);
1097            mNetworkInfo.setIsAvailable(false);
1098
1099            mLastSetCountryCode = null;
1100        }
1101    }
1102
1103    class InactiveState extends State {
1104        @Override
1105        public void enter() {
1106            if (DBG) logd(getName());
1107            mSavedPeerConfig.invalidate();
1108        }
1109
1110        @Override
1111        public boolean processMessage(Message message) {
1112            if (DBG) logd(getName() + message.toString());
1113            switch (message.what) {
1114                case WifiP2pManager.CONNECT:
1115                    if (DBG) logd(getName() + " sending connect");
1116                    WifiP2pConfig config = (WifiP2pConfig) message.obj;
1117                    if (isConfigInvalid(config)) {
1118                        loge("Dropping connect requeset " + config);
1119                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
1120                        break;
1121                    }
1122
1123                    mAutonomousGroup = false;
1124                    mWifiNative.p2pStopFind();
1125                    if (reinvokePersistentGroup(config)) {
1126                        transitionTo(mGroupNegotiationState);
1127                    } else {
1128                        transitionTo(mProvisionDiscoveryState);
1129                    }
1130                    mSavedPeerConfig = config;
1131                    mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1132                    sendPeersChangedBroadcast();
1133                    replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
1134                    break;
1135                case WifiP2pManager.STOP_DISCOVERY:
1136                    if (mWifiNative.p2pStopFind()) {
1137                        // When discovery stops in inactive state, flush to clear
1138                        // state peer data
1139                        mWifiNative.p2pFlush();
1140                        mServiceDiscReqId = null;
1141                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
1142                    } else {
1143                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
1144                                WifiP2pManager.ERROR);
1145                    }
1146                    break;
1147                case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
1148                    config = (WifiP2pConfig) message.obj;
1149                    if (isConfigInvalid(config)) {
1150                        loge("Dropping GO neg request " + config);
1151                        break;
1152                    }
1153                    mSavedPeerConfig = config;
1154                    mAutonomousGroup = false;
1155                    mJoinExistingGroup = false;
1156                    transitionTo(mUserAuthorizingNegotiationRequestState);
1157                    break;
1158                case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
1159                    WifiP2pGroup group = (WifiP2pGroup) message.obj;
1160                    WifiP2pDevice owner = group.getOwner();
1161
1162                    if (owner == null) {
1163                        loge("Ignored invitation from null owner");
1164                        break;
1165                    }
1166
1167                    config = new WifiP2pConfig();
1168                    config.deviceAddress = group.getOwner().deviceAddress;
1169
1170                    if (isConfigInvalid(config)) {
1171                        loge("Dropping invitation request " + config);
1172                        break;
1173                    }
1174                    mSavedPeerConfig = config;
1175
1176                    //Check if we have the owner in peer list and use appropriate
1177                    //wps method. Default is to use PBC.
1178                    if ((owner = mPeers.get(owner.deviceAddress)) != null) {
1179                        if (owner.wpsPbcSupported()) {
1180                            mSavedPeerConfig.wps.setup = WpsInfo.PBC;
1181                        } else if (owner.wpsKeypadSupported()) {
1182                            mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
1183                        } else if (owner.wpsDisplaySupported()) {
1184                            mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
1185                        }
1186                    }
1187
1188                    mAutonomousGroup = false;
1189                    mJoinExistingGroup = true;
1190                    transitionTo(mUserAuthorizingInviteRequestState);
1191                    break;
1192                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1193                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1194                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1195                    //We let the supplicant handle the provision discovery response
1196                    //and wait instead for the GO_NEGOTIATION_REQUEST_EVENT.
1197                    //Handling provision discovery and issuing a p2p_connect before
1198                    //group negotiation comes through causes issues
1199                    break;
1200                case WifiP2pManager.CREATE_GROUP:
1201                    mAutonomousGroup = true;
1202                    int netId = message.arg1;
1203                    boolean ret = false;
1204                    if (netId == WifiP2pGroup.PERSISTENT_NET_ID) {
1205                        // check if the go persistent group is present.
1206                        netId = mGroups.getNetworkId(mThisDevice.deviceAddress);
1207                        if (netId != -1) {
1208                            ret = mWifiNative.p2pGroupAdd(netId);
1209                        } else {
1210                            ret = mWifiNative.p2pGroupAdd(true);
1211                        }
1212                    } else {
1213                        ret = mWifiNative.p2pGroupAdd(false);
1214                    }
1215
1216                    if (ret) {
1217                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
1218                        transitionTo(mGroupNegotiationState);
1219                    } else {
1220                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
1221                                WifiP2pManager.ERROR);
1222                        // remain at this state.
1223                    }
1224                    break;
1225                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1226                    mGroup = (WifiP2pGroup) message.obj;
1227                    if (DBG) logd(getName() + " group started");
1228
1229                    // We hit this scenario when a persistent group is reinvoked
1230                    if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
1231                        mAutonomousGroup = false;
1232                        deferMessage(message);
1233                        transitionTo(mGroupNegotiationState);
1234                    } else {
1235                        loge("Unexpected group creation, remove " + mGroup);
1236                        mWifiNative.p2pGroupRemove(mGroup.getInterface());
1237                    }
1238                    break;
1239                case WifiP2pManager.START_LISTEN:
1240                    if (DBG) logd(getName() + " start listen mode");
1241                    mWifiNative.p2pFlush();
1242                    if (mWifiNative.p2pExtListen(true, 500, 500)) {
1243                        replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
1244                    } else {
1245                        replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
1246                    }
1247                    break;
1248                case WifiP2pManager.STOP_LISTEN:
1249                    if (DBG) logd(getName() + " stop listen mode");
1250                    if (mWifiNative.p2pExtListen(false, 0, 0)) {
1251                        replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
1252                    } else {
1253                        replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
1254                    }
1255                    mWifiNative.p2pFlush();
1256                    break;
1257                case WifiP2pManager.SET_CHANNEL:
1258                    Bundle p2pChannels = (Bundle) message.obj;
1259                    int lc = p2pChannels.getInt("lc", 0);
1260                    int oc = p2pChannels.getInt("oc", 0);
1261                    if (DBG) logd(getName() + " set listen and operating channel");
1262                    if (mWifiNative.p2pSetChannel(lc, oc)) {
1263                        replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
1264                    } else {
1265                        replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
1266                    }
1267                    break;
1268                default:
1269                    return NOT_HANDLED;
1270            }
1271            return HANDLED;
1272        }
1273    }
1274
1275    class GroupCreatingState extends State {
1276        @Override
1277        public void enter() {
1278            if (DBG) logd(getName());
1279            sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,
1280                    ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);
1281        }
1282
1283        @Override
1284        public boolean processMessage(Message message) {
1285            if (DBG) logd(getName() + message.toString());
1286            boolean ret = HANDLED;
1287            switch (message.what) {
1288               case GROUP_CREATING_TIMED_OUT:
1289                    if (mGroupCreatingTimeoutIndex == message.arg1) {
1290                        if (DBG) logd("Group negotiation timed out");
1291                        handleGroupCreationFailure();
1292                        transitionTo(mInactiveState);
1293                    }
1294                    break;
1295                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
1296                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
1297                    if (!mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
1298                        if (DBG) {
1299                            logd("mSavedPeerConfig " + mSavedPeerConfig.deviceAddress +
1300                                "device " + device.deviceAddress);
1301                        }
1302                        // Do the regular device lost handling
1303                        ret = NOT_HANDLED;
1304                        break;
1305                    }
1306                    // Do nothing
1307                    if (DBG) logd("Add device to lost list " + device);
1308                    mPeersLostDuringConnection.updateSupplicantDetails(device);
1309                    break;
1310                case WifiP2pManager.DISCOVER_PEERS:
1311                    /* Discovery will break negotiation */
1312                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
1313                            WifiP2pManager.BUSY);
1314                    break;
1315                case WifiP2pManager.CANCEL_CONNECT:
1316                    //Do a supplicant p2p_cancel which only cancels an ongoing
1317                    //group negotiation. This will fail for a pending provision
1318                    //discovery or for a pending user action, but at the framework
1319                    //level, we always treat cancel as succeeded and enter
1320                    //an inactive state
1321                    mWifiNative.p2pCancelConnect();
1322                    handleGroupCreationFailure();
1323                    transitionTo(mInactiveState);
1324                    replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
1325                    break;
1326                default:
1327                    ret = NOT_HANDLED;
1328            }
1329            return ret;
1330        }
1331    }
1332
1333    class UserAuthorizingNegotiationRequestState extends State {
1334        @Override
1335        public void enter() {
1336            if (DBG) logd(getName());
1337            notifyInvitationReceived();
1338        }
1339
1340        @Override
1341        public boolean processMessage(Message message) {
1342            if (DBG) logd(getName() + message.toString());
1343            boolean ret = HANDLED;
1344            switch (message.what) {
1345                case PEER_CONNECTION_USER_ACCEPT:
1346                    mWifiNative.p2pStopFind();
1347                    p2pConnectWithPinDisplay(mSavedPeerConfig);
1348                    mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1349                    sendPeersChangedBroadcast();
1350                    transitionTo(mGroupNegotiationState);
1351                   break;
1352                case PEER_CONNECTION_USER_REJECT:
1353                    if (DBG) logd("User rejected negotiation " + mSavedPeerConfig);
1354                    transitionTo(mInactiveState);
1355                    break;
1356                default:
1357                    return NOT_HANDLED;
1358            }
1359            return ret;
1360        }
1361
1362        @Override
1363        public void exit() {
1364            //TODO: dismiss dialog if not already done
1365        }
1366    }
1367
1368    class UserAuthorizingInviteRequestState extends State {
1369        @Override
1370        public void enter() {
1371            if (DBG) logd(getName());
1372            notifyInvitationReceived();
1373        }
1374
1375        @Override
1376        public boolean processMessage(Message message) {
1377            if (DBG) logd(getName() + message.toString());
1378            boolean ret = HANDLED;
1379            switch (message.what) {
1380                case PEER_CONNECTION_USER_ACCEPT:
1381                    mWifiNative.p2pStopFind();
1382                    if (!reinvokePersistentGroup(mSavedPeerConfig)) {
1383                        // Do negotiation when persistence fails
1384                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1385                    }
1386                    mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1387                    sendPeersChangedBroadcast();
1388                    transitionTo(mGroupNegotiationState);
1389                   break;
1390                case PEER_CONNECTION_USER_REJECT:
1391                    if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
1392                    transitionTo(mInactiveState);
1393                    break;
1394                default:
1395                    return NOT_HANDLED;
1396            }
1397            return ret;
1398        }
1399
1400        @Override
1401        public void exit() {
1402            //TODO: dismiss dialog if not already done
1403        }
1404    }
1405
1406
1407
1408    class ProvisionDiscoveryState extends State {
1409        @Override
1410        public void enter() {
1411            if (DBG) logd(getName());
1412            mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
1413        }
1414
1415        @Override
1416        public boolean processMessage(Message message) {
1417            if (DBG) logd(getName() + message.toString());
1418            WifiP2pProvDiscEvent provDisc;
1419            WifiP2pDevice device;
1420            switch (message.what) {
1421                case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
1422                    provDisc = (WifiP2pProvDiscEvent) message.obj;
1423                    device = provDisc.device;
1424                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1425
1426                    if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
1427                        if (DBG) logd("Found a match " + mSavedPeerConfig);
1428                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1429                        transitionTo(mGroupNegotiationState);
1430                    }
1431                    break;
1432                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1433                    provDisc = (WifiP2pProvDiscEvent) message.obj;
1434                    device = provDisc.device;
1435                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1436
1437                    if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) {
1438                        if (DBG) logd("Found a match " + mSavedPeerConfig);
1439                        /* we already have the pin */
1440                        if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
1441                            p2pConnectWithPinDisplay(mSavedPeerConfig);
1442                            transitionTo(mGroupNegotiationState);
1443                        } else {
1444                            mJoinExistingGroup = false;
1445                            transitionTo(mUserAuthorizingNegotiationRequestState);
1446                        }
1447                    }
1448                    break;
1449                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1450                    provDisc = (WifiP2pProvDiscEvent) message.obj;
1451                    device = provDisc.device;
1452                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1453
1454                    if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
1455                        if (DBG) logd("Found a match " + mSavedPeerConfig);
1456                        mSavedPeerConfig.wps.pin = provDisc.pin;
1457                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1458                        notifyInvitationSent(provDisc.pin, device.deviceAddress);
1459                        transitionTo(mGroupNegotiationState);
1460                    }
1461                    break;
1462                case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
1463                    loge("provision discovery failed");
1464                    handleGroupCreationFailure();
1465                    transitionTo(mInactiveState);
1466                    break;
1467                default:
1468                    return NOT_HANDLED;
1469            }
1470            return HANDLED;
1471        }
1472    }
1473
1474    class GroupNegotiationState extends State {
1475        @Override
1476        public void enter() {
1477            if (DBG) logd(getName());
1478        }
1479
1480        @Override
1481        public boolean processMessage(Message message) {
1482            if (DBG) logd(getName() + message.toString());
1483            switch (message.what) {
1484                // We ignore these right now, since we get a GROUP_STARTED notification
1485                // afterwards
1486                case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1487                case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
1488                    if (DBG) logd(getName() + " go success");
1489                    break;
1490                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1491                    mGroup = (WifiP2pGroup) message.obj;
1492                    if (DBG) logd(getName() + " group started");
1493
1494                    if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
1495                        /*
1496                         * update cache information and set network id to mGroup.
1497                         */
1498                        updatePersistentNetworks(NO_RELOAD);
1499                        String devAddr = mGroup.getOwner().deviceAddress;
1500                        mGroup.setNetworkId(mGroups.getNetworkId(devAddr,
1501                                mGroup.getNetworkName()));
1502                    }
1503
1504                    if (mGroup.isGroupOwner()) {
1505                        /* Setting an idle time out on GO causes issues with certain scenarios
1506                         * on clients where it can be off-channel for longer and with the power
1507                         * save modes used.
1508                         *
1509                         * TODO: Verify multi-channel scenarios and supplicant behavior are
1510                         * better before adding a time out in future
1511                         */
1512                        //Set group idle timeout of 10 sec, to avoid GO beaconing incase of any
1513                        //failure during 4-way Handshake.
1514                        if (!mAutonomousGroup) {
1515                            mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
1516                        }
1517                        startDhcpServer(mGroup.getInterface());
1518                    } else {
1519                        mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
1520                        mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
1521                                P2pStateMachine.this, mGroup.getInterface());
1522                        // TODO: We should use DHCP state machine PRE message like WifiStateMachine
1523                        mWifiNative.setP2pPowerSave(mGroup.getInterface(), false);
1524                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
1525                        WifiP2pDevice groupOwner = mGroup.getOwner();
1526                        WifiP2pDevice peer = mPeers.get(groupOwner.deviceAddress);
1527                        if (peer != null) {
1528                            // update group owner details with peer details found at discovery
1529                            groupOwner.updateSupplicantDetails(peer);
1530                            mPeers.updateStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
1531                            sendPeersChangedBroadcast();
1532                        } else {
1533                            // A supplicant bug can lead to reporting an invalid
1534                            // group owner address (all zeroes) at times. Avoid a
1535                            // crash, but continue group creation since it is not
1536                            // essential.
1537                            logw("Unknown group owner " + groupOwner);
1538                        }
1539                    }
1540                    transitionTo(mGroupCreatedState);
1541                    break;
1542                case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
1543                    P2pStatus status = (P2pStatus) message.obj;
1544                    if (status == P2pStatus.NO_COMMON_CHANNEL) {
1545                        transitionTo(mFrequencyConflictState);
1546                        break;
1547                    }
1548                    /* continue with group removal handling */
1549                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1550                    if (DBG) logd(getName() + " go failure");
1551                    handleGroupCreationFailure();
1552                    transitionTo(mInactiveState);
1553                    break;
1554                // A group formation failure is always followed by
1555                // a group removed event. Flushing things at group formation
1556                // failure causes supplicant issues. Ignore right now.
1557                case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
1558                    status = (P2pStatus) message.obj;
1559                    if (status == P2pStatus.NO_COMMON_CHANNEL) {
1560                        transitionTo(mFrequencyConflictState);
1561                        break;
1562                    }
1563                    break;
1564                case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
1565                    status = (P2pStatus)message.obj;
1566                    if (status == P2pStatus.SUCCESS) {
1567                        // invocation was succeeded.
1568                        // wait P2P_GROUP_STARTED_EVENT.
1569                        break;
1570                    }
1571                    loge("Invitation result " + status);
1572                    if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
1573                        // target device has already removed the credential.
1574                        // So, remove this credential accordingly.
1575                        int netId = mSavedPeerConfig.netId;
1576                        if (netId >= 0) {
1577                            if (DBG) logd("Remove unknown client from the list");
1578                            removeClientFromList(netId, mSavedPeerConfig.deviceAddress, true);
1579                        }
1580
1581                        // Reinvocation has failed, try group negotiation
1582                        mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
1583                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1584                    } else if (status == P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE) {
1585
1586                        // Devices setting persistent_reconnect to 0 in wpa_supplicant
1587                        // always defer the invocation request and return
1588                        // "information is currently unable" error.
1589                        // So, try another way to connect for interoperability.
1590                        mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
1591                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1592                    } else if (status == P2pStatus.NO_COMMON_CHANNEL) {
1593                        transitionTo(mFrequencyConflictState);
1594                    } else {
1595                        handleGroupCreationFailure();
1596                        transitionTo(mInactiveState);
1597                    }
1598                    break;
1599                default:
1600                    return NOT_HANDLED;
1601            }
1602            return HANDLED;
1603        }
1604    }
1605
1606    class FrequencyConflictState extends State {
1607        private AlertDialog mFrequencyConflictDialog;
1608        @Override
1609        public void enter() {
1610            if (DBG) logd(getName());
1611            notifyFrequencyConflict();
1612        }
1613
1614        private void notifyFrequencyConflict() {
1615            logd("Notify frequency conflict");
1616            Resources r = Resources.getSystem();
1617
1618            AlertDialog dialog = new AlertDialog.Builder(mContext)
1619                .setMessage(r.getString(R.string.wifi_p2p_frequency_conflict_message,
1620                        getDeviceName(mSavedPeerConfig.deviceAddress)))
1621                .setPositiveButton(r.getString(R.string.dlg_ok), new OnClickListener() {
1622                        @Override
1623                        public void onClick(DialogInterface dialog, int which) {
1624                            sendMessage(DROP_WIFI_USER_ACCEPT);
1625                        }
1626                    })
1627                .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
1628                        @Override
1629                        public void onClick(DialogInterface dialog, int which) {
1630                            sendMessage(DROP_WIFI_USER_REJECT);
1631                        }
1632                    })
1633                .setOnCancelListener(new DialogInterface.OnCancelListener() {
1634                        @Override
1635                        public void onCancel(DialogInterface arg0) {
1636                            sendMessage(DROP_WIFI_USER_REJECT);
1637                        }
1638                    })
1639                .create();
1640
1641            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1642            dialog.show();
1643            mFrequencyConflictDialog = dialog;
1644        }
1645
1646        @Override
1647        public boolean processMessage(Message message) {
1648            if (DBG) logd(getName() + message.toString());
1649            switch (message.what) {
1650                case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1651                case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
1652                    loge(getName() + "group sucess during freq conflict!");
1653                    break;
1654                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1655                    loge(getName() + "group started after freq conflict, handle anyway");
1656                    deferMessage(message);
1657                    transitionTo(mGroupNegotiationState);
1658                    break;
1659                case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
1660                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1661                case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
1662                    // Ignore failures since we retry again
1663                    break;
1664                case DROP_WIFI_USER_REJECT:
1665                    // User rejected dropping wifi in favour of p2p
1666                    handleGroupCreationFailure();
1667                    transitionTo(mInactiveState);
1668                    break;
1669                case DROP_WIFI_USER_ACCEPT:
1670                    // User accepted dropping wifi in favour of p2p
1671                    mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 1);
1672                    mTempoarilyDisconnectedWifi = true;
1673                    break;
1674                case DISCONNECT_WIFI_RESPONSE:
1675                    // Got a response from wifistatemachine, retry p2p
1676                    if (DBG) logd(getName() + "Wifi disconnected, retry p2p");
1677                    transitionTo(mInactiveState);
1678                    sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
1679                    break;
1680                default:
1681                    return NOT_HANDLED;
1682            }
1683            return HANDLED;
1684        }
1685
1686        public void exit() {
1687            if (mFrequencyConflictDialog != null) mFrequencyConflictDialog.dismiss();
1688        }
1689    }
1690
1691    class GroupCreatedState extends State {
1692        @Override
1693        public void enter() {
1694            if (DBG) logd(getName());
1695            // Once connected, peer config details are invalid
1696            mSavedPeerConfig.invalidate();
1697            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
1698
1699            updateThisDevice(WifiP2pDevice.CONNECTED);
1700
1701            //DHCP server has already been started if I am a group owner
1702            if (mGroup.isGroupOwner()) {
1703                setWifiP2pInfoOnGroupFormation(NetworkUtils.numericToInetAddress(SERVER_ADDRESS));
1704            }
1705
1706            // In case of a negotiation group, connection changed is sent
1707            // after a client joins. For autonomous, send now
1708            if (mAutonomousGroup) {
1709                sendP2pConnectionChangedBroadcast();
1710            }
1711        }
1712
1713        @Override
1714        public boolean processMessage(Message message) {
1715            if (DBG) logd(getName() + message.toString());
1716            switch (message.what) {
1717                case WifiMonitor.AP_STA_CONNECTED_EVENT:
1718                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
1719                    String deviceAddress = device.deviceAddress;
1720                    // Clear timeout that was set when group was started.
1721                    mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
1722                    if (deviceAddress != null) {
1723                        if (mPeers.get(deviceAddress) != null) {
1724                            mGroup.addClient(mPeers.get(deviceAddress));
1725                        } else {
1726                            mGroup.addClient(deviceAddress);
1727                        }
1728                        mPeers.updateStatus(deviceAddress, WifiP2pDevice.CONNECTED);
1729                        if (DBG) logd(getName() + " ap sta connected");
1730                        sendPeersChangedBroadcast();
1731                    } else {
1732                        loge("Connect on null device address, ignore");
1733                    }
1734                    sendP2pConnectionChangedBroadcast();
1735                    break;
1736                case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
1737                    device = (WifiP2pDevice) message.obj;
1738                    deviceAddress = device.deviceAddress;
1739                    if (deviceAddress != null) {
1740                        mPeers.updateStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
1741                        if (mGroup.removeClient(deviceAddress)) {
1742                            if (DBG) logd("Removed client " + deviceAddress);
1743                            if (!mAutonomousGroup && mGroup.isClientListEmpty()) {
1744                                logd("Client list empty, remove non-persistent p2p group");
1745                                mWifiNative.p2pGroupRemove(mGroup.getInterface());
1746                                // We end up sending connection changed broadcast
1747                                // when this happens at exit()
1748                            } else {
1749                                // Notify when a client disconnects from group
1750                                sendP2pConnectionChangedBroadcast();
1751                            }
1752                        } else {
1753                            if (DBG) logd("Failed to remove client " + deviceAddress);
1754                            for (WifiP2pDevice c : mGroup.getClientList()) {
1755                                if (DBG) logd("client " + c.deviceAddress);
1756                            }
1757                        }
1758                        sendPeersChangedBroadcast();
1759                        if (DBG) logd(getName() + " ap sta disconnected");
1760                    } else {
1761                        loge("Disconnect on unknown device: " + device);
1762                    }
1763                    break;
1764                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
1765                    DhcpResults dhcpResults = (DhcpResults) message.obj;
1766                    if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS &&
1767                            dhcpResults != null) {
1768                        if (DBG) logd("DhcpResults: " + dhcpResults);
1769                        setWifiP2pInfoOnGroupFormation(dhcpResults.serverAddress);
1770                        sendP2pConnectionChangedBroadcast();
1771                        //Turn on power save on client
1772                        mWifiNative.setP2pPowerSave(mGroup.getInterface(), true);
1773                    } else {
1774                        loge("DHCP failed");
1775                        mWifiNative.p2pGroupRemove(mGroup.getInterface());
1776                    }
1777                    break;
1778                case WifiP2pManager.REMOVE_GROUP:
1779                    if (DBG) logd(getName() + " remove group");
1780                    if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
1781                        transitionTo(mOngoingGroupRemovalState);
1782                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
1783                    } else {
1784                        handleGroupRemoved();
1785                        transitionTo(mInactiveState);
1786                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
1787                                WifiP2pManager.ERROR);
1788                    }
1789                    break;
1790                /* We do not listen to NETWORK_DISCONNECTION_EVENT for group removal
1791                 * handling since supplicant actually tries to reconnect after a temporary
1792                 * disconnect until group idle time out. Eventually, a group removal event
1793                 * will come when group has been removed.
1794                 *
1795                 * When there are connectivity issues during temporary disconnect, the application
1796                 * will also just remove the group.
1797                 *
1798                 * Treating network disconnection as group removal causes race conditions since
1799                 * supplicant would still maintain the group at that stage.
1800                 */
1801                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1802                    if (DBG) logd(getName() + " group removed");
1803                    handleGroupRemoved();
1804                    transitionTo(mInactiveState);
1805                    break;
1806                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
1807                    device = (WifiP2pDevice) message.obj;
1808                    //Device loss for a connected device indicates it is not in discovery any more
1809                    if (mGroup.contains(device)) {
1810                        if (DBG) logd("Add device to lost list " + device);
1811                        mPeersLostDuringConnection.updateSupplicantDetails(device);
1812                        return HANDLED;
1813                    }
1814                    // Do the regular device lost handling
1815                    return NOT_HANDLED;
1816                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
1817                    sendMessage(WifiP2pManager.REMOVE_GROUP);
1818                    deferMessage(message);
1819                    break;
1820                    // This allows any client to join the GO during the
1821                    // WPS window
1822                case WifiP2pManager.START_WPS:
1823                    WpsInfo wps = (WpsInfo) message.obj;
1824                    if (wps == null) {
1825                        replyToMessage(message, WifiP2pManager.START_WPS_FAILED);
1826                        break;
1827                    }
1828                    boolean ret = true;
1829                    if (wps.setup == WpsInfo.PBC) {
1830                        ret = mWifiNative.startWpsPbc(mGroup.getInterface(), null);
1831                    } else {
1832                        if (wps.pin == null) {
1833                            String pin = mWifiNative.startWpsPinDisplay(mGroup.getInterface());
1834                            try {
1835                                Integer.parseInt(pin);
1836                                notifyInvitationSent(pin, "any");
1837                            } catch (NumberFormatException ignore) {
1838                                ret = false;
1839                            }
1840                        } else {
1841                            ret = mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
1842                                    wps.pin);
1843                        }
1844                    }
1845                    replyToMessage(message, ret ? WifiP2pManager.START_WPS_SUCCEEDED :
1846                            WifiP2pManager.START_WPS_FAILED);
1847                    break;
1848                case WifiP2pManager.CONNECT:
1849                    WifiP2pConfig config = (WifiP2pConfig) message.obj;
1850                    if (isConfigInvalid(config)) {
1851                        loge("Dropping connect requeset " + config);
1852                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
1853                        break;
1854                    }
1855                    logd("Inviting device : " + config.deviceAddress);
1856                    mSavedPeerConfig = config;
1857                    if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
1858                        mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
1859                        sendPeersChangedBroadcast();
1860                        replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
1861                    } else {
1862                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
1863                                WifiP2pManager.ERROR);
1864                    }
1865                    // TODO: figure out updating the status to declined when invitation is rejected
1866                    break;
1867                case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
1868                    P2pStatus status = (P2pStatus)message.obj;
1869                    if (status == P2pStatus.SUCCESS) {
1870                        // invocation was succeeded.
1871                        break;
1872                    }
1873                    loge("Invitation result " + status);
1874                    if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
1875                        // target device has already removed the credential.
1876                        // So, remove this credential accordingly.
1877                        int netId = mGroup.getNetworkId();
1878                        if (netId >= 0) {
1879                            if (DBG) logd("Remove unknown client from the list");
1880                            if (!removeClientFromList(netId,
1881                                    mSavedPeerConfig.deviceAddress, false)) {
1882                                // not found the client on the list
1883                                loge("Already removed the client, ignore");
1884                                break;
1885                            }
1886                            // try invitation.
1887                            sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
1888                        }
1889                    }
1890                    break;
1891                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1892                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1893                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1894                    WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
1895                    mSavedPeerConfig = new WifiP2pConfig();
1896                    mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
1897                    if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
1898                        mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
1899                    } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
1900                        mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
1901                        mSavedPeerConfig.wps.pin = provDisc.pin;
1902                    } else {
1903                        mSavedPeerConfig.wps.setup = WpsInfo.PBC;
1904                    }
1905                    transitionTo(mUserAuthorizingJoinState);
1906                    break;
1907                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1908                    loge("Duplicate group creation event notice, ignore");
1909                    break;
1910                default:
1911                    return NOT_HANDLED;
1912            }
1913            return HANDLED;
1914        }
1915
1916        public void exit() {
1917            updateThisDevice(WifiP2pDevice.AVAILABLE);
1918            resetWifiP2pInfo();
1919            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
1920            sendP2pConnectionChangedBroadcast();
1921        }
1922    }
1923
1924    class UserAuthorizingJoinState extends State {
1925        @Override
1926        public void enter() {
1927            if (DBG) logd(getName());
1928            notifyInvitationReceived();
1929        }
1930
1931        @Override
1932        public boolean processMessage(Message message) {
1933            if (DBG) logd(getName() + message.toString());
1934            switch (message.what) {
1935                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1936                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1937                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1938                    //Ignore more client requests
1939                    break;
1940                case PEER_CONNECTION_USER_ACCEPT:
1941                    //Stop discovery to avoid failure due to channel switch
1942                    mWifiNative.p2pStopFind();
1943                    if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
1944                        mWifiNative.startWpsPbc(mGroup.getInterface(), null);
1945                    } else {
1946                        mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
1947                                mSavedPeerConfig.wps.pin);
1948                    }
1949                    transitionTo(mGroupCreatedState);
1950                    break;
1951                case PEER_CONNECTION_USER_REJECT:
1952                    if (DBG) logd("User rejected incoming request");
1953                    transitionTo(mGroupCreatedState);
1954                    break;
1955                default:
1956                    return NOT_HANDLED;
1957            }
1958            return HANDLED;
1959        }
1960
1961        @Override
1962        public void exit() {
1963            //TODO: dismiss dialog if not already done
1964        }
1965    }
1966
1967    class OngoingGroupRemovalState extends State {
1968        @Override
1969        public void enter() {
1970            if (DBG) logd(getName());
1971        }
1972
1973        @Override
1974        public boolean processMessage(Message message) {
1975            if (DBG) logd(getName() + message.toString());
1976            switch (message.what) {
1977                // Group removal ongoing. Multiple calls
1978                // end up removing persisted network. Do nothing.
1979                case WifiP2pManager.REMOVE_GROUP:
1980                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
1981                    break;
1982                // Parent state will transition out of this state
1983                // when removal is complete
1984                default:
1985                    return NOT_HANDLED;
1986            }
1987            return HANDLED;
1988        }
1989    }
1990
1991    @Override
1992    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1993        super.dump(fd, pw, args);
1994        pw.println("mWifiP2pInfo " + mWifiP2pInfo);
1995        pw.println("mGroup " + mGroup);
1996        pw.println("mSavedPeerConfig " + mSavedPeerConfig);
1997        pw.println("mSavedP2pGroup " + mSavedP2pGroup);
1998        pw.println();
1999    }
2000
2001    private void sendP2pStateChangedBroadcast(boolean enabled) {
2002        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
2003        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2004        if (enabled) {
2005            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
2006                    WifiP2pManager.WIFI_P2P_STATE_ENABLED);
2007        } else {
2008            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
2009                    WifiP2pManager.WIFI_P2P_STATE_DISABLED);
2010        }
2011        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2012    }
2013
2014    private void sendP2pDiscoveryChangedBroadcast(boolean started) {
2015        if (mDiscoveryStarted == started) return;
2016        mDiscoveryStarted = started;
2017
2018        if (DBG) logd("discovery change broadcast " + started);
2019
2020        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
2021        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2022        intent.putExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, started ?
2023                WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED :
2024                WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
2025        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2026    }
2027
2028    private void sendThisDeviceChangedBroadcast() {
2029        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
2030        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2031        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
2032        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2033    }
2034
2035    private void sendPeersChangedBroadcast() {
2036        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
2037        intent.putExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, new WifiP2pDeviceList(mPeers));
2038        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2039        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2040    }
2041
2042    private void sendP2pConnectionChangedBroadcast() {
2043        if (DBG) logd("sending p2p connection changed broadcast");
2044        Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
2045        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2046                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
2047        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
2048        intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
2049        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, new WifiP2pGroup(mGroup));
2050        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2051        mWifiChannel.sendMessage(WifiP2pServiceImpl.P2P_CONNECTION_CHANGED,
2052                new NetworkInfo(mNetworkInfo));
2053    }
2054
2055    private void sendP2pPersistentGroupsChangedBroadcast() {
2056        if (DBG) logd("sending p2p persistent groups changed broadcast");
2057        Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
2058        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2059        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2060    }
2061
2062    private void startDhcpServer(String intf) {
2063        InterfaceConfiguration ifcg = null;
2064        try {
2065            ifcg = mNwService.getInterfaceConfig(intf);
2066            ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(
2067                        SERVER_ADDRESS), 24));
2068            ifcg.setInterfaceUp();
2069            mNwService.setInterfaceConfig(intf, ifcg);
2070            /* This starts the dnsmasq server */
2071            mNwService.startTethering(DHCP_RANGE);
2072        } catch (Exception e) {
2073            loge("Error configuring interface " + intf + ", :" + e);
2074            return;
2075        }
2076
2077        logd("Started Dhcp server on " + intf);
2078   }
2079
2080    private void stopDhcpServer(String intf) {
2081        try {
2082            mNwService.stopTethering();
2083        } catch (Exception e) {
2084            loge("Error stopping Dhcp server" + e);
2085            return;
2086        }
2087
2088        logd("Stopped Dhcp server");
2089    }
2090
2091    private void notifyP2pEnableFailure() {
2092        Resources r = Resources.getSystem();
2093        AlertDialog dialog = new AlertDialog.Builder(mContext)
2094            .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
2095            .setMessage(r.getString(R.string.wifi_p2p_failed_message))
2096            .setPositiveButton(r.getString(R.string.ok), null)
2097            .create();
2098        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
2099        dialog.show();
2100    }
2101
2102    private void addRowToDialog(ViewGroup group, int stringId, String value) {
2103        Resources r = Resources.getSystem();
2104        View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row,
2105                group, false);
2106        ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId));
2107        ((TextView) row.findViewById(R.id.value)).setText(value);
2108        group.addView(row);
2109    }
2110
2111    private void notifyInvitationSent(String pin, String peerAddress) {
2112        Resources r = Resources.getSystem();
2113
2114        final View textEntryView = LayoutInflater.from(mContext)
2115                .inflate(R.layout.wifi_p2p_dialog, null);
2116
2117        ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
2118        addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress));
2119        addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin);
2120
2121        AlertDialog dialog = new AlertDialog.Builder(mContext)
2122            .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title))
2123            .setView(textEntryView)
2124            .setPositiveButton(r.getString(R.string.ok), null)
2125            .create();
2126        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
2127        dialog.show();
2128    }
2129
2130    private void notifyInvitationReceived() {
2131        Resources r = Resources.getSystem();
2132        final WpsInfo wps = mSavedPeerConfig.wps;
2133        final View textEntryView = LayoutInflater.from(mContext)
2134                .inflate(R.layout.wifi_p2p_dialog, null);
2135
2136        ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
2137        addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName(
2138                mSavedPeerConfig.deviceAddress));
2139
2140        final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin);
2141
2142        AlertDialog dialog = new AlertDialog.Builder(mContext)
2143            .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title))
2144            .setView(textEntryView)
2145            .setPositiveButton(r.getString(R.string.accept), new OnClickListener() {
2146                        public void onClick(DialogInterface dialog, int which) {
2147                            if (wps.setup == WpsInfo.KEYPAD) {
2148                                mSavedPeerConfig.wps.pin = pin.getText().toString();
2149                            }
2150                            if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig);
2151                            sendMessage(PEER_CONNECTION_USER_ACCEPT);
2152                        }
2153                    })
2154            .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
2155                        @Override
2156                        public void onClick(DialogInterface dialog, int which) {
2157                            if (DBG) logd(getName() + " ignore connect");
2158                            sendMessage(PEER_CONNECTION_USER_REJECT);
2159                        }
2160                    })
2161            .setOnCancelListener(new DialogInterface.OnCancelListener() {
2162                        @Override
2163                        public void onCancel(DialogInterface arg0) {
2164                            if (DBG) logd(getName() + " ignore connect");
2165                            sendMessage(PEER_CONNECTION_USER_REJECT);
2166                        }
2167                    })
2168            .create();
2169
2170        //make the enter pin area or the display pin area visible
2171        switch (wps.setup) {
2172            case WpsInfo.KEYPAD:
2173                if (DBG) logd("Enter pin section visible");
2174                textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE);
2175                break;
2176            case WpsInfo.DISPLAY:
2177                if (DBG) logd("Shown pin section visible");
2178                addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin);
2179                break;
2180            default:
2181                break;
2182        }
2183
2184        if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_APPLIANCE) ==
2185                Configuration.UI_MODE_TYPE_APPLIANCE) {
2186            // For appliance devices, add a key listener which accepts.
2187            dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
2188
2189                @Override
2190                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
2191                    // TODO: make the actual key come from a config value.
2192                    if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
2193                        sendMessage(PEER_CONNECTION_USER_ACCEPT);
2194                        dialog.dismiss();
2195                        return true;
2196                    }
2197                    return false;
2198                }
2199            });
2200            // TODO: add timeout for this dialog.
2201            // TODO: update UI in appliance mode to tell user what to do.
2202        }
2203
2204        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
2205        dialog.show();
2206    }
2207
2208    /**
2209     * Synchronize the persistent group list between
2210     * wpa_supplicant and mGroups.
2211     */
2212    private void updatePersistentNetworks(boolean reload) {
2213        String listStr = mWifiNative.listNetworks();
2214        if (listStr == null) return;
2215
2216        boolean isSaveRequired = false;
2217        String[] lines = listStr.split("\n");
2218        if (lines == null) return;
2219
2220        if (reload) mGroups.clear();
2221
2222        // Skip the first line, which is a header
2223        for (int i = 1; i < lines.length; i++) {
2224            String[] result = lines[i].split("\t");
2225            if (result == null || result.length < 4) {
2226                continue;
2227            }
2228            // network-id | ssid | bssid | flags
2229            int netId = -1;
2230            String ssid = result[1];
2231            String bssid = result[2];
2232            String flags = result[3];
2233            try {
2234                netId = Integer.parseInt(result[0]);
2235            } catch(NumberFormatException e) {
2236                e.printStackTrace();
2237                continue;
2238            }
2239
2240            if (flags.indexOf("[CURRENT]") != -1) {
2241                continue;
2242            }
2243            if (flags.indexOf("[P2P-PERSISTENT]") == -1) {
2244                /*
2245                 * The unused profile is sometimes remained when the p2p group formation is failed.
2246                 * So, we clean up the p2p group here.
2247                 */
2248                if (DBG) logd("clean up the unused persistent group. netId=" + netId);
2249                mWifiNative.removeNetwork(netId);
2250                isSaveRequired = true;
2251                continue;
2252            }
2253
2254            if (mGroups.contains(netId)) {
2255                continue;
2256            }
2257
2258            WifiP2pGroup group = new WifiP2pGroup();
2259            group.setNetworkId(netId);
2260            group.setNetworkName(ssid);
2261            String mode = mWifiNative.getNetworkVariable(netId, "mode");
2262            if (mode != null && mode.equals("3")) {
2263                group.setIsGroupOwner(true);
2264            }
2265            if (bssid.equalsIgnoreCase(mThisDevice.deviceAddress)) {
2266                group.setOwner(mThisDevice);
2267            } else {
2268                WifiP2pDevice device = new WifiP2pDevice();
2269                device.deviceAddress = bssid;
2270                group.setOwner(device);
2271            }
2272            mGroups.add(group);
2273            isSaveRequired = true;
2274        }
2275
2276        if (reload || isSaveRequired) {
2277            mWifiNative.saveConfig();
2278            sendP2pPersistentGroupsChangedBroadcast();
2279        }
2280    }
2281
2282    /**
2283     * A config is valid if it has a peer address that has already been
2284     * discovered
2285     * @return true if it is invalid, false otherwise
2286     */
2287    private boolean isConfigInvalid(WifiP2pConfig config) {
2288        if (config == null) return true;
2289        if (TextUtils.isEmpty(config.deviceAddress)) return true;
2290        if (mPeers.get(config.deviceAddress) == null) return true;
2291        return false;
2292    }
2293
2294    /* TODO: The supplicant does not provide group capability changes as an event.
2295     * Having it pushed as an event would avoid polling for this information right
2296     * before a connection
2297     */
2298    private WifiP2pDevice fetchCurrentDeviceDetails(WifiP2pConfig config) {
2299        /* Fetch & update group capability from supplicant on the device */
2300        int gc = mWifiNative.getGroupCapability(config.deviceAddress);
2301        mPeers.updateGroupCapability(config.deviceAddress, gc);
2302        return mPeers.get(config.deviceAddress);
2303    }
2304
2305    /**
2306     * Start a p2p group negotiation and display pin if necessary
2307     * @param config for the peer
2308     */
2309    private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
2310        WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
2311
2312        String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
2313        try {
2314            Integer.parseInt(pin);
2315            notifyInvitationSent(pin, config.deviceAddress);
2316        } catch (NumberFormatException ignore) {
2317            // do nothing if p2pConnect did not return a pin
2318        }
2319    }
2320
2321    /**
2322     * Reinvoke a persistent group.
2323     *
2324     * @param config for the peer
2325     * @return true on success, false on failure
2326     */
2327    private boolean reinvokePersistentGroup(WifiP2pConfig config) {
2328        WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
2329
2330        boolean join = dev.isGroupOwner();
2331        String ssid = mWifiNative.p2pGetSsid(dev.deviceAddress);
2332        if (DBG) logd("target ssid is " + ssid + " join:" + join);
2333
2334        if (join && dev.isGroupLimit()) {
2335            if (DBG) logd("target device reaches group limit.");
2336
2337            // if the target group has reached the limit,
2338            // try group formation.
2339            join = false;
2340        } else if (join) {
2341            int netId = mGroups.getNetworkId(dev.deviceAddress, ssid);
2342            if (netId >= 0) {
2343                // Skip WPS and start 4way handshake immediately.
2344                if (!mWifiNative.p2pGroupAdd(netId)) {
2345                    return false;
2346                }
2347                return true;
2348            }
2349        }
2350
2351        if (!join && dev.isDeviceLimit()) {
2352            loge("target device reaches the device limit.");
2353            return false;
2354        }
2355
2356        if (!join && dev.isInvitationCapable()) {
2357            int netId = WifiP2pGroup.PERSISTENT_NET_ID;
2358            if (config.netId >= 0) {
2359                if (config.deviceAddress.equals(mGroups.getOwnerAddr(config.netId))) {
2360                    netId = config.netId;
2361                }
2362            } else {
2363                netId = mGroups.getNetworkId(dev.deviceAddress);
2364            }
2365            if (netId < 0) {
2366                netId = getNetworkIdFromClientList(dev.deviceAddress);
2367            }
2368            if (DBG) logd("netId related with " + dev.deviceAddress + " = " + netId);
2369            if (netId >= 0) {
2370                // Invoke the persistent group.
2371                if (mWifiNative.p2pReinvoke(netId, dev.deviceAddress)) {
2372                    // Save network id. It'll be used when an invitation result event is received.
2373                    config.netId = netId;
2374                    return true;
2375                } else {
2376                    loge("p2pReinvoke() failed, update networks");
2377                    updatePersistentNetworks(RELOAD);
2378                    return false;
2379                }
2380            }
2381        }
2382
2383        return false;
2384    }
2385
2386    /**
2387     * Return the network id of the group owner profile which has the p2p client with
2388     * the specified device address in it's client list.
2389     * If more than one persistent group of the same address is present in its client
2390     * lists, return the first one.
2391     *
2392     * @param deviceAddress p2p device address.
2393     * @return the network id. if not found, return -1.
2394     */
2395    private int getNetworkIdFromClientList(String deviceAddress) {
2396        if (deviceAddress == null) return -1;
2397
2398        Collection<WifiP2pGroup> groups = mGroups.getGroupList();
2399        for (WifiP2pGroup group : groups) {
2400            int netId = group.getNetworkId();
2401            String[] p2pClientList = getClientList(netId);
2402            if (p2pClientList == null) continue;
2403            for (String client : p2pClientList) {
2404                if (deviceAddress.equalsIgnoreCase(client)) {
2405                    return netId;
2406                }
2407            }
2408        }
2409        return -1;
2410    }
2411
2412    /**
2413     * Return p2p client list associated with the specified network id.
2414     * @param netId network id.
2415     * @return p2p client list. if not found, return null.
2416     */
2417    private String[] getClientList(int netId) {
2418        String p2pClients = mWifiNative.getNetworkVariable(netId, "p2p_client_list");
2419        if (p2pClients == null) {
2420            return null;
2421        }
2422        return p2pClients.split(" ");
2423    }
2424
2425    /**
2426     * Remove the specified p2p client from the specified profile.
2427     * @param netId network id of the profile.
2428     * @param addr p2p client address to be removed.
2429     * @param isRemovable if true, remove the specified profile if its client list becomes empty.
2430     * @return whether removing the specified p2p client is successful or not.
2431     */
2432    private boolean removeClientFromList(int netId, String addr, boolean isRemovable) {
2433        StringBuilder modifiedClientList =  new StringBuilder();
2434        String[] currentClientList = getClientList(netId);
2435        boolean isClientRemoved = false;
2436        if (currentClientList != null) {
2437            for (String client : currentClientList) {
2438                if (!client.equalsIgnoreCase(addr)) {
2439                    modifiedClientList.append(" ");
2440                    modifiedClientList.append(client);
2441                } else {
2442                    isClientRemoved = true;
2443                }
2444            }
2445        }
2446        if (modifiedClientList.length() == 0 && isRemovable) {
2447            // the client list is empty. so remove it.
2448            if (DBG) logd("Remove unknown network");
2449            mGroups.remove(netId);
2450            return true;
2451        }
2452
2453        if (!isClientRemoved) {
2454            // specified p2p client is not found. already removed.
2455            return false;
2456        }
2457
2458        if (DBG) logd("Modified client list: " + modifiedClientList);
2459        if (modifiedClientList.length() == 0) {
2460            modifiedClientList.append("\"\"");
2461        }
2462        mWifiNative.setNetworkVariable(netId,
2463                "p2p_client_list", modifiedClientList.toString());
2464        mWifiNative.saveConfig();
2465        return true;
2466    }
2467
2468    private void setWifiP2pInfoOnGroupFormation(InetAddress serverInetAddress) {
2469        mWifiP2pInfo.groupFormed = true;
2470        mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
2471        mWifiP2pInfo.groupOwnerAddress = serverInetAddress;
2472    }
2473
2474    private void resetWifiP2pInfo() {
2475        mWifiP2pInfo.groupFormed = false;
2476        mWifiP2pInfo.isGroupOwner = false;
2477        mWifiP2pInfo.groupOwnerAddress = null;
2478    }
2479
2480    private String getDeviceName(String deviceAddress) {
2481        WifiP2pDevice d = mPeers.get(deviceAddress);
2482        if (d != null) {
2483                return d.deviceName;
2484        }
2485        //Treat the address as name if there is no match
2486        return deviceAddress;
2487    }
2488
2489    private String getPersistedDeviceName() {
2490        String deviceName = Settings.Global.getString(mContext.getContentResolver(),
2491                Settings.Global.WIFI_P2P_DEVICE_NAME);
2492        if (deviceName == null) {
2493            /* We use the 4 digits of the ANDROID_ID to have a friendly
2494             * default that has low likelihood of collision with a peer */
2495            String id = Settings.Secure.getString(mContext.getContentResolver(),
2496                    Settings.Secure.ANDROID_ID);
2497            return "Android_" + id.substring(0,4);
2498        }
2499        return deviceName;
2500    }
2501
2502    private boolean setAndPersistDeviceName(String devName) {
2503        if (devName == null) return false;
2504
2505        if (!mWifiNative.setDeviceName(devName)) {
2506            loge("Failed to set device name " + devName);
2507            return false;
2508        }
2509
2510        mThisDevice.deviceName = devName;
2511        mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
2512
2513        Settings.Global.putString(mContext.getContentResolver(),
2514                Settings.Global.WIFI_P2P_DEVICE_NAME, devName);
2515        sendThisDeviceChangedBroadcast();
2516        return true;
2517    }
2518
2519    private boolean setWfdInfo(WifiP2pWfdInfo wfdInfo) {
2520        boolean success;
2521
2522        if (!wfdInfo.isWfdEnabled()) {
2523            success = mWifiNative.setWfdEnable(false);
2524        } else {
2525            success =
2526                mWifiNative.setWfdEnable(true)
2527                && mWifiNative.setWfdDeviceInfo(wfdInfo.getDeviceInfoHex());
2528        }
2529
2530        if (!success) {
2531            loge("Failed to set wfd properties");
2532            return false;
2533        }
2534
2535        mThisDevice.wfdInfo = wfdInfo;
2536        sendThisDeviceChangedBroadcast();
2537        return true;
2538    }
2539
2540    private void initializeP2pSettings() {
2541        mWifiNative.setPersistentReconnect(true);
2542        mThisDevice.deviceName = getPersistedDeviceName();
2543        mWifiNative.setDeviceName(mThisDevice.deviceName);
2544        // DIRECT-XY-DEVICENAME (XY is randomly generated)
2545        mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
2546        mWifiNative.setDeviceType(mThisDevice.primaryDeviceType);
2547        // Supplicant defaults to using virtual display with display
2548        // which refers to a remote display. Use physical_display
2549        mWifiNative.setConfigMethods("virtual_push_button physical_display keypad");
2550        // STA has higher priority over P2P
2551        mWifiNative.setConcurrencyPriority("sta");
2552
2553        mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
2554        updateThisDevice(WifiP2pDevice.AVAILABLE);
2555        if (DBG) logd("DeviceAddress: " + mThisDevice.deviceAddress);
2556
2557        mClientInfoList.clear();
2558        mWifiNative.p2pFlush();
2559        mWifiNative.p2pServiceFlush();
2560        mServiceTransactionId = 0;
2561        mServiceDiscReqId = null;
2562
2563        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
2564                Settings.Global.WIFI_COUNTRY_CODE);
2565        if (countryCode != null && !countryCode.isEmpty()) {
2566            mP2pStateMachine.sendMessage(SET_COUNTRY_CODE, countryCode);
2567        }
2568
2569        updatePersistentNetworks(RELOAD);
2570    }
2571
2572    private void updateThisDevice(int status) {
2573        mThisDevice.status = status;
2574        sendThisDeviceChangedBroadcast();
2575    }
2576
2577    private void handleGroupCreationFailure() {
2578        resetWifiP2pInfo();
2579        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.FAILED, null, null);
2580        sendP2pConnectionChangedBroadcast();
2581
2582        // Remove only the peer we failed to connect to so that other devices discovered
2583        // that have not timed out still remain in list for connection
2584        boolean peersChanged = mPeers.remove(mPeersLostDuringConnection);
2585        if (mPeers.remove(mSavedPeerConfig.deviceAddress) != null) {
2586            peersChanged = true;
2587        }
2588        if (peersChanged) {
2589            sendPeersChangedBroadcast();
2590        }
2591
2592        mPeersLostDuringConnection.clear();
2593        mServiceDiscReqId = null;
2594        sendMessage(WifiP2pManager.DISCOVER_PEERS);
2595    }
2596
2597    private void handleGroupRemoved() {
2598        if (mGroup.isGroupOwner()) {
2599            stopDhcpServer(mGroup.getInterface());
2600        } else {
2601            if (DBG) logd("stop DHCP client");
2602            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
2603            mDhcpStateMachine.doQuit();
2604            mDhcpStateMachine = null;
2605        }
2606
2607        try {
2608            mNwService.clearInterfaceAddresses(mGroup.getInterface());
2609        } catch (Exception e) {
2610            loge("Failed to clear addresses " + e);
2611        }
2612        NetworkUtils.resetConnections(mGroup.getInterface(), NetworkUtils.RESET_ALL_ADDRESSES);
2613
2614        // Clear any timeout that was set. This is essential for devices
2615        // that reuse the main p2p interface for a created group.
2616        mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
2617
2618        boolean peersChanged = false;
2619        // Remove only peers part of the group, so that other devices discovered
2620        // that have not timed out still remain in list for connection
2621        for (WifiP2pDevice d : mGroup.getClientList()) {
2622            if (mPeers.remove(d)) peersChanged = true;
2623        }
2624        if (mPeers.remove(mGroup.getOwner())) peersChanged = true;
2625        if (mPeers.remove(mPeersLostDuringConnection)) peersChanged = true;
2626        if (peersChanged) {
2627            sendPeersChangedBroadcast();
2628        }
2629
2630        mGroup = null;
2631        mPeersLostDuringConnection.clear();
2632        mServiceDiscReqId = null;
2633
2634        if (mTempoarilyDisconnectedWifi) {
2635            mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 0);
2636            mTempoarilyDisconnectedWifi = false;
2637        }
2638   }
2639
2640    //State machine initiated requests can have replyTo set to null indicating
2641    //there are no recipients, we ignore those reply actions
2642    private void replyToMessage(Message msg, int what) {
2643        if (msg.replyTo == null) return;
2644        Message dstMsg = obtainMessage(msg);
2645        dstMsg.what = what;
2646        mReplyChannel.replyToMessage(msg, dstMsg);
2647    }
2648
2649    private void replyToMessage(Message msg, int what, int arg1) {
2650        if (msg.replyTo == null) return;
2651        Message dstMsg = obtainMessage(msg);
2652        dstMsg.what = what;
2653        dstMsg.arg1 = arg1;
2654        mReplyChannel.replyToMessage(msg, dstMsg);
2655    }
2656
2657    private void replyToMessage(Message msg, int what, Object obj) {
2658        if (msg.replyTo == null) return;
2659        Message dstMsg = obtainMessage(msg);
2660        dstMsg.what = what;
2661        dstMsg.obj = obj;
2662        mReplyChannel.replyToMessage(msg, dstMsg);
2663    }
2664
2665    /* arg2 on the source message has a hash code that needs to be retained in replies
2666     * see WifiP2pManager for details */
2667    private Message obtainMessage(Message srcMsg) {
2668        Message msg = Message.obtain();
2669        msg.arg2 = srcMsg.arg2;
2670        return msg;
2671    }
2672
2673    @Override
2674    protected void logd(String s) {
2675        Slog.d(TAG, s);
2676    }
2677
2678    @Override
2679    protected void loge(String s) {
2680        Slog.e(TAG, s);
2681    }
2682
2683    /**
2684     * Update service discovery request to wpa_supplicant.
2685     */
2686    private boolean updateSupplicantServiceRequest() {
2687        clearSupplicantServiceRequest();
2688
2689        StringBuffer sb = new StringBuffer();
2690        for (ClientInfo c: mClientInfoList.values()) {
2691            int key;
2692            WifiP2pServiceRequest req;
2693            for (int i=0; i < c.mReqList.size(); i++) {
2694                req = c.mReqList.valueAt(i);
2695                if (req != null) {
2696                    sb.append(req.getSupplicantQuery());
2697                }
2698            }
2699        }
2700
2701        if (sb.length() == 0) {
2702            return false;
2703        }
2704
2705        mServiceDiscReqId = mWifiNative.p2pServDiscReq("00:00:00:00:00:00", sb.toString());
2706        if (mServiceDiscReqId == null) {
2707            return false;
2708        }
2709        return true;
2710    }
2711
2712    /**
2713     * Clear service discovery request in wpa_supplicant
2714     */
2715    private void clearSupplicantServiceRequest() {
2716        if (mServiceDiscReqId == null) return;
2717
2718        mWifiNative.p2pServDiscCancelReq(mServiceDiscReqId);
2719        mServiceDiscReqId = null;
2720    }
2721
2722    /* TODO: We could track individual service adds separately and avoid
2723     * having to do update all service requests on every new request
2724     */
2725    private boolean addServiceRequest(Messenger m, WifiP2pServiceRequest req) {
2726        clearClientDeadChannels();
2727        ClientInfo clientInfo = getClientInfo(m, true);
2728        if (clientInfo == null) {
2729            return false;
2730        }
2731
2732        ++mServiceTransactionId;
2733        //The Wi-Fi p2p spec says transaction id should be non-zero
2734        if (mServiceTransactionId == 0) ++mServiceTransactionId;
2735        req.setTransactionId(mServiceTransactionId);
2736        clientInfo.mReqList.put(mServiceTransactionId, req);
2737
2738        if (mServiceDiscReqId == null) {
2739            return true;
2740        }
2741
2742        return updateSupplicantServiceRequest();
2743    }
2744
2745    private void removeServiceRequest(Messenger m, WifiP2pServiceRequest req) {
2746        ClientInfo clientInfo = getClientInfo(m, false);
2747        if (clientInfo == null) {
2748            return;
2749        }
2750
2751        //Application does not have transaction id information
2752        //go through stored requests to remove
2753        boolean removed = false;
2754        for (int i=0; i<clientInfo.mReqList.size(); i++) {
2755            if (req.equals(clientInfo.mReqList.valueAt(i))) {
2756                removed = true;
2757                clientInfo.mReqList.removeAt(i);
2758                break;
2759            }
2760        }
2761
2762        if (!removed) return;
2763
2764        if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
2765            if (DBG) logd("remove client information from framework");
2766            mClientInfoList.remove(clientInfo.mMessenger);
2767        }
2768
2769        if (mServiceDiscReqId == null) {
2770            return;
2771        }
2772
2773        updateSupplicantServiceRequest();
2774    }
2775
2776    private void clearServiceRequests(Messenger m) {
2777
2778        ClientInfo clientInfo = getClientInfo(m, false);
2779        if (clientInfo == null) {
2780            return;
2781        }
2782
2783        if (clientInfo.mReqList.size() == 0) {
2784            return;
2785        }
2786
2787        clientInfo.mReqList.clear();
2788
2789        if (clientInfo.mServList.size() == 0) {
2790            if (DBG) logd("remove channel information from framework");
2791            mClientInfoList.remove(clientInfo.mMessenger);
2792        }
2793
2794        if (mServiceDiscReqId == null) {
2795            return;
2796        }
2797
2798        updateSupplicantServiceRequest();
2799    }
2800
2801    private boolean addLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
2802        clearClientDeadChannels();
2803        ClientInfo clientInfo = getClientInfo(m, true);
2804        if (clientInfo == null) {
2805            return false;
2806        }
2807
2808        if (!clientInfo.mServList.add(servInfo)) {
2809            return false;
2810        }
2811
2812        if (!mWifiNative.p2pServiceAdd(servInfo)) {
2813            clientInfo.mServList.remove(servInfo);
2814            return false;
2815        }
2816
2817        return true;
2818    }
2819
2820    private void removeLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
2821        ClientInfo clientInfo = getClientInfo(m, false);
2822        if (clientInfo == null) {
2823            return;
2824        }
2825
2826        mWifiNative.p2pServiceDel(servInfo);
2827
2828        clientInfo.mServList.remove(servInfo);
2829        if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
2830            if (DBG) logd("remove client information from framework");
2831            mClientInfoList.remove(clientInfo.mMessenger);
2832        }
2833    }
2834
2835    private void clearLocalServices(Messenger m) {
2836        ClientInfo clientInfo = getClientInfo(m, false);
2837        if (clientInfo == null) {
2838            return;
2839        }
2840
2841        for (WifiP2pServiceInfo servInfo: clientInfo.mServList) {
2842            mWifiNative.p2pServiceDel(servInfo);
2843        }
2844
2845        clientInfo.mServList.clear();
2846        if (clientInfo.mReqList.size() == 0) {
2847            if (DBG) logd("remove client information from framework");
2848            mClientInfoList.remove(clientInfo.mMessenger);
2849        }
2850    }
2851
2852    private void clearClientInfo(Messenger m) {
2853        clearLocalServices(m);
2854        clearServiceRequests(m);
2855    }
2856
2857    /**
2858     * Send the service response to the WifiP2pManager.Channel.
2859     *
2860     * @param resp
2861     */
2862    private void sendServiceResponse(WifiP2pServiceResponse resp) {
2863        for (ClientInfo c : mClientInfoList.values()) {
2864            WifiP2pServiceRequest req = c.mReqList.get(resp.getTransactionId());
2865            if (req != null) {
2866                Message msg = Message.obtain();
2867                msg.what = WifiP2pManager.RESPONSE_SERVICE;
2868                msg.arg1 = 0;
2869                msg.arg2 = 0;
2870                msg.obj = resp;
2871                try {
2872                    c.mMessenger.send(msg);
2873                } catch (RemoteException e) {
2874                    if (DBG) logd("detect dead channel");
2875                    clearClientInfo(c.mMessenger);
2876                    return;
2877                }
2878            }
2879        }
2880    }
2881
2882    /**
2883     * We dont get notifications of clients that have gone away.
2884     * We detect this actively when services are added and throw
2885     * them away.
2886     *
2887     * TODO: This can be done better with full async channels.
2888     */
2889    private void clearClientDeadChannels() {
2890        ArrayList<Messenger> deadClients = new ArrayList<Messenger>();
2891
2892        for (ClientInfo c : mClientInfoList.values()) {
2893            Message msg = Message.obtain();
2894            msg.what = WifiP2pManager.PING;
2895            msg.arg1 = 0;
2896            msg.arg2 = 0;
2897            msg.obj = null;
2898            try {
2899                c.mMessenger.send(msg);
2900            } catch (RemoteException e) {
2901                if (DBG) logd("detect dead channel");
2902                deadClients.add(c.mMessenger);
2903            }
2904        }
2905
2906        for (Messenger m : deadClients) {
2907            clearClientInfo(m);
2908        }
2909    }
2910
2911    /**
2912     * Return the specified ClientInfo.
2913     * @param m Messenger
2914     * @param createIfNotExist if true and the specified channel info does not exist,
2915     * create new client info.
2916     * @return the specified ClientInfo.
2917     */
2918    private ClientInfo getClientInfo(Messenger m, boolean createIfNotExist) {
2919        ClientInfo clientInfo = mClientInfoList.get(m);
2920
2921        if (clientInfo == null && createIfNotExist) {
2922            if (DBG) logd("add a new client");
2923            clientInfo = new ClientInfo(m);
2924            mClientInfoList.put(m, clientInfo);
2925        }
2926
2927        return clientInfo;
2928    }
2929
2930    }
2931
2932    /**
2933     * Information about a particular client and we track the service discovery requests
2934     * and the local services registered by the client.
2935     */
2936    private class ClientInfo {
2937
2938        /*
2939         * A reference to WifiP2pManager.Channel handler.
2940         * The response of this request is notified to WifiP2pManager.Channel handler
2941         */
2942        private Messenger mMessenger;
2943
2944        /*
2945         * A service discovery request list.
2946         */
2947        private SparseArray<WifiP2pServiceRequest> mReqList;
2948
2949        /*
2950         * A local service information list.
2951         */
2952        private List<WifiP2pServiceInfo> mServList;
2953
2954        private ClientInfo(Messenger m) {
2955            mMessenger = m;
2956            mReqList = new SparseArray();
2957            mServList = new ArrayList<WifiP2pServiceInfo>();
2958        }
2959    }
2960}
2961