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