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