WifiP2pService.java revision 9cb980422ac53b81d6ad15242b0de35b5f3ce13c
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.nsd.WifiP2pServiceInfo;
49import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
50import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
51import android.os.Binder;
52import android.os.Bundle;
53import android.os.IBinder;
54import android.os.INetworkManagementService;
55import android.os.Handler;
56import android.os.HandlerThread;
57import android.os.Message;
58import android.os.Messenger;
59import android.os.Parcel;
60import android.os.Parcelable;
61import android.os.RemoteException;
62import android.os.ServiceManager;
63import android.os.SystemProperties;
64import android.os.Parcelable.Creator;
65import android.provider.Settings;
66import android.text.TextUtils;
67import android.util.Slog;
68import android.util.SparseArray;
69import android.view.LayoutInflater;
70import android.view.View;
71import android.view.ViewGroup;
72import android.view.WindowManager;
73import android.widget.EditText;
74import android.widget.TextView;
75
76import com.android.internal.R;
77import com.android.internal.telephony.TelephonyIntents;
78import com.android.internal.util.AsyncChannel;
79import com.android.internal.util.Protocol;
80import com.android.internal.util.State;
81import com.android.internal.util.StateMachine;
82
83import java.io.FileDescriptor;
84import java.io.PrintWriter;
85import java.util.ArrayList;
86import java.util.Collection;
87import java.util.HashMap;
88import java.util.List;
89
90/**
91 * WifiP2pService includes a state machine to perform Wi-Fi p2p operations. Applications
92 * communicate with this service to issue device discovery and connectivity requests
93 * through the WifiP2pManager interface. The state machine communicates with the wifi
94 * driver through wpa_supplicant and handles the event responses through WifiMonitor.
95 *
96 * Note that the term Wifi when used without a p2p suffix refers to the client mode
97 * of Wifi operation
98 * @hide
99 */
100public class WifiP2pService extends IWifiP2pManager.Stub {
101    private static final String TAG = "WifiP2pService";
102    private static final boolean DBG = false;
103    private static final String NETWORKTYPE = "WIFI_P2P";
104
105    private Context mContext;
106    private String mInterface;
107    private Notification mNotification;
108
109    INetworkManagementService mNwService;
110    private DhcpStateMachine mDhcpStateMachine;
111
112    private ActivityManager mActivityMgr;
113
114    private P2pStateMachine mP2pStateMachine;
115    private AsyncChannel mReplyChannel = new AsyncChannel();
116    private AsyncChannel mWifiChannel;
117
118    private static final Boolean JOIN_GROUP = true;
119    private static final Boolean FORM_GROUP = false;
120
121    /* Two minutes comes from the wpa_supplicant setting */
122    private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
123    private static int mGroupCreatingTimeoutIndex = 0;
124
125    /* Set a two minute discover timeout to avoid STA scans from being blocked */
126    private static final int DISCOVER_TIMEOUT_S = 120;
127
128    /* Idle time after a peer is gone when the group is torn down */
129    private static final int GROUP_IDLE_TIME_S = 2;
130
131    /**
132     * Delay between restarts upon failure to setup connection with supplicant
133     */
134    private static final int P2P_RESTART_INTERVAL_MSECS = 5000;
135
136    /**
137     * Number of times we attempt to restart p2p
138     */
139    private static final int P2P_RESTART_TRIES = 5;
140
141    private int mP2pRestartCount = 0;
142
143    private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
144
145    /* Delayed message to timeout group creation */
146    public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 1;
147
148    /* User accepted a peer request */
149    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 2;
150    /* User rejected a peer request */
151    private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 3;
152
153    private final boolean mP2pSupported;
154
155    private WifiP2pDevice mThisDevice = new WifiP2pDevice();
156
157    /* When a group has been explicitly created by an app, we persist the group
158     * even after all clients have been disconnected until an explicit remove
159     * is invoked */
160    private boolean mAutonomousGroup;
161
162    /* Invitation to join an existing p2p group */
163    private boolean mJoinExistingGroup;
164
165    /* Track whether we are in p2p discovery. This is used to avoid sending duplicate
166     * broadcasts
167     */
168    private boolean mDiscoveryStarted;
169
170    private NetworkInfo mNetworkInfo;
171
172    /* The transaction Id of service discovery request */
173    private byte mServiceTransactionId = 0;
174
175    /* Service discovery request ID of wpa_supplicant.
176     * null means it's not set yet. */
177    private String mServiceDiscReqId;
178
179    /* clients(application) information list. */
180    private HashMap<Messenger, ClientInfo> mClientInfoList = new HashMap<Messenger, ClientInfo>();
181
182    /* The foreground application's messenger.
183     * The connection request is notified only to foreground application  */
184    private Messenger mForegroundAppMessenger;
185
186    /* the package name of foreground application. */
187    private String mForegroundAppPkgName;
188
189    /* Is chosen as a unique range to avoid conflict with
190       the range defined in Tethering.java */
191    private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"};
192    private static final String SERVER_ADDRESS = "192.168.49.1";
193
194    public WifiP2pService(Context context) {
195        mContext = context;
196
197        //STOPSHIP: get this from native side
198        mInterface = "p2p0";
199        mActivityMgr = (ActivityManager)context.getSystemService(Activity.ACTIVITY_SERVICE);
200
201        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
202
203        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
204                PackageManager.FEATURE_WIFI_DIRECT);
205
206        mThisDevice.primaryDeviceType = mContext.getResources().getString(
207                com.android.internal.R.string.config_wifi_p2p_device_type);
208
209        mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
210        mP2pStateMachine.start();
211    }
212
213    public void connectivityServiceReady() {
214        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
215        mNwService = INetworkManagementService.Stub.asInterface(b);
216    }
217
218    private void enforceAccessPermission() {
219        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
220                "WifiP2pService");
221    }
222
223    private void enforceChangePermission() {
224        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
225                "WifiP2pService");
226    }
227
228    /**
229     * Get a reference to handler. This is used by a client to establish
230     * an AsyncChannel communication with WifiP2pService
231     */
232    public Messenger getMessenger() {
233        enforceAccessPermission();
234        enforceChangePermission();
235        return new Messenger(mP2pStateMachine.getHandler());
236    }
237
238    @Override
239    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
240        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
241                != PackageManager.PERMISSION_GRANTED) {
242            pw.println("Permission Denial: can't dump WifiP2pService from from pid="
243                    + Binder.getCallingPid()
244                    + ", uid=" + Binder.getCallingUid());
245            return;
246        }
247    }
248
249
250    /**
251     * Handles interaction with WifiStateMachine
252     */
253    private class P2pStateMachine extends StateMachine {
254
255        private DefaultState mDefaultState = new DefaultState();
256        private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
257        private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
258        private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
259        private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
260        private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
261        // Inactive is when p2p is enabled with no connectivity
262        private InactiveState mInactiveState = new InactiveState();
263        private GroupCreatingState mGroupCreatingState = new GroupCreatingState();
264        private UserAuthorizingInvitationState mUserAuthorizingInvitationState
265                = new UserAuthorizingInvitationState();
266        private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState();
267        private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
268
269        private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
270        private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState();
271
272        private WifiNative mWifiNative = new WifiNative(mInterface);
273        private WifiMonitor mWifiMonitor = new WifiMonitor(this, mWifiNative);
274
275        private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
276        private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
277        private WifiP2pGroup mGroup;
278
279        // Saved WifiP2pConfig for a peer connection
280        private WifiP2pConfig mSavedPeerConfig;
281
282        // Saved WifiP2pGroup from invitation request
283        private WifiP2pGroup mSavedP2pGroup;
284
285        // Saved WifiP2pDevice from provisioning request
286        private WifiP2pDevice mSavedProvDiscDevice;
287
288        P2pStateMachine(String name, boolean p2pSupported) {
289            super(name);
290
291            addState(mDefaultState);
292                addState(mP2pNotSupportedState, mDefaultState);
293                addState(mP2pDisablingState, mDefaultState);
294                addState(mP2pDisabledState, mDefaultState);
295                addState(mP2pEnablingState, mDefaultState);
296                addState(mP2pEnabledState, mDefaultState);
297                    addState(mInactiveState, mP2pEnabledState);
298                    addState(mGroupCreatingState, mP2pEnabledState);
299                        addState(mUserAuthorizingInvitationState, mGroupCreatingState);
300                        addState(mProvisionDiscoveryState, mGroupCreatingState);
301                        addState(mGroupNegotiationState, mGroupCreatingState);
302                    addState(mGroupCreatedState, mP2pEnabledState);
303                        addState(mUserAuthorizingJoinState, mGroupCreatedState);
304
305            if (p2pSupported) {
306                setInitialState(mP2pDisabledState);
307            } else {
308                setInitialState(mP2pNotSupportedState);
309            }
310        }
311
312    class DefaultState extends State {
313        @Override
314        public boolean processMessage(Message message) {
315            if (DBG) logd(getName() + message.toString());
316            switch (message.what) {
317                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
318                    if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
319                        if (DBG) logd("Full connection with WifiStateMachine established");
320                        mWifiChannel = (AsyncChannel) message.obj;
321                    } else {
322                        loge("Full connection failure, error = " + message.arg1);
323                        mWifiChannel = null;
324                    }
325                    break;
326
327                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
328                    if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
329                        loge("Send failed, client connection lost");
330                    } else {
331                        loge("Client connection lost with reason: " + message.arg1);
332                    }
333                    mWifiChannel = null;
334                    break;
335
336                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
337                    AsyncChannel ac = new AsyncChannel();
338                    ac.connect(mContext, getHandler(), message.replyTo);
339                    break;
340                case WifiP2pManager.DISCOVER_PEERS:
341                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
342                            WifiP2pManager.BUSY);
343                    break;
344                case WifiP2pManager.STOP_DISCOVERY:
345                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
346                            WifiP2pManager.BUSY);
347                    break;
348                case WifiP2pManager.DISCOVER_SERVICES:
349                    replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
350                            WifiP2pManager.BUSY);
351                    break;
352                case WifiP2pManager.CONNECT:
353                    replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
354                            WifiP2pManager.BUSY);
355                    break;
356                case WifiP2pManager.CANCEL_CONNECT:
357                    replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
358                            WifiP2pManager.BUSY);
359                    break;
360                case WifiP2pManager.CREATE_GROUP:
361                    replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
362                            WifiP2pManager.BUSY);
363                    break;
364                case WifiP2pManager.REMOVE_GROUP:
365                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
366                            WifiP2pManager.BUSY);
367                    break;
368                case WifiP2pManager.ADD_LOCAL_SERVICE:
369                    replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
370                            WifiP2pManager.BUSY);
371                    break;
372                case WifiP2pManager.REMOVE_LOCAL_SERVICE:
373                    replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
374                            WifiP2pManager.BUSY);
375                    break;
376                case WifiP2pManager.CLEAR_LOCAL_SERVICES:
377                    replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
378                            WifiP2pManager.BUSY);
379                    break;
380                case WifiP2pManager.ADD_SERVICE_REQUEST:
381                    replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
382                            WifiP2pManager.BUSY);
383                    break;
384                case WifiP2pManager.REMOVE_SERVICE_REQUEST:
385                    replyToMessage(message,
386                            WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
387                            WifiP2pManager.BUSY);
388                    break;
389                case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
390                    replyToMessage(message,
391                            WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
392                            WifiP2pManager.BUSY);
393                    break;
394                case WifiP2pManager.SET_DEVICE_NAME:
395                    replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
396                            WifiP2pManager.BUSY);
397                    break;
398                case WifiP2pManager.REQUEST_PEERS:
399                    replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
400                    break;
401                case WifiP2pManager.REQUEST_CONNECTION_INFO:
402                    replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, mWifiP2pInfo);
403                    break;
404                case WifiP2pManager.REQUEST_GROUP_INFO:
405                    replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
406                    break;
407                case WifiP2pManager.SET_DIALOG_LISTENER:
408                    String appPkgName = (String)message.getData().getString(
409                            WifiP2pManager.APP_PKG_BUNDLE_KEY);
410                    boolean isReset = message.getData().getBoolean(
411                            WifiP2pManager.RESET_DIALOG_LISTENER_BUNDLE_KEY);
412                    if (setDialogListenerApp(message.replyTo, appPkgName, isReset)) {
413                        replyToMessage(message, WifiP2pManager.DIALOG_LISTENER_ATTACHED);
414                    } else {
415                        replyToMessage(message, WifiP2pManager.DIALOG_LISTENER_DETACHED,
416                                WifiP2pManager.NOT_IN_FOREGROUND);
417                    }
418                    break;
419                    // Ignore
420                case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
421                case WifiMonitor.SCAN_RESULTS_EVENT:
422                case WifiMonitor.SUP_CONNECTION_EVENT:
423                case WifiMonitor.SUP_DISCONNECTION_EVENT:
424                case WifiMonitor.NETWORK_CONNECTION_EVENT:
425                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
426                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
427                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
428                case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
429                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
430                case WifiMonitor.P2P_FIND_STOPPED_EVENT:
431                case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
432                case WifiStateMachine.CMD_ENABLE_P2P:
433                case WifiStateMachine.CMD_DISABLE_P2P:
434                case PEER_CONNECTION_USER_ACCEPT:
435                case PEER_CONNECTION_USER_REJECT:
436                case GROUP_CREATING_TIMED_OUT:
437                    break;
438                    /* unexpected group created, remove */
439                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
440                    mGroup = (WifiP2pGroup) message.obj;
441                    loge("Unexpected group creation, remove " + mGroup);
442                    mWifiNative.p2pGroupRemove(mGroup.getInterface());
443                    break;
444                // A group formation failure is always followed by
445                // a group removed event. Flushing things at group formation
446                // failure causes supplicant issues. Ignore right now.
447                case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
448                    break;
449                default:
450                    loge("Unhandled message " + message);
451                    return NOT_HANDLED;
452            }
453            return HANDLED;
454        }
455    }
456
457    class P2pNotSupportedState extends State {
458        @Override
459        public boolean processMessage(Message message) {
460            switch (message.what) {
461               case WifiP2pManager.DISCOVER_PEERS:
462                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
463                            WifiP2pManager.P2P_UNSUPPORTED);
464                    break;
465                case WifiP2pManager.STOP_DISCOVERY:
466                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
467                            WifiP2pManager.P2P_UNSUPPORTED);
468                    break;
469                case WifiP2pManager.DISCOVER_SERVICES:
470                    replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
471                            WifiP2pManager.P2P_UNSUPPORTED);
472                    break;
473                case WifiP2pManager.CONNECT:
474                    replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
475                            WifiP2pManager.P2P_UNSUPPORTED);
476                    break;
477                case WifiP2pManager.CANCEL_CONNECT:
478                    replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
479                            WifiP2pManager.P2P_UNSUPPORTED);
480                    break;
481               case WifiP2pManager.CREATE_GROUP:
482                    replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
483                            WifiP2pManager.P2P_UNSUPPORTED);
484                    break;
485                case WifiP2pManager.REMOVE_GROUP:
486                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
487                            WifiP2pManager.P2P_UNSUPPORTED);
488                    break;
489                case WifiP2pManager.ADD_LOCAL_SERVICE:
490                    replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
491                            WifiP2pManager.P2P_UNSUPPORTED);
492                    break;
493                case WifiP2pManager.SET_DIALOG_LISTENER:
494                    replyToMessage(message, WifiP2pManager.DIALOG_LISTENER_DETACHED,
495                            WifiP2pManager.P2P_UNSUPPORTED);
496                    break;
497                case WifiP2pManager.REMOVE_LOCAL_SERVICE:
498                    replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
499                            WifiP2pManager.P2P_UNSUPPORTED);
500                    break;
501                case WifiP2pManager.CLEAR_LOCAL_SERVICES:
502                    replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
503                            WifiP2pManager.P2P_UNSUPPORTED);
504                    break;
505                case WifiP2pManager.ADD_SERVICE_REQUEST:
506                    replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
507                            WifiP2pManager.P2P_UNSUPPORTED);
508                    break;
509                case WifiP2pManager.REMOVE_SERVICE_REQUEST:
510                    replyToMessage(message,
511                            WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
512                            WifiP2pManager.P2P_UNSUPPORTED);
513                    break;
514                case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
515                    replyToMessage(message,
516                            WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
517                            WifiP2pManager.P2P_UNSUPPORTED);
518                    break;
519                case WifiP2pManager.SET_DEVICE_NAME:
520                    replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
521                            WifiP2pManager.P2P_UNSUPPORTED);
522                    break;
523               default:
524                    return NOT_HANDLED;
525            }
526            return HANDLED;
527        }
528    }
529
530    class P2pDisablingState extends State {
531        @Override
532        public boolean processMessage(Message message) {
533            if (DBG) logd(getName() + message.toString());
534            switch (message.what) {
535                case WifiMonitor.SUP_DISCONNECTION_EVENT:
536                    if (DBG) logd("p2p socket connection lost");
537                    transitionTo(mP2pDisabledState);
538                    break;
539                case WifiStateMachine.CMD_ENABLE_P2P:
540                case WifiStateMachine.CMD_DISABLE_P2P:
541                    deferMessage(message);
542                    break;
543                default:
544                    return NOT_HANDLED;
545            }
546            return HANDLED;
547        }
548    }
549
550    class P2pDisabledState extends State {
551       @Override
552        public void enter() {
553            if (DBG) logd(getName());
554        }
555
556        @Override
557        public boolean processMessage(Message message) {
558            if (DBG) logd(getName() + message.toString());
559            switch (message.what) {
560                case WifiStateMachine.CMD_ENABLE_P2P:
561                    try {
562                        mNwService.setInterfaceUp(mInterface);
563                    } catch (RemoteException re) {
564                        loge("Unable to change interface settings: " + re);
565                    } catch (IllegalStateException ie) {
566                        loge("Unable to change interface settings: " + ie);
567                    }
568                    mWifiMonitor.startMonitoring();
569                    transitionTo(mP2pEnablingState);
570                    break;
571                case WifiStateMachine.CMD_DISABLE_P2P:
572                    //Nothing to do
573                    break;
574                default:
575                    return NOT_HANDLED;
576            }
577            return HANDLED;
578        }
579    }
580
581    class P2pEnablingState extends State {
582        @Override
583        public void enter() {
584            if (DBG) logd(getName());
585        }
586
587        @Override
588        public boolean processMessage(Message message) {
589            if (DBG) logd(getName() + message.toString());
590            switch (message.what) {
591                case WifiMonitor.SUP_CONNECTION_EVENT:
592                    if (DBG) logd("P2p socket connection successful");
593                    transitionTo(mInactiveState);
594                    break;
595                case WifiMonitor.SUP_DISCONNECTION_EVENT:
596                    loge("P2p socket connection failed");
597                    transitionTo(mP2pDisabledState);
598                    break;
599                case WifiStateMachine.CMD_ENABLE_P2P:
600                case WifiStateMachine.CMD_DISABLE_P2P:
601                    deferMessage(message);
602                    break;
603                default:
604                    return NOT_HANDLED;
605            }
606            return HANDLED;
607        }
608    }
609
610    class P2pEnabledState extends State {
611        @Override
612        public void enter() {
613            if (DBG) logd(getName());
614            sendP2pStateChangedBroadcast(true);
615            mNetworkInfo.setIsAvailable(true);
616            sendP2pConnectionChangedBroadcast();
617            initializeP2pSettings();
618        }
619
620        @Override
621        public boolean processMessage(Message message) {
622            if (DBG) logd(getName() + message.toString());
623            switch (message.what) {
624                case WifiStateMachine.CMD_ENABLE_P2P:
625                    //Nothing to do
626                    break;
627                case WifiStateMachine.CMD_DISABLE_P2P:
628                    if (mPeers.clear()) sendP2pPeersChangedBroadcast();
629                    mWifiNative.closeSupplicantConnection();
630                    transitionTo(mP2pDisablingState);
631                    break;
632                case WifiP2pManager.SET_DEVICE_NAME:
633                    WifiP2pDevice d = (WifiP2pDevice) message.obj;
634                    if (d != null && setAndPersistDeviceName(d.deviceName)) {
635                        if (DBG) logd("set device name " + d.deviceName);
636                        replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED);
637                    } else {
638                        replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
639                                WifiP2pManager.ERROR);
640                    }
641                    break;
642                case WifiP2pManager.DISCOVER_PEERS:
643                    // do not send service discovery request while normal find operation.
644                    clearSupplicantServiceRequest();
645                    if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
646                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
647                        sendP2pDiscoveryChangedBroadcast(true);
648                    } else {
649                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
650                                WifiP2pManager.ERROR);
651                    }
652                    break;
653                case WifiMonitor.P2P_FIND_STOPPED_EVENT:
654                    sendP2pDiscoveryChangedBroadcast(false);
655                    break;
656                case WifiP2pManager.STOP_DISCOVERY:
657                    if (mWifiNative.p2pStopFind()) {
658                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
659                    } else {
660                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
661                                WifiP2pManager.ERROR);
662                    }
663                    break;
664                case WifiP2pManager.DISCOVER_SERVICES:
665                    if (DBG) logd(getName() + " discover services");
666                    if (!updateSupplicantServiceRequest()) {
667                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
668                                WifiP2pManager.NO_SERVICE_REQUESTS);
669                        break;
670                    }
671                    if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
672                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED);
673                    } else {
674                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
675                                WifiP2pManager.ERROR);
676                    }
677                    break;
678                case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
679                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
680                    if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
681                    mPeers.update(device);
682                    sendP2pPeersChangedBroadcast();
683                    break;
684                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
685                    device = (WifiP2pDevice) message.obj;
686                    if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
687                    break;
688               case WifiP2pManager.ADD_LOCAL_SERVICE:
689                    if (DBG) logd(getName() + " add service");
690                    WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo)message.obj;
691                    if (addLocalService(message.replyTo, servInfo)) {
692                        replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_SUCCEEDED);
693                    } else {
694                        replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED);
695                    }
696                    break;
697                case WifiP2pManager.REMOVE_LOCAL_SERVICE:
698                    if (DBG) logd(getName() + " remove service");
699                    servInfo = (WifiP2pServiceInfo)message.obj;
700                    removeLocalService(message.replyTo, servInfo);
701                    replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_SUCCEEDED);
702                    break;
703                case WifiP2pManager.CLEAR_LOCAL_SERVICES:
704                    if (DBG) logd(getName() + " clear service");
705                    clearLocalServices(message.replyTo);
706                    break;
707                case WifiP2pManager.ADD_SERVICE_REQUEST:
708                    if (DBG) logd(getName() + " add service request");
709                    if (!addServiceRequest(message.replyTo, (WifiP2pServiceRequest)message.obj)) {
710                        replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED);
711                        break;
712                    }
713                    replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_SUCCEEDED);
714                    break;
715                case WifiP2pManager.REMOVE_SERVICE_REQUEST:
716                    if (DBG) logd(getName() + " remove service request");
717                    removeServiceRequest(message.replyTo, (WifiP2pServiceRequest)message.obj);
718                    replyToMessage(message, WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED);
719                    break;
720                case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
721                    if (DBG) logd(getName() + " clear service request");
722                    clearServiceRequests(message.replyTo);
723                    replyToMessage(message, WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED);
724                    break;
725               case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
726                    if (DBG) logd(getName() + " receive service response");
727                    List<WifiP2pServiceResponse> sdRespList =
728                        (List<WifiP2pServiceResponse>) message.obj;
729                    for (WifiP2pServiceResponse resp : sdRespList) {
730                        WifiP2pDevice dev =
731                            mPeers.get(resp.getSrcDevice().deviceAddress);
732                        resp.setSrcDevice(dev);
733                        sendServiceResponse(resp);
734                    }
735                    break;
736                default:
737                    return NOT_HANDLED;
738            }
739            return HANDLED;
740        }
741
742        @Override
743        public void exit() {
744            sendP2pStateChangedBroadcast(false);
745            mNetworkInfo.setIsAvailable(false);
746        }
747    }
748
749    class InactiveState extends State {
750        @Override
751        public void enter() {
752            if (DBG) logd(getName());
753            //Start listening every time we get inactive
754            //TODO: Fix listen after driver behavior is fixed
755            //mWifiNative.p2pListen();
756        }
757
758        @Override
759        public boolean processMessage(Message message) {
760            if (DBG) logd(getName() + message.toString());
761            switch (message.what) {
762                case WifiP2pManager.CONNECT:
763                    if (DBG) logd(getName() + " sending connect");
764                    WifiP2pConfig config = (WifiP2pConfig) message.obj;
765                    mAutonomousGroup = false;
766
767                    if (mSavedPeerConfig != null && config.deviceAddress.equals(
768                                    mSavedPeerConfig.deviceAddress)) {
769                        mSavedPeerConfig = config;
770
771                        //Stop discovery before issuing connect
772                        mWifiNative.p2pStopFind();
773                        if (mPeers.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
774                            p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
775                        } else {
776                            p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
777                        }
778                        transitionTo(mGroupNegotiationState);
779                    } else {
780                        mSavedPeerConfig = config;
781                        int netId = configuredNetworkId(mSavedPeerConfig.deviceAddress);
782                        if (netId >= 0) {
783                            //TODO: if failure, remove config and do a regular p2pConnect()
784                            mWifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress);
785                        } else {
786                            //Stop discovery before issuing connect
787                            mWifiNative.p2pStopFind();
788                            //If peer is a GO, we do not need to send provisional discovery,
789                            //the supplicant takes care of it.
790                            if (mPeers.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
791                                if (DBG) logd("Sending join to GO");
792                                p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
793                                transitionTo(mGroupNegotiationState);
794                            } else {
795                                if (DBG) logd("Sending prov disc");
796                                transitionTo(mProvisionDiscoveryState);
797                            }
798                        }
799                    }
800                    mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
801                    sendP2pPeersChangedBroadcast();
802                    replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
803                    break;
804                case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
805                    mSavedPeerConfig = (WifiP2pConfig) message.obj;
806
807                    mAutonomousGroup = false;
808                    mJoinExistingGroup = false;
809                    if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
810                            mSavedPeerConfig)) {
811                        transitionTo(mUserAuthorizingInvitationState);
812                    }
813                    break;
814                case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
815                    WifiP2pGroup group = (WifiP2pGroup) message.obj;
816                    WifiP2pDevice owner = group.getOwner();
817
818                    if (owner == null) {
819                        if (DBG) loge("Ignored invitation from null owner");
820                        break;
821                    }
822
823                    mSavedPeerConfig = new WifiP2pConfig();
824                    mSavedPeerConfig.deviceAddress = group.getOwner().deviceAddress;
825
826                    //Check if we have the owner in peer list and use appropriate
827                    //wps method. Default is to use PBC.
828                    if ((owner = mPeers.get(owner.deviceAddress)) != null) {
829                        if (owner.wpsPbcSupported()) {
830                            mSavedPeerConfig.wps.setup = WpsInfo.PBC;
831                        } else if (owner.wpsKeypadSupported()) {
832                            mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
833                        } else if (owner.wpsDisplaySupported()) {
834                            mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
835                        }
836                    }
837
838                    mAutonomousGroup = false;
839                    mJoinExistingGroup = true;
840                    //TODO In the p2p client case, we should set source address correctly.
841                    if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
842                            mSavedPeerConfig)) {
843                        transitionTo(mUserAuthorizingInvitationState);
844                    }
845                    break;
846                case WifiMonitor.P2P_FIND_STOPPED_EVENT:
847                    // When discovery stops in inactive state, flush to clear
848                    // state peer data
849                    mWifiNative.p2pFlush();
850                    mServiceDiscReqId = null;
851                    sendP2pDiscoveryChangedBroadcast(false);
852                    break;
853                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
854                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
855                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
856                    //We let the supplicant handle the provision discovery response
857                    //and wait instead for the GO_NEGOTIATION_REQUEST_EVENT.
858                    //Handling provision discovery and issuing a p2p_connect before
859                    //group negotiation comes through causes issues
860                   break;
861                case WifiP2pManager.CREATE_GROUP:
862                   mAutonomousGroup = true;
863                   if (mWifiNative.p2pGroupAdd()) {
864                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
865                    } else {
866                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
867                                WifiP2pManager.ERROR);
868                    }
869                    transitionTo(mGroupNegotiationState);
870                    break;
871               default:
872                    return NOT_HANDLED;
873            }
874            return HANDLED;
875        }
876    }
877
878    class GroupCreatingState extends State {
879        @Override
880        public void enter() {
881            if (DBG) logd(getName());
882            sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,
883                    ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);
884        }
885
886        @Override
887        public boolean processMessage(Message message) {
888            if (DBG) logd(getName() + message.toString());
889            boolean ret = HANDLED;
890            switch (message.what) {
891               case GROUP_CREATING_TIMED_OUT:
892                    if (mGroupCreatingTimeoutIndex == message.arg1) {
893                        if (DBG) logd("Group negotiation timed out");
894                        handleGroupCreationFailure();
895                        transitionTo(mInactiveState);
896                    }
897                    break;
898                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
899                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
900                    if (!mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
901                        // Do the regular device lost handling
902                        ret = NOT_HANDLED;
903                        break;
904                    }
905                    // Do nothing
906                    if (DBG) logd("Retain connecting device " + device);
907                    break;
908                case WifiP2pManager.DISCOVER_PEERS:
909                    /* Discovery will break negotiation */
910                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
911                            WifiP2pManager.BUSY);
912                    break;
913                case WifiP2pManager.CANCEL_CONNECT:
914                    //Do a supplicant p2p_cancel which only cancels an ongoing
915                    //group negotiation. This will fail for a pending provision
916                    //discovery or for a pending user action, but at the framework
917                    //level, we always treat cancel as succeeded and enter
918                    //an inactive state
919                    mWifiNative.p2pCancelConnect();
920                    handleGroupCreationFailure();
921                    transitionTo(mInactiveState);
922                    replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
923                    break;
924                default:
925                    ret = NOT_HANDLED;
926            }
927            return ret;
928        }
929    }
930
931    class UserAuthorizingInvitationState extends State {
932        @Override
933        public void enter() {
934            if (DBG) logd(getName());
935            if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
936                    mSavedPeerConfig)) {
937                notifyInvitationReceived();
938            }
939        }
940
941        @Override
942        public boolean processMessage(Message message) {
943            if (DBG) logd(getName() + message.toString());
944            boolean ret = HANDLED;
945            switch (message.what) {
946                case PEER_CONNECTION_USER_ACCEPT:
947                    //TODO: handle persistence
948                    if (mJoinExistingGroup) {
949                        p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
950                    } else {
951                        p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
952                    }
953                    mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
954                    sendP2pPeersChangedBroadcast();
955                    transitionTo(mGroupNegotiationState);
956                    break;
957                case PEER_CONNECTION_USER_REJECT:
958                    if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
959                    mSavedPeerConfig = null;
960                    transitionTo(mInactiveState);
961                    break;
962                default:
963                    return NOT_HANDLED;
964            }
965            return ret;
966        }
967
968        @Override
969        public void exit() {
970            //TODO: dismiss dialog if not already done
971        }
972    }
973
974    class ProvisionDiscoveryState extends State {
975        @Override
976        public void enter() {
977            if (DBG) logd(getName());
978            mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
979        }
980
981        @Override
982        public boolean processMessage(Message message) {
983            if (DBG) logd(getName() + message.toString());
984            WifiP2pProvDiscEvent provDisc;
985            WifiP2pDevice device;
986            switch (message.what) {
987                case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
988                    provDisc = (WifiP2pProvDiscEvent) message.obj;
989                    device = provDisc.device;
990                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
991
992                    if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
993                        if (DBG) logd("Found a match " + mSavedPeerConfig);
994                        mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
995                        transitionTo(mGroupNegotiationState);
996                    }
997                    break;
998                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
999                    provDisc = (WifiP2pProvDiscEvent) message.obj;
1000                    device = provDisc.device;
1001                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1002
1003                    if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) {
1004                        if (DBG) logd("Found a match " + mSavedPeerConfig);
1005                        /* we already have the pin */
1006                        if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
1007                            mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
1008                            transitionTo(mGroupNegotiationState);
1009                        } else {
1010                            mJoinExistingGroup = false;
1011                            transitionTo(mUserAuthorizingInvitationState);
1012                        }
1013                    }
1014                    break;
1015                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1016                    provDisc = (WifiP2pProvDiscEvent) message.obj;
1017                    device = provDisc.device;
1018                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1019
1020                    if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
1021                        if (DBG) logd("Found a match " + mSavedPeerConfig);
1022                        mSavedPeerConfig.wps.pin = provDisc.pin;
1023                        mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
1024                        if (!sendShowPinReqToFrontApp(provDisc.pin)) {
1025                            notifyInvitationSent(provDisc.pin, device.deviceAddress);
1026                        }
1027                        transitionTo(mGroupNegotiationState);
1028                    }
1029                    break;
1030                default:
1031                    return NOT_HANDLED;
1032            }
1033            return HANDLED;
1034        }
1035    }
1036
1037    class GroupNegotiationState extends State {
1038        @Override
1039        public void enter() {
1040            if (DBG) logd(getName());
1041        }
1042
1043        @Override
1044        public boolean processMessage(Message message) {
1045            if (DBG) logd(getName() + message.toString());
1046            switch (message.what) {
1047                // We ignore these right now, since we get a GROUP_STARTED notification
1048                // afterwards
1049                case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1050                case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
1051                    if (DBG) logd(getName() + " go success");
1052                    break;
1053                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1054                    mGroup = (WifiP2pGroup) message.obj;
1055                    if (DBG) logd(getName() + " group started");
1056                    if (mGroup.isGroupOwner()) {
1057                        startDhcpServer(mGroup.getInterface());
1058                    } else {
1059                        // Set group idle only for a client on the group interface to speed up
1060                        // disconnect when GO is gone. Setting group idle time for a group owner
1061                        // causes connectivity issues for new clients
1062                        mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
1063                        mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
1064                                P2pStateMachine.this, mGroup.getInterface());
1065                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
1066                        WifiP2pDevice groupOwner = mGroup.getOwner();
1067                        mPeers.updateStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
1068                        sendP2pPeersChangedBroadcast();
1069                    }
1070                    mSavedPeerConfig = null;
1071                    transitionTo(mGroupCreatedState);
1072                    break;
1073                case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
1074                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1075                    if (DBG) logd(getName() + " go failure");
1076                    handleGroupCreationFailure();
1077                    transitionTo(mInactiveState);
1078                    break;
1079                // A group formation failure is always followed by
1080                // a group removed event. Flushing things at group formation
1081                // failure causes supplicant issues. Ignore right now.
1082                case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
1083                    break;
1084                default:
1085                    return NOT_HANDLED;
1086            }
1087            return HANDLED;
1088        }
1089    }
1090
1091
1092
1093    class GroupCreatedState extends State {
1094        @Override
1095        public void enter() {
1096            if (DBG) logd(getName());
1097            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
1098
1099            updateThisDevice(WifiP2pDevice.CONNECTED);
1100
1101            //DHCP server has already been started if I am a group owner
1102            if (mGroup.isGroupOwner()) {
1103                setWifiP2pInfoOnGroupFormation(SERVER_ADDRESS);
1104                sendP2pConnectionChangedBroadcast();
1105            }
1106        }
1107
1108        @Override
1109        public boolean processMessage(Message message) {
1110            if (DBG) logd(getName() + message.toString());
1111            switch (message.what) {
1112                case WifiMonitor.AP_STA_CONNECTED_EVENT:
1113                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
1114                    String deviceAddress = device.deviceAddress;
1115                    if (deviceAddress != null) {
1116                        if (mSavedProvDiscDevice != null &&
1117                                deviceAddress.equals(mSavedProvDiscDevice.deviceAddress)) {
1118                            mSavedProvDiscDevice = null;
1119                        }
1120                        mGroup.addClient(deviceAddress);
1121                        mPeers.updateStatus(deviceAddress, WifiP2pDevice.CONNECTED);
1122                        if (DBG) logd(getName() + " ap sta connected");
1123                        sendP2pPeersChangedBroadcast();
1124                    } else {
1125                        loge("Connect on null device address, ignore");
1126                    }
1127                    break;
1128                case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
1129                    device = (WifiP2pDevice) message.obj;
1130                    deviceAddress = device.deviceAddress;
1131                    if (deviceAddress != null) {
1132                        mPeers.updateStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
1133                        if (mGroup.removeClient(deviceAddress)) {
1134                            if (DBG) logd("Removed client " + deviceAddress);
1135                            if (!mAutonomousGroup && mGroup.isClientListEmpty()) {
1136                                Slog.d(TAG, "Client list empty, remove non-persistent p2p group");
1137                                mWifiNative.p2pGroupRemove(mGroup.getInterface());
1138                            }
1139                        } else {
1140                            if (DBG) logd("Failed to remove client " + deviceAddress);
1141                            for (WifiP2pDevice c : mGroup.getClientList()) {
1142                                if (DBG) logd("client " + c.deviceAddress);
1143                            }
1144                        }
1145                        sendP2pPeersChangedBroadcast();
1146                        if (DBG) loge(getName() + " ap sta disconnected");
1147                    } else {
1148                        loge("Disconnect on unknown device: " + device);
1149                    }
1150                    break;
1151                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
1152                    DhcpInfoInternal dhcpInfo = (DhcpInfoInternal) message.obj;
1153                    if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS &&
1154                            dhcpInfo != null) {
1155                        if (DBG) logd("DhcpInfo: " + dhcpInfo);
1156                        setWifiP2pInfoOnGroupFormation(dhcpInfo.serverAddress);
1157                        sendP2pConnectionChangedBroadcast();
1158                        //Turn on power save on client
1159                        mWifiNative.setP2pPowerSave(mGroup.getInterface(), true);
1160                    } else {
1161                        loge("DHCP failed");
1162                        mWifiNative.p2pGroupRemove(mGroup.getInterface());
1163                    }
1164                    break;
1165                case WifiP2pManager.REMOVE_GROUP:
1166                    if (DBG) loge(getName() + " remove group");
1167                    if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
1168                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
1169                    } else {
1170                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
1171                                WifiP2pManager.ERROR);
1172                    }
1173                    break;
1174                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1175                    if (DBG) loge(getName() + " group removed");
1176                    Collection <WifiP2pDevice> devices = mGroup.getClientList();
1177                    boolean changed = false;
1178                    for (WifiP2pDevice d : mPeers.getDeviceList()) {
1179                        if (devices.contains(d) || mGroup.getOwner().equals(d)) {
1180                            d.status = WifiP2pDevice.AVAILABLE;
1181                            changed = true;
1182                        }
1183                    }
1184
1185                    if (mGroup.isGroupOwner()) {
1186                        stopDhcpServer();
1187                    } else {
1188                        if (DBG) logd("stop DHCP client");
1189                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
1190                        mDhcpStateMachine.quit();
1191                        mDhcpStateMachine = null;
1192                    }
1193
1194                    mGroup = null;
1195                    mWifiNative.p2pFlush();
1196                    mServiceDiscReqId = null;
1197                    if (changed) sendP2pPeersChangedBroadcast();
1198                    transitionTo(mInactiveState);
1199                    break;
1200                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
1201                    device = (WifiP2pDevice) message.obj;
1202                    //Device loss for a connected device indicates it is not in discovery any more
1203                    if (mGroup.contains(device)) {
1204                        if (DBG) logd("Lost " + device +" , do nothing");
1205                        return HANDLED;
1206                    }
1207                    // Do the regular device lost handling
1208                    return NOT_HANDLED;
1209                case WifiStateMachine.CMD_DISABLE_P2P:
1210                    sendMessage(WifiP2pManager.REMOVE_GROUP);
1211                    deferMessage(message);
1212                    break;
1213                case WifiP2pManager.CONNECT:
1214                    WifiP2pConfig config = (WifiP2pConfig) message.obj;
1215                    if (config.deviceAddress == null ||
1216                            (mSavedProvDiscDevice != null &&
1217                                    mSavedProvDiscDevice.deviceAddress.equals(
1218                                            config.deviceAddress))) {
1219                        if (config.wps.setup == WpsInfo.PBC) {
1220                            mWifiNative.startWpsPbc(mGroup.getInterface(), null);
1221                        } else {
1222                            if (config.wps.pin == null) {
1223                                String pin = mWifiNative.startWpsPinDisplay(mGroup.getInterface());
1224                                try {
1225                                    Integer.parseInt(pin);
1226                                    if (!sendShowPinReqToFrontApp(pin)) {
1227                                        notifyInvitationSent(pin,
1228                                                config.deviceAddress != null ?
1229                                                        config.deviceAddress : "any");
1230                                    }
1231                                } catch (NumberFormatException ignore) {
1232                                    // do nothing if pin is invalid
1233                                }
1234                            } else {
1235                                mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
1236                                        config.wps.pin);
1237                            }
1238                        }
1239                        if (config.deviceAddress != null) {
1240                            mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
1241                            sendP2pPeersChangedBroadcast();
1242                        }
1243                        replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
1244                    } else {
1245                        logd("Inviting device : " + config.deviceAddress);
1246                        if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
1247                            mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
1248                            sendP2pPeersChangedBroadcast();
1249                            replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
1250                        } else {
1251                            replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
1252                                    WifiP2pManager.ERROR);
1253                        }
1254                    }
1255                    // TODO: figure out updating the status to declined when invitation is rejected
1256                    break;
1257                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1258                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1259                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1260                    WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
1261                    mSavedProvDiscDevice = provDisc.device;
1262                    mSavedPeerConfig = new WifiP2pConfig();
1263                    mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
1264                    if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
1265                        mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
1266                    } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
1267                        mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
1268                        mSavedPeerConfig.wps.pin = provDisc.pin;
1269                    } else {
1270                        mSavedPeerConfig.wps.setup = WpsInfo.PBC;
1271                    }
1272                    if (!sendConnectNoticeToApp(mSavedProvDiscDevice, mSavedPeerConfig)) {
1273                        transitionTo(mUserAuthorizingJoinState);
1274                    }
1275                    break;
1276                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1277                    Slog.e(TAG, "Duplicate group creation event notice, ignore");
1278                    break;
1279                default:
1280                    return NOT_HANDLED;
1281            }
1282            return HANDLED;
1283        }
1284
1285        public void exit() {
1286            mSavedProvDiscDevice = null;
1287            updateThisDevice(WifiP2pDevice.AVAILABLE);
1288            setWifiP2pInfoOnGroupTermination();
1289            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
1290            sendP2pConnectionChangedBroadcast();
1291        }
1292    }
1293
1294    class UserAuthorizingJoinState extends State {
1295        @Override
1296        public void enter() {
1297            if (DBG) logd(getName());
1298
1299            notifyInvitationReceived();
1300        }
1301
1302        @Override
1303        public boolean processMessage(Message message) {
1304            if (DBG) logd(getName() + message.toString());
1305            switch (message.what) {
1306                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1307                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1308                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1309                    //Ignore more client requests
1310                    break;
1311                case PEER_CONNECTION_USER_ACCEPT:
1312                    if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
1313                        mWifiNative.startWpsPbc(mGroup.getInterface(), null);
1314                    } else {
1315                        mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
1316                                mSavedPeerConfig.wps.pin);
1317                    }
1318                    mSavedPeerConfig = null;
1319                    transitionTo(mGroupCreatedState);
1320                    break;
1321                case PEER_CONNECTION_USER_REJECT:
1322                    if (DBG) logd("User rejected incoming request");
1323                    mSavedPeerConfig = null;
1324                    transitionTo(mGroupCreatedState);
1325                    break;
1326                default:
1327                    return NOT_HANDLED;
1328            }
1329            return HANDLED;
1330        }
1331
1332        @Override
1333        public void exit() {
1334            //TODO: dismiss dialog if not already done
1335        }
1336    }
1337
1338    private void sendP2pStateChangedBroadcast(boolean enabled) {
1339        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
1340        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1341        if (enabled) {
1342            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
1343                    WifiP2pManager.WIFI_P2P_STATE_ENABLED);
1344        } else {
1345            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
1346                    WifiP2pManager.WIFI_P2P_STATE_DISABLED);
1347        }
1348        mContext.sendStickyBroadcast(intent);
1349    }
1350
1351    private void sendP2pDiscoveryChangedBroadcast(boolean started) {
1352        if (mDiscoveryStarted == started) return;
1353        mDiscoveryStarted = started;
1354
1355        if (DBG) logd("discovery change broadcast " + started);
1356
1357        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
1358        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1359        intent.putExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, started ?
1360                WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED :
1361                WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
1362        mContext.sendStickyBroadcast(intent);
1363    }
1364
1365    private void sendThisDeviceChangedBroadcast() {
1366        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
1367        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1368        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
1369        mContext.sendStickyBroadcast(intent);
1370    }
1371
1372    private void sendP2pPeersChangedBroadcast() {
1373        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
1374        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1375        mContext.sendBroadcast(intent);
1376    }
1377
1378    private void sendP2pConnectionChangedBroadcast() {
1379        if (DBG) logd("sending p2p connection changed broadcast");
1380        Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
1381        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1382                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1383        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
1384        intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
1385        mContext.sendStickyBroadcast(intent);
1386    }
1387
1388    private void startDhcpServer(String intf) {
1389        InterfaceConfiguration ifcg = null;
1390        try {
1391            ifcg = mNwService.getInterfaceConfig(intf);
1392            ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(
1393                        SERVER_ADDRESS), 24));
1394            ifcg.setInterfaceUp();
1395            mNwService.setInterfaceConfig(intf, ifcg);
1396            /* This starts the dnsmasq server */
1397            mNwService.startTethering(DHCP_RANGE);
1398        } catch (Exception e) {
1399            loge("Error configuring interface " + intf + ", :" + e);
1400            return;
1401        }
1402
1403        logd("Started Dhcp server on " + intf);
1404   }
1405
1406    private void stopDhcpServer() {
1407        try {
1408            mNwService.stopTethering();
1409        } catch (Exception e) {
1410            loge("Error stopping Dhcp server" + e);
1411            return;
1412        }
1413
1414        logd("Stopped Dhcp server");
1415    }
1416
1417    private void notifyP2pEnableFailure() {
1418        Resources r = Resources.getSystem();
1419        AlertDialog dialog = new AlertDialog.Builder(mContext)
1420            .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
1421            .setMessage(r.getString(R.string.wifi_p2p_failed_message))
1422            .setPositiveButton(r.getString(R.string.ok), null)
1423            .create();
1424        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1425        dialog.show();
1426    }
1427
1428    private void addRowToDialog(ViewGroup group, int stringId, String value) {
1429        Resources r = Resources.getSystem();
1430        View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row,
1431                group, false);
1432        ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId));
1433        ((TextView) row.findViewById(R.id.value)).setText(value);
1434        group.addView(row);
1435    }
1436
1437    private void notifyInvitationSent(String pin, String peerAddress) {
1438        Resources r = Resources.getSystem();
1439
1440        final View textEntryView = LayoutInflater.from(mContext)
1441                .inflate(R.layout.wifi_p2p_dialog, null);
1442
1443        ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
1444        addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress));
1445        addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin);
1446
1447        AlertDialog dialog = new AlertDialog.Builder(mContext)
1448            .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title))
1449            .setView(textEntryView)
1450            .setPositiveButton(r.getString(R.string.ok), null)
1451            .create();
1452        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1453        dialog.show();
1454    }
1455
1456    private void notifyInvitationReceived() {
1457        Resources r = Resources.getSystem();
1458        final WpsInfo wps = mSavedPeerConfig.wps;
1459        final View textEntryView = LayoutInflater.from(mContext)
1460                .inflate(R.layout.wifi_p2p_dialog, null);
1461
1462        ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
1463        addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName(
1464                mSavedPeerConfig.deviceAddress));
1465
1466        final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin);
1467
1468        AlertDialog dialog = new AlertDialog.Builder(mContext)
1469            .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title))
1470            .setView(textEntryView)
1471            .setPositiveButton(r.getString(R.string.accept), new OnClickListener() {
1472                        public void onClick(DialogInterface dialog, int which) {
1473                            if (wps.setup == WpsInfo.KEYPAD) {
1474                                mSavedPeerConfig.wps.pin = pin.getText().toString();
1475                            }
1476                            if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig);
1477                            sendMessage(PEER_CONNECTION_USER_ACCEPT);
1478                        }
1479                    })
1480            .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
1481                        @Override
1482                        public void onClick(DialogInterface dialog, int which) {
1483                            if (DBG) logd(getName() + " ignore connect");
1484                            sendMessage(PEER_CONNECTION_USER_REJECT);
1485                        }
1486                    })
1487            .setOnCancelListener(new DialogInterface.OnCancelListener() {
1488                        @Override
1489                        public void onCancel(DialogInterface arg0) {
1490                            if (DBG) logd(getName() + " ignore connect");
1491                            sendMessage(PEER_CONNECTION_USER_REJECT);
1492                        }
1493                    })
1494            .create();
1495
1496        //make the enter pin area or the display pin area visible
1497        switch (wps.setup) {
1498            case WpsInfo.KEYPAD:
1499                if (DBG) logd("Enter pin section visible");
1500                textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE);
1501                break;
1502            case WpsInfo.DISPLAY:
1503                if (DBG) logd("Shown pin section visible");
1504                addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin);
1505                break;
1506            default:
1507                break;
1508        }
1509
1510        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1511        dialog.show();
1512    }
1513
1514    //TODO: implement when wpa_supplicant is fixed
1515    private int configuredNetworkId(String deviceAddress) {
1516        return -1;
1517    }
1518
1519    private void setWifiP2pInfoOnGroupFormation(String serverAddress) {
1520        mWifiP2pInfo.groupFormed = true;
1521        mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
1522        mWifiP2pInfo.groupOwnerAddress = NetworkUtils.numericToInetAddress(serverAddress);
1523    }
1524
1525    private void setWifiP2pInfoOnGroupTermination() {
1526        mWifiP2pInfo.groupFormed = false;
1527        mWifiP2pInfo.isGroupOwner = false;
1528        mWifiP2pInfo.groupOwnerAddress = null;
1529    }
1530
1531    private String getDeviceName(String deviceAddress) {
1532        WifiP2pDevice d = mPeers.get(deviceAddress);
1533        if (d != null) {
1534                return d.deviceName;
1535        }
1536        //Treat the address as name if there is no match
1537        return deviceAddress;
1538    }
1539
1540    private void p2pConnectWithPinDisplay(WifiP2pConfig config, boolean join) {
1541        String pin = mWifiNative.p2pConnect(config, join);
1542        try {
1543            Integer.parseInt(pin);
1544            if (!sendShowPinReqToFrontApp(pin)) {
1545                notifyInvitationSent(pin, config.deviceAddress);
1546            }
1547        } catch (NumberFormatException ignore) {
1548            // do nothing if p2pConnect did not return a pin
1549        }
1550    }
1551
1552    private String getPersistedDeviceName() {
1553        String deviceName = Settings.Secure.getString(mContext.getContentResolver(),
1554                Settings.Secure.WIFI_P2P_DEVICE_NAME);
1555        if (deviceName == null) {
1556            /* We use the 4 digits of the ANDROID_ID to have a friendly
1557             * default that has low likelihood of collision with a peer */
1558            String id = Settings.Secure.getString(mContext.getContentResolver(),
1559                    Settings.Secure.ANDROID_ID);
1560            return "Android_" + id.substring(0,4);
1561        }
1562        return deviceName;
1563    }
1564
1565    private boolean setAndPersistDeviceName(String devName) {
1566        if (devName == null) return false;
1567
1568        if (!mWifiNative.setDeviceName(devName)) {
1569            loge("Failed to set device name " + devName);
1570            return false;
1571        }
1572
1573        mThisDevice.deviceName = devName;
1574        mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
1575
1576        Settings.Secure.putString(mContext.getContentResolver(),
1577                Settings.Secure.WIFI_P2P_DEVICE_NAME, devName);
1578        sendThisDeviceChangedBroadcast();
1579        return true;
1580    }
1581
1582    private void initializeP2pSettings() {
1583        mWifiNative.setPersistentReconnect(true);
1584        mThisDevice.deviceName = getPersistedDeviceName();
1585        mWifiNative.setDeviceName(mThisDevice.deviceName);
1586        // DIRECT-XY-DEVICENAME (XY is randomly generated)
1587        mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
1588        mWifiNative.setDeviceType(mThisDevice.primaryDeviceType);
1589        // Supplicant defaults to using virtual display with display
1590        // which refers to a remote display. Use physical_display
1591        mWifiNative.setConfigMethods("virtual_push_button physical_display keypad");
1592        // STA has higher priority over P2P
1593        mWifiNative.setConcurrencyPriority("sta");
1594
1595        mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
1596        updateThisDevice(WifiP2pDevice.AVAILABLE);
1597        if (DBG) Slog.d(TAG, "DeviceAddress: " + mThisDevice.deviceAddress);
1598
1599        mClientInfoList.clear();
1600        mWifiNative.p2pFlush();
1601        mWifiNative.p2pServiceFlush();
1602        mServiceTransactionId = 0;
1603        mServiceDiscReqId = null;
1604    }
1605
1606    private void updateThisDevice(int status) {
1607        mThisDevice.status = status;
1608        sendThisDeviceChangedBroadcast();
1609    }
1610
1611    private void handleGroupCreationFailure() {
1612        mSavedPeerConfig = null;
1613        /* After cancelling group formation, new connections on existing peers can fail
1614         * at supplicant. Flush and restart discovery */
1615        mWifiNative.p2pFlush();
1616        mServiceDiscReqId = null;
1617        sendMessage(WifiP2pManager.DISCOVER_PEERS);
1618    }
1619
1620    //State machine initiated requests can have replyTo set to null indicating
1621    //there are no recipients, we ignore those reply actions
1622    private void replyToMessage(Message msg, int what) {
1623        if (msg.replyTo == null) return;
1624        Message dstMsg = obtainMessage(msg);
1625        dstMsg.what = what;
1626        mReplyChannel.replyToMessage(msg, dstMsg);
1627    }
1628
1629    private void replyToMessage(Message msg, int what, int arg1) {
1630        if (msg.replyTo == null) return;
1631        Message dstMsg = obtainMessage(msg);
1632        dstMsg.what = what;
1633        dstMsg.arg1 = arg1;
1634        mReplyChannel.replyToMessage(msg, dstMsg);
1635    }
1636
1637    private void replyToMessage(Message msg, int what, Object obj) {
1638        if (msg.replyTo == null) return;
1639        Message dstMsg = obtainMessage(msg);
1640        dstMsg.what = what;
1641        dstMsg.obj = obj;
1642        mReplyChannel.replyToMessage(msg, dstMsg);
1643    }
1644
1645    /* arg2 on the source message has a hash code that needs to be retained in replies
1646     * see WifiP2pManager for details */
1647    private Message obtainMessage(Message srcMsg) {
1648        Message msg = Message.obtain();
1649        msg.arg2 = srcMsg.arg2;
1650        return msg;
1651    }
1652
1653    private void logd(String s) {
1654        Slog.d(TAG, s);
1655    }
1656
1657    private void loge(String s) {
1658        Slog.e(TAG, s);
1659    }
1660
1661    /**
1662     * Update service discovery request to wpa_supplicant.
1663     */
1664    private boolean updateSupplicantServiceRequest() {
1665        clearSupplicantServiceRequest();
1666
1667        StringBuffer sb = new StringBuffer();
1668        for (ClientInfo c: mClientInfoList.values()) {
1669            int key;
1670            WifiP2pServiceRequest req;
1671            for (int i=0; i < c.mReqList.size(); i++) {
1672                req = c.mReqList.valueAt(i);
1673                if (req != null) {
1674                    sb.append(req.getSupplicantQuery());
1675                }
1676            }
1677        }
1678
1679        if (sb.length() == 0) {
1680            return false;
1681        }
1682
1683        mServiceDiscReqId = mWifiNative.p2pServDiscReq("00:00:00:00:00:00", sb.toString());
1684        if (mServiceDiscReqId == null) {
1685            return false;
1686        }
1687        return true;
1688    }
1689
1690    /**
1691     * Clear service discovery request in wpa_supplicant
1692     */
1693    private void clearSupplicantServiceRequest() {
1694        if (mServiceDiscReqId == null) return;
1695
1696        mWifiNative.p2pServDiscCancelReq(mServiceDiscReqId);
1697        mServiceDiscReqId = null;
1698    }
1699
1700    /* TODO: We could track individual service adds separately and avoid
1701     * having to do update all service requests on every new request
1702     */
1703    private boolean addServiceRequest(Messenger m, WifiP2pServiceRequest req) {
1704        clearClientDeadChannels();
1705        ClientInfo clientInfo = getClientInfo(m, true);
1706        if (clientInfo == null) {
1707            return false;
1708        }
1709
1710        ++mServiceTransactionId;
1711        //The Wi-Fi p2p spec says transaction id should be non-zero
1712        if (mServiceTransactionId == 0) ++mServiceTransactionId;
1713        req.setTransactionId(mServiceTransactionId);
1714        clientInfo.mReqList.put(mServiceTransactionId, req);
1715
1716        if (mServiceDiscReqId == null) {
1717            return true;
1718        }
1719
1720        return updateSupplicantServiceRequest();
1721    }
1722
1723    private void removeServiceRequest(Messenger m, WifiP2pServiceRequest req) {
1724        ClientInfo clientInfo = getClientInfo(m, false);
1725        if (clientInfo == null) {
1726            return;
1727        }
1728
1729        //Application does not have transaction id information
1730        //go through stored requests to remove
1731        boolean removed = false;
1732        for (int i=0; i < clientInfo.mReqList.size(); i++) {
1733            if (req.equals(clientInfo.mReqList.valueAt(i))) {
1734                removed = true;
1735                clientInfo.mReqList.removeAt(i);
1736                break;
1737            }
1738        }
1739
1740        if (!removed) return;
1741
1742        if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
1743            if (DBG) logd("remove client information from framework");
1744            mClientInfoList.remove(clientInfo.mMessenger);
1745        }
1746
1747        if (mServiceDiscReqId == null) {
1748            return;
1749        }
1750
1751        updateSupplicantServiceRequest();
1752    }
1753
1754    private void clearServiceRequests(Messenger m) {
1755
1756        ClientInfo clientInfo = getClientInfo(m, false);
1757        if (clientInfo == null) {
1758            return;
1759        }
1760
1761        if (clientInfo.mReqList.size() == 0) {
1762            return;
1763        }
1764
1765        clientInfo.mReqList.clear();
1766
1767        if (clientInfo.mServList.size() == 0) {
1768            if (DBG) logd("remove channel information from framework");
1769            mClientInfoList.remove(clientInfo.mMessenger);
1770        }
1771
1772        if (mServiceDiscReqId == null) {
1773            return;
1774        }
1775
1776        updateSupplicantServiceRequest();
1777    }
1778
1779    private boolean addLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
1780        clearClientDeadChannels();
1781        ClientInfo clientInfo = getClientInfo(m, true);
1782        if (clientInfo == null) {
1783            return false;
1784        }
1785
1786        if (!clientInfo.mServList.add(servInfo)) {
1787            return false;
1788        }
1789
1790        if (!mWifiNative.p2pServiceAdd(servInfo)) {
1791            clientInfo.mServList.remove(servInfo);
1792            return false;
1793        }
1794
1795        return true;
1796    }
1797
1798    private void removeLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
1799        ClientInfo clientInfo = getClientInfo(m, false);
1800        if (clientInfo == null) {
1801            return;
1802        }
1803
1804        mWifiNative.p2pServiceDel(servInfo);
1805
1806        clientInfo.mServList.remove(servInfo);
1807        if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
1808            if (DBG) logd("remove client information from framework");
1809            mClientInfoList.remove(clientInfo.mMessenger);
1810        }
1811    }
1812
1813    private void clearLocalServices(Messenger m) {
1814        ClientInfo clientInfo = getClientInfo(m, false);
1815        if (clientInfo == null) {
1816            return;
1817        }
1818
1819        for (WifiP2pServiceInfo servInfo: clientInfo.mServList) {
1820            mWifiNative.p2pServiceDel(servInfo);
1821        }
1822
1823        clientInfo.mServList.clear();
1824        if (clientInfo.mReqList.size() == 0) {
1825            if (DBG) logd("remove client information from framework");
1826            mClientInfoList.remove(clientInfo.mMessenger);
1827        }
1828    }
1829
1830    private void clearClientInfo(Messenger m) {
1831        clearLocalServices(m);
1832        clearServiceRequests(m);
1833    }
1834
1835    /**
1836     * Send the service response to the WifiP2pManager.Channel.
1837     *
1838     * @param resp
1839     */
1840    private void sendServiceResponse(WifiP2pServiceResponse resp) {
1841        for (ClientInfo c : mClientInfoList.values()) {
1842            WifiP2pServiceRequest req = c.mReqList.get(resp.getTransactionId());
1843            if (req != null) {
1844                Message msg = Message.obtain();
1845                msg.what = WifiP2pManager.RESPONSE_SERVICE;
1846                msg.arg1 = 0;
1847                msg.arg2 = 0;
1848                msg.obj = resp;
1849                try {
1850                    c.mMessenger.send(msg);
1851                } catch (RemoteException e) {
1852                    if (DBG) logd("detect dead channel");
1853                    clearClientInfo(c.mMessenger);
1854                    return;
1855                }
1856            }
1857        }
1858    }
1859
1860    /**
1861     * We dont get notifications of clients that have gone away.
1862     * We detect this actively when services are added and throw
1863     * them away.
1864     *
1865     * TODO: This can be done better with full async channels.
1866     */
1867    private void clearClientDeadChannels() {
1868        ArrayList<Messenger> deadClients = new ArrayList<Messenger>();
1869
1870        for (ClientInfo c : mClientInfoList.values()) {
1871            Message msg = Message.obtain();
1872            msg.what = WifiP2pManager.PING;
1873            msg.arg1 = 0;
1874            msg.arg2 = 0;
1875            msg.obj = null;
1876            try {
1877                c.mMessenger.send(msg);
1878            } catch (RemoteException e) {
1879                if (DBG) logd("detect dead channel");
1880                deadClients.add(c.mMessenger);
1881            }
1882        }
1883
1884        for (Messenger m : deadClients) {
1885            clearClientInfo(m);
1886        }
1887    }
1888
1889    /**
1890     * Return the specified ClientInfo.
1891     * @param m Messenger
1892     * @param createIfNotExist if true and the specified channel info does not exist,
1893     * create new client info.
1894     * @return the specified ClientInfo.
1895     */
1896    private ClientInfo getClientInfo(Messenger m, boolean createIfNotExist) {
1897        ClientInfo clientInfo = mClientInfoList.get(m);
1898
1899        if (clientInfo == null && createIfNotExist) {
1900            if (DBG) logd("add a new client");
1901            clientInfo = new ClientInfo(m);
1902            mClientInfoList.put(m, clientInfo);
1903        }
1904
1905        return clientInfo;
1906    }
1907
1908    /**
1909     * Send detached message to dialog listener in the foreground application.
1910     * @param reason
1911     */
1912    private void sendDetachedMsg(int reason) {
1913        if (mForegroundAppMessenger == null) return;
1914
1915        Message msg = Message.obtain();
1916        msg.what = WifiP2pManager.DIALOG_LISTENER_DETACHED;
1917        msg.arg1 = reason;
1918        try {
1919            mForegroundAppMessenger.send(msg);
1920        } catch (RemoteException e) {
1921        }
1922        mForegroundAppMessenger = null;
1923        mForegroundAppPkgName = null;
1924    }
1925
1926    /**
1927     * Send a request to show wps pin to dialog listener in the foreground application.
1928     * @param pin WPS pin
1929     * @return
1930     */
1931    private boolean sendShowPinReqToFrontApp(String pin) {
1932        if (!isForegroundApp(mForegroundAppPkgName)) {
1933            sendDetachedMsg(WifiP2pManager.NOT_IN_FOREGROUND);
1934            return false;
1935        }
1936        Message msg = Message.obtain();
1937        msg.what = WifiP2pManager.SHOW_PIN_REQUESTED;
1938        Bundle bundle = new Bundle();
1939        bundle.putString(WifiP2pManager.WPS_PIN_BUNDLE_KEY, pin);
1940        msg.setData(bundle);
1941        return sendDialogMsgToFrontApp(msg);
1942    }
1943
1944    /**
1945     * Send a request to establish the connection to dialog listener in the foreground
1946     * application.
1947     * @param dev source device
1948     * @param config
1949     * @return
1950     */
1951    private boolean sendConnectNoticeToApp(WifiP2pDevice dev, WifiP2pConfig config) {
1952        if (dev == null) {
1953            dev = new WifiP2pDevice(config.deviceAddress);
1954        }
1955
1956        if (!isForegroundApp(mForegroundAppPkgName)) {
1957            if (DBG) logd("application is NOT foreground");
1958            sendDetachedMsg(WifiP2pManager.NOT_IN_FOREGROUND);
1959            return false;
1960        }
1961
1962        Message msg = Message.obtain();
1963        msg.what = WifiP2pManager.CONNECTION_REQUESTED;
1964        Bundle bundle = new Bundle();
1965        bundle.putParcelable(WifiP2pManager.P2P_DEV_BUNDLE_KEY, dev);
1966        bundle.putParcelable(WifiP2pManager.P2P_CONFIG_BUNDLE_KEY, config);
1967        msg.setData(bundle);
1968        return sendDialogMsgToFrontApp(msg);
1969    }
1970
1971    /**
1972     * Send dialog event message to front application's dialog listener.
1973     * @param msg
1974     * @return true if success.
1975     */
1976    private boolean sendDialogMsgToFrontApp(Message msg) {
1977        try {
1978            mForegroundAppMessenger.send(msg);
1979        } catch (RemoteException e) {
1980            mForegroundAppMessenger = null;
1981            mForegroundAppPkgName = null;
1982            return false;
1983        }
1984        return true;
1985    }
1986
1987    /**
1988     * Set dialog listener application.
1989     * @param m
1990     * @param appPkgName if null, reset the listener.
1991     * @param isReset if true, try to reset.
1992     * @return
1993     */
1994    private boolean setDialogListenerApp(Messenger m,
1995            String appPkgName, boolean isReset) {
1996
1997        if (mForegroundAppPkgName != null && !mForegroundAppPkgName.equals(appPkgName)) {
1998            if (isForegroundApp(mForegroundAppPkgName)) {
1999                // The current dialog listener is foreground app's.
2000                if (DBG) logd("application is NOT foreground");
2001                return false;
2002            }
2003            // detach an old listener.
2004            sendDetachedMsg(WifiP2pManager.NOT_IN_FOREGROUND);
2005        }
2006
2007        if (isReset) {
2008            if (DBG) logd("reset dialog listener");
2009            mForegroundAppMessenger = null;
2010            mForegroundAppPkgName = null;
2011            return true;
2012        }
2013
2014        if (!isForegroundApp(appPkgName)) {
2015            return false;
2016        }
2017
2018        mForegroundAppMessenger = m;
2019        mForegroundAppPkgName = appPkgName;
2020        if (DBG) logd("set dialog listener. app=" + appPkgName);
2021        return true;
2022    }
2023
2024    /**
2025     * Return true if the specified package name is foreground app's.
2026     *
2027     * @param pkgName application package name.
2028     * @return
2029     */
2030    private boolean isForegroundApp(String pkgName) {
2031        if (pkgName == null) return false;
2032
2033        List<RunningTaskInfo> tasks = mActivityMgr.getRunningTasks(1);
2034        if (tasks.size() == 0) {
2035            return false;
2036        }
2037
2038        return pkgName.equals(tasks.get(0).baseActivity.getPackageName());
2039    }
2040
2041    }
2042
2043    /**
2044     * Information about a particular client and we track the service discovery requests
2045     * and the local services registered by the client.
2046     */
2047    private class ClientInfo {
2048
2049        /*
2050         * A reference to WifiP2pManager.Channel handler.
2051         * The response of this request is notified to WifiP2pManager.Channel handler
2052         */
2053        private Messenger mMessenger;
2054
2055        /*
2056         * A service discovery request list.
2057         */
2058        private SparseArray<WifiP2pServiceRequest> mReqList;
2059
2060        /*
2061         * A local service information list.
2062         */
2063        private List<WifiP2pServiceInfo> mServList;
2064
2065        private ClientInfo(Messenger m) {
2066            mMessenger = m;
2067            mReqList = new SparseArray();
2068            mServList = new ArrayList<WifiP2pServiceInfo>();
2069        }
2070    }
2071
2072}
2073