WifiP2pService.java revision 1f095869536472c178046bb63c59947508eac4a6
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.wifi.p2p;
18
19import android.app.AlertDialog;
20import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.DialogInterface.OnClickListener;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.PackageManager;
30import android.content.res.Resources;
31import android.net.IConnectivityManager;
32import android.net.ConnectivityManager;
33import android.net.DhcpInfoInternal;
34import android.net.DhcpStateMachine;
35import android.net.InterfaceConfiguration;
36import android.net.LinkAddress;
37import android.net.LinkProperties;
38import android.net.NetworkInfo;
39import android.net.NetworkUtils;
40import android.net.wifi.WifiManager;
41import android.net.wifi.WifiMonitor;
42import android.net.wifi.WifiNative;
43import android.net.wifi.WifiStateMachine;
44import android.net.wifi.WpsInfo;
45import android.os.Binder;
46import android.os.IBinder;
47import android.os.INetworkManagementService;
48import android.os.Handler;
49import android.os.HandlerThread;
50import android.os.Message;
51import android.os.Messenger;
52import android.os.ServiceManager;
53import android.os.SystemProperties;
54import android.provider.Settings;
55import android.text.TextUtils;
56import android.util.Slog;
57import android.view.LayoutInflater;
58import android.view.View;
59import android.view.ViewGroup;
60import android.view.WindowManager;
61import android.widget.EditText;
62import android.widget.TextView;
63
64import com.android.internal.R;
65import com.android.internal.telephony.TelephonyIntents;
66import com.android.internal.util.AsyncChannel;
67import com.android.internal.util.Protocol;
68import com.android.internal.util.State;
69import com.android.internal.util.StateMachine;
70
71import java.io.FileDescriptor;
72import java.io.PrintWriter;
73import java.util.Collection;
74
75/**
76 * WifiP2pService inclues a state machine to perform Wi-Fi p2p operations. Applications
77 * communicate with this service to issue device discovery and connectivity requests
78 * through the WifiP2pManager interface. The state machine communicates with the wifi
79 * driver through wpa_supplicant and handles the event responses through WifiMonitor.
80 *
81 * Note that the term Wifi when used without a p2p suffix refers to the client mode
82 * of Wifi operation
83 * @hide
84 */
85public class WifiP2pService extends IWifiP2pManager.Stub {
86    private static final String TAG = "WifiP2pService";
87    private static final boolean DBG = true;
88    private static final String NETWORKTYPE = "WIFI_P2P";
89
90    private Context mContext;
91    private String mInterface;
92    private Notification mNotification;
93
94    INetworkManagementService mNwService;
95    private DhcpStateMachine mDhcpStateMachine;
96
97    //Tracked to notify the user about wifi client/hotspot being shut down
98    //during p2p bring up
99    private int mWifiState = WifiManager.WIFI_STATE_DISABLED;
100    private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
101
102    private P2pStateMachine mP2pStateMachine;
103    private AsyncChannel mReplyChannel = new AsyncChannel();
104    private AsyncChannel mWifiChannel;
105
106    private static final Boolean JOIN_GROUP = true;
107    private static final Boolean FORM_GROUP = false;
108
109    /* Two minutes comes from the wpa_supplicant setting */
110    private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
111    private static int mGroupCreatingTimeoutIndex = 0;
112
113    /**
114     * Delay between restarts upon failure to setup connection with supplicant
115     */
116    private static final int P2P_RESTART_INTERVAL_MSECS = 5000;
117
118    /**
119     * Number of times we attempt to restart p2p
120     */
121    private static final int P2P_RESTART_TRIES = 5;
122
123    private int mP2pRestartCount = 0;
124
125    private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
126
127    /* Message sent to WifiStateMachine to indicate p2p enable is pending */
128    public static final int P2P_ENABLE_PENDING              =   BASE + 1;
129    /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */
130    public static final int WIFI_ENABLE_PROCEED             =   BASE + 2;
131
132    /* Delayed message to timeout group creation */
133    public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 3;
134
135    /* User accepted to disable Wi-Fi in order to enable p2p */
136    private static final int WIFI_DISABLE_USER_ACCEPT       =   BASE + 4;
137    /* User rejected to disable Wi-Fi in order to enable p2p */
138    private static final int WIFI_DISABLE_USER_REJECT       =   BASE + 5;
139
140    /* User accepted a peer request */
141    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 6;
142    /* User rejected a peer request */
143    private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 7;
144
145    /* Airplane mode changed */
146    private static final int AIRPLANE_MODE_CHANGED          =   BASE + 8;
147    /* Emergency callback mode */
148    private static final int EMERGENCY_CALLBACK_MODE        =   BASE + 9;
149
150    private final boolean mP2pSupported;
151
152    private WifiP2pDevice mThisDevice = new WifiP2pDevice();
153
154    /* When a group has been explicitly created by an app, we persist the group
155     * even after all clients have been disconnected until an explicit remove
156     * is invoked */
157    private boolean mPersistGroup;
158
159    private NetworkInfo mNetworkInfo;
160
161    /* Is chosen as a unique range to avoid conflict with
162       the range defined in Tethering.java */
163    private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"};
164    private static final String SERVER_ADDRESS = "192.168.49.1";
165
166    public WifiP2pService(Context context) {
167        mContext = context;
168
169        mInterface = SystemProperties.get("wifi.interface", "wlan0");
170        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
171
172        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
173                PackageManager.FEATURE_WIFI_DIRECT);
174
175        mThisDevice.primaryDeviceType = mContext.getResources().getString(
176                com.android.internal.R.string.config_wifi_p2p_device_type);
177        mThisDevice.deviceName = getDefaultDeviceName();
178
179        mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
180        mP2pStateMachine.start();
181
182        // broadcasts
183        IntentFilter filter = new IntentFilter();
184        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
185        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
186        filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
187        filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
188        mContext.registerReceiver(new WifiStateReceiver(), filter);
189
190    }
191
192    public void connectivityServiceReady() {
193        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
194        mNwService = INetworkManagementService.Stub.asInterface(b);
195    }
196
197    private class WifiStateReceiver extends BroadcastReceiver {
198        @Override
199        public void onReceive(Context context, Intent intent) {
200            String action = intent.getAction();
201            if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
202                mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
203                        WifiManager.WIFI_STATE_DISABLED);
204            } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
205                mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
206                        WifiManager.WIFI_AP_STATE_DISABLED);
207            } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
208                mP2pStateMachine.sendMessage(AIRPLANE_MODE_CHANGED);
209            } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
210                if (intent.getBooleanExtra("phoneinECMState", false) == true) {
211                    mP2pStateMachine.sendMessage(EMERGENCY_CALLBACK_MODE);
212                }
213            }
214        }
215    }
216
217    private void enforceAccessPermission() {
218        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
219                "WifiP2pService");
220    }
221
222    private void enforceChangePermission() {
223        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
224                "WifiP2pService");
225    }
226
227    /* We use the 4 digits of the ANDROID_ID to have a friendly
228     * default that has low likelihood of collision with a peer */
229    private String getDefaultDeviceName() {
230        String id = Settings.Secure.getString(mContext.getContentResolver(),
231                    Settings.Secure.ANDROID_ID);
232        return "Android_" + id.substring(0,4);
233    }
234
235    /**
236     * Get a reference to handler. This is used by a client to establish
237     * an AsyncChannel communication with WifiP2pService
238     */
239    public Messenger getMessenger() {
240        enforceAccessPermission();
241        enforceChangePermission();
242        return new Messenger(mP2pStateMachine.getHandler());
243    }
244
245    @Override
246    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
247        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
248                != PackageManager.PERMISSION_GRANTED) {
249            pw.println("Permission Denial: can't dump WifiP2pService from from pid="
250                    + Binder.getCallingPid()
251                    + ", uid=" + Binder.getCallingUid());
252            return;
253        }
254    }
255
256
257    /**
258     * Handles interaction with WifiStateMachine
259     */
260    private class P2pStateMachine extends StateMachine {
261
262        private DefaultState mDefaultState = new DefaultState();
263        private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
264        private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
265        private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
266        private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState();
267        private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState();
268        private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
269        private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
270        // Inactive is when p2p is enabled with no connectivity
271        private InactiveState mInactiveState = new InactiveState();
272        private GroupCreatingState mGroupCreatingState = new GroupCreatingState();
273        private UserAuthorizingInvitationState mUserAuthorizingInvitationState
274                = new UserAuthorizingInvitationState();
275        private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState();
276        private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
277
278        private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
279        private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState();
280
281        private WifiMonitor mWifiMonitor = new WifiMonitor(this);
282
283        private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
284        private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
285        private WifiP2pGroup mGroup;
286
287        // Saved WifiP2pConfig for a peer connection
288        private WifiP2pConfig mSavedPeerConfig;
289
290        // Saved WifiP2pGroup from invitation request
291        private WifiP2pGroup mSavedP2pGroup;
292
293        P2pStateMachine(String name, boolean p2pSupported) {
294            super(name);
295
296            addState(mDefaultState);
297                addState(mP2pNotSupportedState, mDefaultState);
298                addState(mP2pDisablingState, mDefaultState);
299                addState(mP2pDisabledState, mDefaultState);
300                    addState(mWaitForUserActionState, mP2pDisabledState);
301                    addState(mWaitForWifiDisableState, mP2pDisabledState);
302                addState(mP2pEnablingState, mDefaultState);
303                addState(mP2pEnabledState, mDefaultState);
304                    addState(mInactiveState, mP2pEnabledState);
305                    addState(mGroupCreatingState, mP2pEnabledState);
306                        addState(mUserAuthorizingInvitationState, mGroupCreatingState);
307                        addState(mProvisionDiscoveryState, mGroupCreatingState);
308                        addState(mGroupNegotiationState, mGroupCreatingState);
309                    addState(mGroupCreatedState, mP2pEnabledState);
310                        addState(mUserAuthorizingJoinState, mGroupCreatedState);
311
312            if (p2pSupported) {
313                setInitialState(mP2pDisabledState);
314            } else {
315                setInitialState(mP2pNotSupportedState);
316            }
317        }
318
319    class DefaultState extends State {
320        @Override
321        public boolean processMessage(Message message) {
322            if (DBG) logd(getName() + message.toString());
323            switch (message.what) {
324                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
325                    if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
326                        if (DBG) logd("Full connection with WifiStateMachine established");
327                        mWifiChannel = (AsyncChannel) message.obj;
328                    } else {
329                        loge("Full connection failure, error = " + message.arg1);
330                        mWifiChannel = null;
331                    }
332                    break;
333
334                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
335                    if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
336                        loge("Send failed, client connection lost");
337                    } else {
338                        loge("Client connection lost with reason: " + message.arg1);
339                    }
340                    mWifiChannel = null;
341                    break;
342
343                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
344                    AsyncChannel ac = new AsyncChannel();
345                    ac.connect(mContext, getHandler(), message.replyTo);
346                    break;
347                case WifiStateMachine.WIFI_ENABLE_PENDING:
348                    // Disable p2p operation before we can respond
349                    sendMessage(WifiP2pManager.DISABLE_P2P);
350                    deferMessage(message);
351                    break;
352                case WifiP2pManager.ENABLE_P2P:
353                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
354                            WifiP2pManager.BUSY);
355                    break;
356                case WifiP2pManager.DISABLE_P2P:
357                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
358                            WifiP2pManager.BUSY);
359                    break;
360                case WifiP2pManager.DISCOVER_PEERS:
361                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
362                            WifiP2pManager.BUSY);
363                    break;
364                case WifiP2pManager.CONNECT:
365                    replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
366                            WifiP2pManager.BUSY);
367                    break;
368                case WifiP2pManager.CANCEL_CONNECT:
369                    replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
370                            WifiP2pManager.BUSY);
371                    break;
372                case WifiP2pManager.CREATE_GROUP:
373                    replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
374                            WifiP2pManager.BUSY);
375                    break;
376                case WifiP2pManager.REMOVE_GROUP:
377                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
378                            WifiP2pManager.BUSY);
379                    break;
380                case WifiP2pManager.REQUEST_PEERS:
381                    replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
382                    break;
383                case WifiP2pManager.REQUEST_CONNECTION_INFO:
384                    replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, mWifiP2pInfo);
385                    break;
386                case WifiP2pManager.REQUEST_GROUP_INFO:
387                    replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
388                    break;
389                case AIRPLANE_MODE_CHANGED:
390                    if (isAirplaneModeOn()) sendMessage(WifiP2pManager.DISABLE_P2P);
391                    break;
392                case EMERGENCY_CALLBACK_MODE:
393                    sendMessage(WifiP2pManager.DISABLE_P2P);
394                    break;
395                    // Ignore
396                case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
397                case WIFI_DISABLE_USER_ACCEPT:
398                case WIFI_DISABLE_USER_REJECT:
399                case PEER_CONNECTION_USER_ACCEPT:
400                case PEER_CONNECTION_USER_REJECT:
401                case GROUP_CREATING_TIMED_OUT:
402                    break;
403                default:
404                    loge("Unhandled message " + message);
405                    return NOT_HANDLED;
406            }
407            return HANDLED;
408        }
409    }
410
411    class P2pNotSupportedState extends State {
412        @Override
413        public boolean processMessage(Message message) {
414            switch (message.what) {
415                // Allow Wi-Fi to proceed
416                case WifiStateMachine.WIFI_ENABLE_PENDING:
417                    replyToMessage(message, WIFI_ENABLE_PROCEED);
418                    break;
419                case WifiP2pManager.ENABLE_P2P:
420                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
421                            WifiP2pManager.P2P_UNSUPPORTED);
422                    break;
423                case WifiP2pManager.DISABLE_P2P:
424                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
425                            WifiP2pManager.P2P_UNSUPPORTED);
426                    break;
427                case WifiP2pManager.DISCOVER_PEERS:
428                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
429                            WifiP2pManager.P2P_UNSUPPORTED);
430                    break;
431                case WifiP2pManager.CONNECT:
432                    replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
433                            WifiP2pManager.P2P_UNSUPPORTED);
434                    break;
435                case WifiP2pManager.CANCEL_CONNECT:
436                    replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
437                            WifiP2pManager.P2P_UNSUPPORTED);
438                    break;
439                case WifiP2pManager.CREATE_GROUP:
440                    replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
441                            WifiP2pManager.P2P_UNSUPPORTED);
442                    break;
443                case WifiP2pManager.REMOVE_GROUP:
444                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
445                            WifiP2pManager.P2P_UNSUPPORTED);
446                    break;
447               default:
448                    return NOT_HANDLED;
449            }
450            return HANDLED;
451        }
452    }
453
454    class P2pDisablingState extends State {
455        @Override
456        public void enter() {
457            if (DBG) logd(getName());
458            logd("stopping supplicant");
459            if (!WifiNative.stopSupplicant()) {
460                loge("Failed to stop supplicant, issue kill");
461                WifiNative.killSupplicant();
462            }
463        }
464
465        @Override
466        public boolean processMessage(Message message) {
467            if (DBG) logd(getName() + message.toString());
468            switch (message.what) {
469                case WifiMonitor.SUP_DISCONNECTION_EVENT:
470                    logd("Supplicant connection lost");
471                    WifiNative.closeSupplicantConnection();
472                    transitionTo(mP2pDisabledState);
473                    break;
474                case WifiP2pManager.ENABLE_P2P:
475                case WifiP2pManager.DISABLE_P2P:
476                    deferMessage(message);
477                    break;
478                default:
479                    return NOT_HANDLED;
480            }
481            return HANDLED;
482        }
483    }
484
485
486    class P2pDisabledState extends State {
487       @Override
488        public void enter() {
489            if (DBG) logd(getName());
490        }
491
492        @Override
493        public boolean processMessage(Message message) {
494            if (DBG) logd(getName() + message.toString());
495            switch (message.what) {
496                case WifiP2pManager.ENABLE_P2P:
497                    OnClickListener listener = new OnClickListener() {
498                        @Override
499                        public void onClick(DialogInterface dialog, int which) {
500                            if (which == DialogInterface.BUTTON_POSITIVE) {
501                                sendMessage(WIFI_DISABLE_USER_ACCEPT);
502                            } else {
503                                sendMessage(WIFI_DISABLE_USER_REJECT);
504                            }
505                        }
506                    };
507
508                    // Show a user request dialog if we know Wi-Fi client/hotspot is in operation
509                    if (mWifiState != WifiManager.WIFI_STATE_DISABLED ||
510                            mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) {
511                        Resources r = Resources.getSystem();
512                        AlertDialog dialog = new AlertDialog.Builder(mContext)
513                            .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
514                            .setMessage(r.getString(R.string.wifi_p2p_turnon_message))
515                            .setPositiveButton(r.getString(R.string.ok), listener)
516                            .setNegativeButton(r.getString(R.string.cancel), listener)
517                            .create();
518                        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
519                        dialog.show();
520                        transitionTo(mWaitForUserActionState);
521                    } else {
522                        mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
523                        transitionTo(mWaitForWifiDisableState);
524                    }
525                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
526                    break;
527                case WifiP2pManager.DISABLE_P2P:
528                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
529                    break;
530                case WifiStateMachine.WIFI_ENABLE_PENDING:
531                    replyToMessage(message, WIFI_ENABLE_PROCEED);
532                    break;
533                default:
534                    return NOT_HANDLED;
535            }
536            return HANDLED;
537        }
538    }
539
540    class WaitForUserActionState extends State {
541        @Override
542        public void enter() {
543            if (DBG) logd(getName());
544        }
545
546        @Override
547        public boolean processMessage(Message message) {
548            if (DBG) logd(getName() + message.toString());
549            switch (message.what) {
550                case WIFI_DISABLE_USER_ACCEPT:
551                    mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
552                    transitionTo(mWaitForWifiDisableState);
553                    break;
554                case WIFI_DISABLE_USER_REJECT:
555                    logd("User rejected enabling p2p");
556                    sendP2pStateChangedBroadcast(false);
557                    transitionTo(mP2pDisabledState);
558                    break;
559                case WifiP2pManager.ENABLE_P2P:
560                case WifiP2pManager.DISABLE_P2P:
561                    deferMessage(message);
562                    break;
563                default:
564                    return NOT_HANDLED;
565            }
566            return HANDLED;
567        }
568    }
569
570    class WaitForWifiDisableState extends State {
571        @Override
572        public void enter() {
573            if (DBG) logd(getName());
574        }
575
576        @Override
577        public boolean processMessage(Message message) {
578            if (DBG) logd(getName() + message.toString());
579            switch (message.what) {
580                case WifiStateMachine.P2P_ENABLE_PROCEED:
581                    try {
582                        mNwService.wifiFirmwareReload(mInterface, "P2P");
583                    } catch (Exception e) {
584                        loge("Failed to reload p2p firmware " + e);
585                        // continue
586                    }
587
588                    //A runtime crash can leave the interface up and
589                    //this affects p2p when supplicant starts up.
590                    //Ensure interface is down before a supplicant start.
591                    try {
592                        mNwService.setInterfaceDown(mInterface);
593                    } catch (Exception e) {
594                        if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e);
595                    }
596
597                    if (WifiNative.startP2pSupplicant()) {
598                        mWifiMonitor.startMonitoring();
599                        transitionTo(mP2pEnablingState);
600                    } else {
601                        notifyP2pEnableFailure();
602                        transitionTo(mP2pDisabledState);
603                    }
604                    break;
605                case WifiP2pManager.ENABLE_P2P:
606                case WifiP2pManager.DISABLE_P2P:
607                    deferMessage(message);
608                    break;
609                default:
610                    return NOT_HANDLED;
611            }
612            return HANDLED;
613        }
614    }
615
616    class P2pEnablingState extends State {
617        @Override
618        public void enter() {
619            if (DBG) logd(getName());
620        }
621
622        @Override
623        public boolean processMessage(Message message) {
624            if (DBG) logd(getName() + message.toString());
625            switch (message.what) {
626                case WifiMonitor.SUP_CONNECTION_EVENT:
627                    logd("P2p start successful");
628                    transitionTo(mInactiveState);
629                    break;
630                case WifiMonitor.SUP_DISCONNECTION_EVENT:
631                    if (++mP2pRestartCount <= P2P_RESTART_TRIES) {
632                        loge("Failed to start p2p, retry");
633                        WifiNative.killSupplicant();
634                        sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
635                    } else {
636                        loge("Failed " + mP2pRestartCount + " times to start p2p, quit ");
637                        mP2pRestartCount = 0;
638                    }
639                    transitionTo(mP2pDisabledState);
640                    break;
641                case WifiP2pManager.ENABLE_P2P:
642                case WifiP2pManager.DISABLE_P2P:
643                    deferMessage(message);
644                    break;
645                default:
646                    return NOT_HANDLED;
647            }
648            return HANDLED;
649        }
650    }
651
652    class P2pEnabledState extends State {
653        @Override
654        public void enter() {
655            if (DBG) logd(getName());
656            sendP2pStateChangedBroadcast(true);
657            mNetworkInfo.setIsAvailable(true);
658            initializeP2pSettings();
659            showNotification();
660        }
661
662        @Override
663        public boolean processMessage(Message message) {
664            if (DBG) logd(getName() + message.toString());
665            switch (message.what) {
666                case WifiP2pManager.ENABLE_P2P:
667                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
668                    break;
669                case WifiP2pManager.DISABLE_P2P:
670                    if (mPeers.clear()) sendP2pPeersChangedBroadcast();
671                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
672                    transitionTo(mP2pDisablingState);
673                    break;
674                case WifiP2pManager.DISCOVER_PEERS:
675                    int timeout = message.arg1;
676                    if (WifiNative.p2pFind(timeout)) {
677                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
678                    } else {
679                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
680                                WifiP2pManager.ERROR);
681                    }
682                    break;
683                case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
684                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
685                    if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
686                    mPeers.update(device);
687                    sendP2pPeersChangedBroadcast();
688                    break;
689                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
690                    device = (WifiP2pDevice) message.obj;
691                    if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
692                    break;
693               case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant died */
694                    loge("Connection lost, restart p2p");
695                    WifiNative.killSupplicant();
696                    WifiNative.closeSupplicantConnection();
697                    if (mPeers.clear()) sendP2pPeersChangedBroadcast();
698                    transitionTo(mP2pDisabledState);
699                    sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
700                    break;
701                default:
702                    return NOT_HANDLED;
703            }
704            return HANDLED;
705        }
706
707        @Override
708        public void exit() {
709            sendP2pStateChangedBroadcast(false);
710            mNetworkInfo.setIsAvailable(false);
711            clearNotification();
712        }
713    }
714
715    class InactiveState extends State {
716        @Override
717        public void enter() {
718            if (DBG) logd(getName());
719            //Start listening every time we get inactive
720            WifiNative.p2pListen();
721        }
722
723        @Override
724        public boolean processMessage(Message message) {
725            if (DBG) logd(getName() + message.toString());
726            switch (message.what) {
727                case WifiP2pManager.CONNECT:
728                    if (DBG) logd(getName() + " sending connect");
729                    mSavedPeerConfig = (WifiP2pConfig) message.obj;
730                    String updatedPeerDetails = WifiNative.p2pPeer(mSavedPeerConfig.deviceAddress);
731                    mPeers.update(new WifiP2pDevice(updatedPeerDetails));
732                    mPersistGroup = false;
733                    int netId = configuredNetworkId(mSavedPeerConfig.deviceAddress);
734                    if (netId >= 0) {
735                        //TODO: if failure, remove config and do a regular p2pConnect()
736                        WifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress);
737                    } else {
738                        //If peer is a GO, we do not need to send provisional discovery,
739                        //the supplicant takes care of it.
740                        if (isGroupOwner(mSavedPeerConfig.deviceAddress)) {
741                            p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
742                            transitionTo(mGroupNegotiationState);
743                        } else {
744                            transitionTo(mProvisionDiscoveryState);
745                        }
746                    }
747                    updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
748                    sendP2pPeersChangedBroadcast();
749                    replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
750                    break;
751                case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
752                    mSavedPeerConfig = (WifiP2pConfig) message.obj;
753                    transitionTo(mUserAuthorizingInvitationState);
754                    break;
755                case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
756                    WifiP2pGroup group = (WifiP2pGroup) message.obj;
757                    WifiP2pDevice owner = group.getOwner();
758
759                    if (owner == null) {
760                        if (DBG) loge("Ignored invitation from null owner");
761                        break;
762                    }
763
764                    mSavedPeerConfig = new WifiP2pConfig();
765                    mSavedPeerConfig.deviceAddress = group.getOwner().deviceAddress;
766
767                    //Check if we have the owner in peer list and use appropriate
768                    //wps method. Default is to use PBC.
769                    if ((owner = getDeviceFromPeerList(owner.deviceAddress)) != null) {
770                        if (owner.wpsPbcSupported()) {
771                            mSavedPeerConfig.wps.setup = WpsInfo.PBC;
772                        } else if (owner.wpsKeypadSupported()) {
773                            mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
774                        } else if (owner.wpsDisplaySupported()) {
775                            mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
776                        }
777                    }
778                    transitionTo(mUserAuthorizingInvitationState);
779                    break;
780                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
781                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
782                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
783                    WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
784                    mSavedPeerConfig = new WifiP2pConfig();
785                    mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
786                    if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
787                        mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
788                        if (DBG) logd("Keypad prov disc request");
789                    } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
790                        mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
791                        mSavedPeerConfig.wps.pin = provDisc.pin;
792                        if (DBG) logd("Display prov disc request");
793                    } else {
794                        mSavedPeerConfig.wps.setup = WpsInfo.PBC;
795                        if (DBG) logd("PBC prov disc request");
796                    }
797                    transitionTo(mUserAuthorizingInvitationState);
798                    break;
799                case WifiP2pManager.CREATE_GROUP:
800                    mPersistGroup = true;
801                    if (WifiNative.p2pGroupAdd()) {
802                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
803                    } else {
804                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
805                                WifiP2pManager.ERROR);
806                    }
807                    transitionTo(mGroupNegotiationState);
808                    break;
809               default:
810                    return NOT_HANDLED;
811            }
812            return HANDLED;
813        }
814    }
815
816    class GroupCreatingState extends State {
817        @Override
818        public void enter() {
819            if (DBG) logd(getName());
820            sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,
821                    ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);
822        }
823
824        @Override
825        public boolean processMessage(Message message) {
826            if (DBG) logd(getName() + message.toString());
827            switch (message.what) {
828               case GROUP_CREATING_TIMED_OUT:
829                    if (mGroupCreatingTimeoutIndex == message.arg1) {
830                        if (DBG) logd("Group negotiation timed out");
831                        updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.FAILED);
832                        mSavedPeerConfig = null;
833                        sendP2pPeersChangedBroadcast();
834                        transitionTo(mInactiveState);
835                    }
836                    break;
837                case WifiP2pManager.DISCOVER_PEERS:
838                    /* Discovery will break negotiation */
839                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
840                            WifiP2pManager.BUSY);
841                    break;
842                case WifiP2pManager.CANCEL_CONNECT:
843                    if (WifiNative.p2pCancelConnect()) {
844                        replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
845                    } else {
846                        replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
847                                WifiP2pManager.ERROR);
848                    }
849                    break;
850                default:
851                    return NOT_HANDLED;
852            }
853            return HANDLED;
854        }
855    }
856
857    class UserAuthorizingInvitationState extends State {
858        @Override
859        public void enter() {
860            if (DBG) logd(getName());
861            notifyInvitationReceived();
862        }
863
864        @Override
865        public boolean processMessage(Message message) {
866            if (DBG) logd(getName() + message.toString());
867            switch (message.what) {
868                case PEER_CONNECTION_USER_ACCEPT:
869                    //TODO: handle persistence
870                    if (isGroupOwner(mSavedPeerConfig.deviceAddress)) {
871                        p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
872                    } else {
873                        p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
874                    }
875                    updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
876                    sendP2pPeersChangedBroadcast();
877                    transitionTo(mGroupNegotiationState);
878                    break;
879                case PEER_CONNECTION_USER_REJECT:
880                    if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
881                    mSavedPeerConfig = null;
882                    transitionTo(mInactiveState);
883                    break;
884                default:
885                    return NOT_HANDLED;
886            }
887            return HANDLED;
888        }
889
890        @Override
891        public void exit() {
892            //TODO: dismiss dialog if not already done
893        }
894    }
895
896    class ProvisionDiscoveryState extends State {
897        @Override
898        public void enter() {
899            if (DBG) logd(getName());
900            WifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
901        }
902
903        @Override
904        public boolean processMessage(Message message) {
905            if (DBG) logd(getName() + message.toString());
906            WifiP2pProvDiscEvent provDisc;
907            WifiP2pDevice device;
908            switch (message.what) {
909                case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
910                    provDisc = (WifiP2pProvDiscEvent) message.obj;
911                    device = provDisc.device;
912                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
913
914                    if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
915                        if (DBG) logd("Found a match " + mSavedPeerConfig);
916                        WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
917                        transitionTo(mGroupNegotiationState);
918                    }
919                    break;
920                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
921                    provDisc = (WifiP2pProvDiscEvent) message.obj;
922                    device = provDisc.device;
923                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
924
925                    if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) {
926                        if (DBG) logd("Found a match " + mSavedPeerConfig);
927                        /* we already have the pin */
928                        if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
929                            WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
930                            transitionTo(mGroupNegotiationState);
931                        } else {
932                            transitionTo(mUserAuthorizingInvitationState);
933                        }
934                    }
935                    break;
936                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
937                    provDisc = (WifiP2pProvDiscEvent) message.obj;
938                    device = provDisc.device;
939                    if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
940
941                    if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
942                        if (DBG) logd("Found a match " + mSavedPeerConfig);
943                        mSavedPeerConfig.wps.pin = provDisc.pin;
944                        WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
945                        notifyInvitationSent(provDisc.pin, device.deviceAddress);
946                        transitionTo(mGroupNegotiationState);
947                    }
948                    break;
949                default:
950                    return NOT_HANDLED;
951            }
952            return HANDLED;
953        }
954    }
955
956    class GroupNegotiationState extends State {
957        @Override
958        public void enter() {
959            if (DBG) logd(getName());
960        }
961
962        @Override
963        public boolean processMessage(Message message) {
964            if (DBG) logd(getName() + message.toString());
965            switch (message.what) {
966                // We ignore these right now, since we get a GROUP_STARTED notification
967                // afterwards
968                case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
969                case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
970                    if (DBG) logd(getName() + " go success");
971                    break;
972                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
973                    mGroup = (WifiP2pGroup) message.obj;
974                    if (DBG) logd(getName() + " group started");
975                    if (mGroup.isGroupOwner()) {
976                        startDhcpServer(mGroup.getInterface());
977                    } else {
978                        mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
979                                P2pStateMachine.this, mGroup.getInterface());
980                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
981                        WifiP2pDevice groupOwner = mGroup.getOwner();
982                        updateDeviceStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
983                        sendP2pPeersChangedBroadcast();
984                    }
985                    mSavedPeerConfig = null;
986                    transitionTo(mGroupCreatedState);
987                    break;
988                case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
989                case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
990                    if (DBG) logd(getName() + " go failure");
991                    updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.FAILED);
992                    mSavedPeerConfig = null;
993                    sendP2pPeersChangedBroadcast();
994                    transitionTo(mInactiveState);
995                    break;
996                default:
997                    return NOT_HANDLED;
998            }
999            return HANDLED;
1000        }
1001    }
1002
1003
1004
1005    class GroupCreatedState extends State {
1006        @Override
1007        public void enter() {
1008            if (DBG) logd(getName());
1009            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
1010
1011            updateThisDevice(WifiP2pDevice.CONNECTED);
1012
1013            //DHCP server has already been started if I am a group owner
1014            if (mGroup.isGroupOwner()) {
1015                setWifiP2pInfoOnGroupFormation(SERVER_ADDRESS);
1016                sendP2pConnectionChangedBroadcast();
1017            }
1018        }
1019
1020        @Override
1021        public boolean processMessage(Message message) {
1022            if (DBG) logd(getName() + message.toString());
1023            switch (message.what) {
1024                case WifiMonitor.AP_STA_CONNECTED_EVENT:
1025                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
1026                    String deviceAddress = device.deviceAddress;
1027                    if (deviceAddress != null) {
1028                        mGroup.addClient(deviceAddress);
1029                        mPeers.updateInterfaceAddress(device);
1030                        updateDeviceStatus(deviceAddress, WifiP2pDevice.CONNECTED);
1031                        if (DBG) logd(getName() + " ap sta connected");
1032                        sendP2pPeersChangedBroadcast();
1033                    } else {
1034                        loge("Connect on null device address, ignore");
1035                    }
1036                    break;
1037                case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
1038                    //TODO: the disconnection event is still inconsistent and reports
1039                    //interface address. Fix this after wpa_supplicant is fixed.
1040                    String interfaceAddress = (String) message.obj;
1041                    deviceAddress = getDeviceAddress(interfaceAddress);
1042                    if (deviceAddress != null) {
1043                        updateDeviceStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
1044                        if (mGroup.removeClient(deviceAddress)) {
1045                            if (DBG) logd("Removed client " + deviceAddress);
1046                            if (!mPersistGroup && mGroup.isClientListEmpty()) {
1047                                Slog.d(TAG, "Client list empty, remove non-persistent p2p group");
1048                                WifiNative.p2pGroupRemove(mGroup.getInterface());
1049                            }
1050                        } else {
1051                            if (DBG) logd("Failed to remove client " + deviceAddress);
1052                            for (WifiP2pDevice c : mGroup.getClientList()) {
1053                                if (DBG) logd("client " + c.deviceAddress);
1054                            }
1055                        }
1056                        sendP2pPeersChangedBroadcast();
1057                        if (DBG) loge(getName() + " ap sta disconnected");
1058                    } else {
1059                        loge("Disconnect on unknown interface address : " + interfaceAddress);
1060                    }
1061                    break;
1062                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
1063                    DhcpInfoInternal dhcpInfo = (DhcpInfoInternal) message.obj;
1064                    if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS &&
1065                            dhcpInfo != null) {
1066                        if (DBG) logd("DhcpInfo: " + dhcpInfo);
1067                        setWifiP2pInfoOnGroupFormation(dhcpInfo.serverAddress);
1068                        sendP2pConnectionChangedBroadcast();
1069                    } else {
1070                        WifiNative.p2pGroupRemove(mGroup.getInterface());
1071                    }
1072                    break;
1073                case WifiP2pManager.REMOVE_GROUP:
1074                    if (DBG) loge(getName() + " remove group");
1075                    if (WifiNative.p2pGroupRemove(mGroup.getInterface())) {
1076                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
1077                    } else {
1078                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
1079                                WifiP2pManager.ERROR);
1080                    }
1081                    break;
1082                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1083                    if (DBG) loge(getName() + " group removed");
1084                    Collection <WifiP2pDevice> devices = mGroup.getClientList();
1085                    boolean changed = false;
1086                    for (WifiP2pDevice d : mPeers.getDeviceList()) {
1087                        if (devices.contains(d) || mGroup.getOwner().equals(d)) {
1088                            d.status = WifiP2pDevice.AVAILABLE;
1089                            changed = true;
1090                        }
1091                    }
1092
1093                    if (mGroup.isGroupOwner()) {
1094                        stopDhcpServer();
1095                    } else {
1096                        if (DBG) logd("stop DHCP client");
1097                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
1098                        mDhcpStateMachine.quit();
1099                        mDhcpStateMachine = null;
1100                    }
1101
1102                    mGroup = null;
1103                    if (changed) sendP2pPeersChangedBroadcast();
1104                    transitionTo(mInactiveState);
1105                    break;
1106                case WifiMonitor.P2P_DEVICE_LOST_EVENT:
1107                    device = (WifiP2pDevice) message.obj;
1108                    //Device loss for a connected device indicates it is not in discovery any more
1109                    if (mGroup.contains(device)) {
1110                        if (DBG) logd("Lost " + device +" , do nothing");
1111                        return HANDLED;
1112                    }
1113                    // Do the regular device lost handling
1114                    return NOT_HANDLED;
1115                case WifiP2pManager.DISABLE_P2P:
1116                    sendMessage(WifiP2pManager.REMOVE_GROUP);
1117                    deferMessage(message);
1118                    break;
1119                case WifiP2pManager.CONNECT:
1120                    WifiP2pConfig config = (WifiP2pConfig) message.obj;
1121                    logd("Inviting device : " + config.deviceAddress);
1122                    if (WifiNative.p2pInvite(mGroup, config.deviceAddress)) {
1123                        updateDeviceStatus(config.deviceAddress, WifiP2pDevice.INVITED);
1124                        sendP2pPeersChangedBroadcast();
1125                        replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
1126                    } else {
1127                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
1128                                WifiP2pManager.ERROR);
1129                    }
1130                    // TODO: figure out updating the status to declined when invitation is rejected
1131                    break;
1132                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1133                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1134                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1135                    WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
1136                    mSavedPeerConfig = new WifiP2pConfig();
1137                    mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
1138                    if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
1139                        mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
1140                    } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
1141                        mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
1142                        mSavedPeerConfig.wps.pin = provDisc.pin;
1143                    } else {
1144                        mSavedPeerConfig.wps.setup = WpsInfo.PBC;
1145                    }
1146                    transitionTo(mUserAuthorizingJoinState);
1147                    break;
1148                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1149                    Slog.e(TAG, "Duplicate group creation event notice, ignore");
1150                    break;
1151                default:
1152                    return NOT_HANDLED;
1153            }
1154            return HANDLED;
1155        }
1156
1157        public void exit() {
1158            updateThisDevice(WifiP2pDevice.AVAILABLE);
1159            setWifiP2pInfoOnGroupTermination();
1160            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
1161            sendP2pConnectionChangedBroadcast();
1162        }
1163    }
1164
1165    class UserAuthorizingJoinState extends State {
1166        @Override
1167        public void enter() {
1168            if (DBG) logd(getName());
1169            notifyInvitationReceived();
1170        }
1171
1172        @Override
1173        public boolean processMessage(Message message) {
1174            if (DBG) logd(getName() + message.toString());
1175            switch (message.what) {
1176                case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1177                case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1178                case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1179                    //Ignore more client requests
1180                    break;
1181                case PEER_CONNECTION_USER_ACCEPT:
1182                    if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
1183                        WifiNative.startWpsPbc();
1184                    } else {
1185                        WifiNative.startWpsPinKeypad(mSavedPeerConfig.wps.pin);
1186                    }
1187                    mSavedPeerConfig = null;
1188                    transitionTo(mGroupCreatedState);
1189                    break;
1190                case PEER_CONNECTION_USER_REJECT:
1191                    if (DBG) logd("User rejected incoming request");
1192                    mSavedPeerConfig = null;
1193                    transitionTo(mGroupCreatedState);
1194                    break;
1195                default:
1196                    return NOT_HANDLED;
1197            }
1198            return HANDLED;
1199        }
1200
1201        @Override
1202        public void exit() {
1203            //TODO: dismiss dialog if not already done
1204        }
1205    }
1206
1207    private void sendP2pStateChangedBroadcast(boolean enabled) {
1208        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
1209        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1210        if (enabled) {
1211            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
1212                    WifiP2pManager.WIFI_P2P_STATE_ENABLED);
1213        } else {
1214            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
1215                    WifiP2pManager.WIFI_P2P_STATE_DISABLED);
1216        }
1217        mContext.sendStickyBroadcast(intent);
1218    }
1219
1220    private void sendThisDeviceChangedBroadcast() {
1221        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
1222        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1223        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
1224        mContext.sendStickyBroadcast(intent);
1225    }
1226
1227    private void sendP2pPeersChangedBroadcast() {
1228        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
1229        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1230        mContext.sendBroadcast(intent);
1231    }
1232
1233    private void sendP2pConnectionChangedBroadcast() {
1234        if (DBG) logd("sending p2p connection changed broadcast");
1235        Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
1236        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1237                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1238        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
1239        intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
1240        mContext.sendStickyBroadcast(intent);
1241    }
1242
1243    private void startDhcpServer(String intf) {
1244        InterfaceConfiguration ifcg = null;
1245        try {
1246            ifcg = mNwService.getInterfaceConfig(intf);
1247            ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(
1248                        SERVER_ADDRESS), 24));
1249            ifcg.setInterfaceUp();
1250            mNwService.setInterfaceConfig(intf, ifcg);
1251            /* This starts the dnsmasq server */
1252            mNwService.startTethering(DHCP_RANGE);
1253        } catch (Exception e) {
1254            loge("Error configuring interface " + intf + ", :" + e);
1255            return;
1256        }
1257
1258        logd("Started Dhcp server on " + intf);
1259   }
1260
1261    private void stopDhcpServer() {
1262        try {
1263            mNwService.stopTethering();
1264        } catch (Exception e) {
1265            loge("Error stopping Dhcp server" + e);
1266            return;
1267        }
1268
1269        logd("Stopped Dhcp server");
1270    }
1271
1272    private void notifyP2pEnableFailure() {
1273        Resources r = Resources.getSystem();
1274        AlertDialog dialog = new AlertDialog.Builder(mContext)
1275            .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
1276            .setMessage(r.getString(R.string.wifi_p2p_failed_message))
1277            .setPositiveButton(r.getString(R.string.ok), null)
1278            .create();
1279        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1280        dialog.show();
1281    }
1282
1283    private void addRowToDialog(ViewGroup group, int stringId, String value) {
1284        Resources r = Resources.getSystem();
1285        View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row,
1286                group, false);
1287        ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId));
1288        ((TextView) row.findViewById(R.id.value)).setText(value);
1289        group.addView(row);
1290    }
1291
1292    private void notifyInvitationSent(String pin, String peerAddress) {
1293        Resources r = Resources.getSystem();
1294
1295        final View textEntryView = LayoutInflater.from(mContext)
1296                .inflate(R.layout.wifi_p2p_dialog, null);
1297
1298        ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
1299        addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress));
1300        addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin);
1301
1302        AlertDialog dialog = new AlertDialog.Builder(mContext)
1303            .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title))
1304            .setView(textEntryView)
1305            .setPositiveButton(r.getString(R.string.ok), null)
1306            .create();
1307        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1308        dialog.show();
1309    }
1310
1311    private void notifyInvitationReceived() {
1312        Resources r = Resources.getSystem();
1313        final WpsInfo wps = mSavedPeerConfig.wps;
1314        final View textEntryView = LayoutInflater.from(mContext)
1315                .inflate(R.layout.wifi_p2p_dialog, null);
1316
1317        ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
1318        addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName(
1319                mSavedPeerConfig.deviceAddress));
1320
1321        final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin);
1322
1323        AlertDialog dialog = new AlertDialog.Builder(mContext)
1324            .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title))
1325            .setView(textEntryView)
1326            .setPositiveButton(r.getString(R.string.accept), new OnClickListener() {
1327                        public void onClick(DialogInterface dialog, int which) {
1328                            if (wps.setup == WpsInfo.KEYPAD) {
1329                                mSavedPeerConfig.wps.pin = pin.getText().toString();
1330                            }
1331                            if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig);
1332                            sendMessage(PEER_CONNECTION_USER_ACCEPT);
1333                        }
1334                    })
1335            .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
1336                        @Override
1337                        public void onClick(DialogInterface dialog, int which) {
1338                            if (DBG) logd(getName() + " ignore connect");
1339                            sendMessage(PEER_CONNECTION_USER_REJECT);
1340                        }
1341                    })
1342            .create();
1343
1344        //make the enter pin area or the display pin area visible
1345        switch (wps.setup) {
1346            case WpsInfo.KEYPAD:
1347                if (DBG) logd("Enter pin section visible");
1348                textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE);
1349                break;
1350            case WpsInfo.DISPLAY:
1351                if (DBG) logd("Shown pin section visible");
1352                addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin);
1353                break;
1354            default:
1355                break;
1356        }
1357
1358        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1359        dialog.show();
1360    }
1361
1362    private void updateDeviceStatus(String deviceAddress, int status) {
1363        for (WifiP2pDevice d : mPeers.getDeviceList()) {
1364            if (d.deviceAddress.equals(deviceAddress)) {
1365                d.status = status;
1366            }
1367        }
1368    }
1369
1370    private boolean isGroupOwner(String deviceAddress) {
1371        for (WifiP2pDevice d : mPeers.getDeviceList()) {
1372            if (d.deviceAddress.equals(deviceAddress)) {
1373                return d.isGroupOwner();
1374            }
1375        }
1376        return false;
1377    }
1378
1379    //TODO: implement when wpa_supplicant is fixed
1380    private int configuredNetworkId(String deviceAddress) {
1381        return -1;
1382    }
1383
1384    private void setWifiP2pInfoOnGroupFormation(String serverAddress) {
1385        mWifiP2pInfo.groupFormed = true;
1386        mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
1387        mWifiP2pInfo.groupOwnerAddress = NetworkUtils.numericToInetAddress(serverAddress);
1388    }
1389
1390    private void setWifiP2pInfoOnGroupTermination() {
1391        mWifiP2pInfo.groupFormed = false;
1392        mWifiP2pInfo.isGroupOwner = false;
1393        mWifiP2pInfo.groupOwnerAddress = null;
1394    }
1395
1396    private String getDeviceName(String deviceAddress) {
1397        for (WifiP2pDevice d : mPeers.getDeviceList()) {
1398            if (d.deviceAddress.equals(deviceAddress)) {
1399                return d.deviceName;
1400            }
1401        }
1402        //Treat the address as name if there is no match
1403        return deviceAddress;
1404    }
1405
1406    private String getDeviceAddress(String interfaceAddress) {
1407        for (WifiP2pDevice d : mPeers.getDeviceList()) {
1408            if (interfaceAddress.equals(d.interfaceAddress)) {
1409                return d.deviceAddress;
1410            }
1411        }
1412        return null;
1413    }
1414
1415    private WifiP2pDevice getDeviceFromPeerList(String deviceAddress) {
1416        for (WifiP2pDevice d : mPeers.getDeviceList()) {
1417            if (d.deviceAddress.equals(deviceAddress)) {
1418                return d;
1419            }
1420        }
1421        return null;
1422    }
1423
1424    private void p2pConnectWithPinDisplay(WifiP2pConfig config, boolean join) {
1425        String pin = WifiNative.p2pConnect(config, join);
1426        try {
1427            Integer.parseInt(pin);
1428            notifyInvitationSent(pin, config.deviceAddress);
1429        } catch (NumberFormatException ignore) {
1430            // do nothing if p2pConnect did not return a pin
1431        }
1432    }
1433
1434    private void initializeP2pSettings() {
1435        WifiNative.setPersistentReconnect(true);
1436        WifiNative.setDeviceName(mThisDevice.deviceName);
1437        //DIRECT-XY-DEVICENAME (XY is randomly generated)
1438        WifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
1439        WifiNative.setDeviceType(mThisDevice.primaryDeviceType);
1440        //The supplicant default is to support everything, but a bug necessitates
1441        //the framework to specify this explicitly
1442        WifiNative.setConfigMethods("keypad display push_button");
1443
1444        mThisDevice.deviceAddress = WifiNative.p2pGetDeviceAddress();
1445        updateThisDevice(WifiP2pDevice.AVAILABLE);
1446        if (DBG) Slog.d(TAG, "DeviceAddress: " + mThisDevice.deviceAddress);
1447    }
1448
1449    private void updateThisDevice(int status) {
1450        mThisDevice.status = status;
1451        sendThisDeviceChangedBroadcast();
1452    }
1453
1454    //State machine initiated requests can have replyTo set to null indicating
1455    //there are no recepients, we ignore those reply actions
1456    private void replyToMessage(Message msg, int what) {
1457        if (msg.replyTo == null) return;
1458        Message dstMsg = obtainMessage(msg);
1459        dstMsg.what = what;
1460        mReplyChannel.replyToMessage(msg, dstMsg);
1461    }
1462
1463    private void replyToMessage(Message msg, int what, int arg1) {
1464        if (msg.replyTo == null) return;
1465        Message dstMsg = obtainMessage(msg);
1466        dstMsg.what = what;
1467        dstMsg.arg1 = arg1;
1468        mReplyChannel.replyToMessage(msg, dstMsg);
1469    }
1470
1471    private void replyToMessage(Message msg, int what, Object obj) {
1472        if (msg.replyTo == null) return;
1473        Message dstMsg = obtainMessage(msg);
1474        dstMsg.what = what;
1475        dstMsg.obj = obj;
1476        mReplyChannel.replyToMessage(msg, dstMsg);
1477    }
1478
1479    /* arg2 on the source message has a hash code that needs to be retained in replies
1480     * see WifiP2pManager for details */
1481    private Message obtainMessage(Message srcMsg) {
1482        Message msg = Message.obtain();
1483        msg.arg2 = srcMsg.arg2;
1484        return msg;
1485    }
1486
1487    private void logd(String s) {
1488        Slog.d(TAG, s);
1489    }
1490
1491    private void loge(String s) {
1492        Slog.e(TAG, s);
1493    }
1494
1495    private void showNotification() {
1496        NotificationManager notificationManager =
1497            (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
1498        if (notificationManager == null || mNotification != null) {
1499            return;
1500        }
1501
1502        Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
1503        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
1504
1505        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
1506
1507        Resources r = Resources.getSystem();
1508        CharSequence title = r.getText(R.string.wifi_p2p_enabled_notification_title);
1509        CharSequence message = r.getText(R.string.wifi_p2p_enabled_notification_message);
1510
1511        mNotification = new Notification();
1512        mNotification.when = 0;
1513        //TODO: might change to be a seperate icon
1514        mNotification.icon = R.drawable.stat_sys_tether_wifi;
1515        mNotification.defaults &= ~Notification.DEFAULT_SOUND;
1516        mNotification.flags = Notification.FLAG_ONGOING_EVENT;
1517        mNotification.tickerText = title;
1518        mNotification.setLatestEventInfo(mContext, title, message, pi);
1519
1520        notificationManager.notify(mNotification.icon, mNotification);
1521    }
1522
1523    private void clearNotification() {
1524        NotificationManager notificationManager =
1525            (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
1526        if (notificationManager != null && mNotification != null) {
1527            notificationManager.cancel(mNotification.icon);
1528            mNotification = null;
1529        }
1530    }
1531
1532    private boolean isAirplaneSensitive() {
1533        String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1534                Settings.System.AIRPLANE_MODE_RADIOS);
1535        return airplaneModeRadios == null
1536            || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1537    }
1538
1539    private boolean isAirplaneModeOn() {
1540        return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
1541                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1542    }
1543
1544    }
1545}
1546