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