WifiP2pServiceImpl.java revision 67fd6d55895b602bab62f640618554cbb4aed7f3
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                        }
716                        break;
717
718                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
719                        if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
720                            loge("Send failed, client connection lost");
721                        } else {
722                            loge("Client connection lost with reason: " + message.arg1);
723                        }
724                        mWifiChannel = null;
725                        break;
726
727                    case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
728                        AsyncChannel ac = new WifiAsyncChannel(TAG);
729                        ac.connect(mContext, getHandler(), message.replyTo);
730                        break;
731                    case BLOCK_DISCOVERY:
732                        mDiscoveryBlocked = (message.arg1 == ENABLED ? true : false);
733                        // always reset this - we went to a state that doesn't support discovery so
734                        // it would have stopped regardless
735                        mDiscoveryPostponed = false;
736                        if (mDiscoveryBlocked) {
737                            try {
738                                StateMachine m = (StateMachine) message.obj;
739                                m.sendMessage(message.arg2);
740                            } catch (Exception e) {
741                                loge("unable to send BLOCK_DISCOVERY response: " + e);
742                            }
743                        }
744                        break;
745                    case WifiP2pManager.DISCOVER_PEERS:
746                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
747                                WifiP2pManager.BUSY);
748                        break;
749                    case WifiP2pManager.STOP_DISCOVERY:
750                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
751                                WifiP2pManager.BUSY);
752                        break;
753                    case WifiP2pManager.DISCOVER_SERVICES:
754                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
755                                WifiP2pManager.BUSY);
756                        break;
757                    case WifiP2pManager.CONNECT:
758                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
759                                WifiP2pManager.BUSY);
760                        break;
761                    case WifiP2pManager.CANCEL_CONNECT:
762                        replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
763                                WifiP2pManager.BUSY);
764                        break;
765                    case WifiP2pManager.CREATE_GROUP:
766                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
767                                WifiP2pManager.BUSY);
768                        break;
769                    case WifiP2pManager.REMOVE_GROUP:
770                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
771                                WifiP2pManager.BUSY);
772                        break;
773                    case WifiP2pManager.ADD_LOCAL_SERVICE:
774                        replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
775                                WifiP2pManager.BUSY);
776                        break;
777                    case WifiP2pManager.REMOVE_LOCAL_SERVICE:
778                        replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
779                                WifiP2pManager.BUSY);
780                        break;
781                    case WifiP2pManager.CLEAR_LOCAL_SERVICES:
782                        replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
783                                WifiP2pManager.BUSY);
784                        break;
785                    case WifiP2pManager.ADD_SERVICE_REQUEST:
786                        replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
787                                WifiP2pManager.BUSY);
788                        break;
789                    case WifiP2pManager.REMOVE_SERVICE_REQUEST:
790                        replyToMessage(message,
791                                WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
792                                WifiP2pManager.BUSY);
793                        break;
794                    case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
795                        replyToMessage(message,
796                                WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
797                                WifiP2pManager.BUSY);
798                        break;
799                    case WifiP2pManager.SET_DEVICE_NAME:
800                        replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
801                                WifiP2pManager.BUSY);
802                        break;
803                    case WifiP2pManager.DELETE_PERSISTENT_GROUP:
804                        replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
805                                WifiP2pManager.BUSY);
806                        break;
807                    case WifiP2pManager.SET_WFD_INFO:
808                        if (!getWfdPermission(message.sendingUid)) {
809                            replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
810                                    WifiP2pManager.ERROR);
811                        } else {
812                            replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
813                                    WifiP2pManager.BUSY);
814                        }
815                        break;
816                    case WifiP2pManager.REQUEST_PEERS:
817                        replyToMessage(message, WifiP2pManager.RESPONSE_PEERS,
818                                getPeers((Bundle) message.obj, message.sendingUid));
819                        break;
820                    case WifiP2pManager.REQUEST_CONNECTION_INFO:
821                        replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO,
822                                new WifiP2pInfo(mWifiP2pInfo));
823                        break;
824                    case WifiP2pManager.REQUEST_GROUP_INFO:
825                        replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO,
826                                mGroup != null ? new WifiP2pGroup(mGroup) : null);
827                        break;
828                    case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
829                        replyToMessage(message, WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO,
830                                new WifiP2pGroupList(mGroups, null));
831                        break;
832                    case WifiP2pManager.START_WPS:
833                        replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
834                                WifiP2pManager.BUSY);
835                        break;
836                    case WifiP2pManager.GET_HANDOVER_REQUEST:
837                    case WifiP2pManager.GET_HANDOVER_SELECT:
838                        replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE, null);
839                        break;
840                    case WifiP2pManager.INITIATOR_REPORT_NFC_HANDOVER:
841                    case WifiP2pManager.RESPONDER_REPORT_NFC_HANDOVER:
842                        replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED,
843                                WifiP2pManager.BUSY);
844                        break;
845                    case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
846                    case WifiMonitor.SCAN_RESULTS_EVENT:
847                    case WifiMonitor.SUP_CONNECTION_EVENT:
848                    case WifiMonitor.SUP_DISCONNECTION_EVENT:
849                    case WifiMonitor.NETWORK_CONNECTION_EVENT:
850                    case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
851                    case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
852                    case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
853                    case WifiMonitor.WPS_SUCCESS_EVENT:
854                    case WifiMonitor.WPS_FAIL_EVENT:
855                    case WifiMonitor.WPS_OVERLAP_EVENT:
856                    case WifiMonitor.WPS_TIMEOUT_EVENT:
857                    case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
858                    case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
859                    case WifiMonitor.P2P_DEVICE_LOST_EVENT:
860                    case WifiMonitor.P2P_FIND_STOPPED_EVENT:
861                    case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
862                    case PEER_CONNECTION_USER_ACCEPT:
863                    case PEER_CONNECTION_USER_REJECT:
864                    case DISCONNECT_WIFI_RESPONSE:
865                    case DROP_WIFI_USER_ACCEPT:
866                    case DROP_WIFI_USER_REJECT:
867                    case GROUP_CREATING_TIMED_OUT:
868                    case DISABLE_P2P_TIMED_OUT:
869                    case IPM_PRE_DHCP_ACTION:
870                    case IPM_POST_DHCP_ACTION:
871                    case IPM_DHCP_RESULTS:
872                    case IPM_PROVISIONING_SUCCESS:
873                    case IPM_PROVISIONING_FAILURE:
874                    case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
875                    case SET_MIRACAST_MODE:
876                    case WifiP2pManager.START_LISTEN:
877                    case WifiP2pManager.STOP_LISTEN:
878                    case WifiP2pManager.SET_CHANNEL:
879                    case WifiStateMachine.CMD_ENABLE_P2P:
880                        // Enable is lazy and has no response
881                        break;
882                    case WifiStateMachine.CMD_DISABLE_P2P_REQ:
883                        // If we end up handling in default, p2p is not enabled
884                        mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
885                        break;
886                    case WifiMonitor.P2P_GROUP_STARTED_EVENT:
887                        // unexpected group created, remove
888                        mGroup = (WifiP2pGroup) message.obj;
889                        loge("Unexpected group creation, remove " + mGroup);
890                        mWifiNative.p2pGroupRemove(mGroup.getInterface());
891                        break;
892                    case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
893                        // A group formation failure is always followed by
894                        // a group removed event. Flushing things at group formation
895                        // failure causes supplicant issues. Ignore right now.
896                        break;
897                    default:
898                        loge("Unhandled message " + message);
899                        return NOT_HANDLED;
900                }
901                return HANDLED;
902            }
903        }
904
905        class P2pNotSupportedState extends State {
906            @Override
907            public boolean processMessage(Message message) {
908                switch (message.what) {
909                    case WifiP2pManager.DISCOVER_PEERS:
910                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
911                                WifiP2pManager.P2P_UNSUPPORTED);
912                        break;
913                    case WifiP2pManager.STOP_DISCOVERY:
914                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
915                                WifiP2pManager.P2P_UNSUPPORTED);
916                        break;
917                    case WifiP2pManager.DISCOVER_SERVICES:
918                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
919                                WifiP2pManager.P2P_UNSUPPORTED);
920                        break;
921                    case WifiP2pManager.CONNECT:
922                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
923                                WifiP2pManager.P2P_UNSUPPORTED);
924                        break;
925                    case WifiP2pManager.CANCEL_CONNECT:
926                        replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
927                                WifiP2pManager.P2P_UNSUPPORTED);
928                        break;
929                    case WifiP2pManager.CREATE_GROUP:
930                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
931                                WifiP2pManager.P2P_UNSUPPORTED);
932                        break;
933                    case WifiP2pManager.REMOVE_GROUP:
934                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
935                                WifiP2pManager.P2P_UNSUPPORTED);
936                        break;
937                    case WifiP2pManager.ADD_LOCAL_SERVICE:
938                        replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
939                                WifiP2pManager.P2P_UNSUPPORTED);
940                        break;
941                    case WifiP2pManager.REMOVE_LOCAL_SERVICE:
942                        replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
943                                WifiP2pManager.P2P_UNSUPPORTED);
944                        break;
945                    case WifiP2pManager.CLEAR_LOCAL_SERVICES:
946                        replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
947                                WifiP2pManager.P2P_UNSUPPORTED);
948                        break;
949                    case WifiP2pManager.ADD_SERVICE_REQUEST:
950                        replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
951                                WifiP2pManager.P2P_UNSUPPORTED);
952                        break;
953                    case WifiP2pManager.REMOVE_SERVICE_REQUEST:
954                        replyToMessage(message,
955                                WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
956                                WifiP2pManager.P2P_UNSUPPORTED);
957                        break;
958                    case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
959                        replyToMessage(message,
960                                WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
961                                WifiP2pManager.P2P_UNSUPPORTED);
962                        break;
963                    case WifiP2pManager.SET_DEVICE_NAME:
964                        replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
965                                WifiP2pManager.P2P_UNSUPPORTED);
966                        break;
967                    case WifiP2pManager.DELETE_PERSISTENT_GROUP:
968                        replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
969                                WifiP2pManager.P2P_UNSUPPORTED);
970                        break;
971                    case WifiP2pManager.SET_WFD_INFO:
972                        if (!getWfdPermission(message.sendingUid)) {
973                            replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
974                                    WifiP2pManager.ERROR);
975                        } else {
976                            replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
977                                    WifiP2pManager.P2P_UNSUPPORTED);
978                        }
979                        break;
980                    case WifiP2pManager.START_WPS:
981                        replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
982                                WifiP2pManager.P2P_UNSUPPORTED);
983                        break;
984                    case WifiP2pManager.START_LISTEN:
985                        replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED,
986                                WifiP2pManager.P2P_UNSUPPORTED);
987                        break;
988                    case WifiP2pManager.STOP_LISTEN:
989                        replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED,
990                                WifiP2pManager.P2P_UNSUPPORTED);
991                        break;
992
993                    default:
994                        return NOT_HANDLED;
995                }
996                return HANDLED;
997            }
998        }
999
1000        class P2pDisablingState extends State {
1001            @Override
1002            public void enter() {
1003                if (DBG) logd(getName());
1004                sendMessageDelayed(obtainMessage(DISABLE_P2P_TIMED_OUT,
1005                        ++sDisableP2pTimeoutIndex, 0), DISABLE_P2P_WAIT_TIME_MS);
1006            }
1007
1008            @Override
1009            public boolean processMessage(Message message) {
1010                if (DBG) logd(getName() + message.toString());
1011                switch (message.what) {
1012                    case WifiMonitor.SUP_DISCONNECTION_EVENT:
1013                        if (DBG) logd("p2p socket connection lost");
1014                        transitionTo(mP2pDisabledState);
1015                        break;
1016                    case WifiStateMachine.CMD_ENABLE_P2P:
1017                    case WifiStateMachine.CMD_DISABLE_P2P_REQ:
1018                        deferMessage(message);
1019                        break;
1020                    case DISABLE_P2P_TIMED_OUT:
1021                        if (sDisableP2pTimeoutIndex == message.arg1) {
1022                            loge("P2p disable timed out");
1023                            transitionTo(mP2pDisabledState);
1024                        }
1025                        break;
1026                    default:
1027                        return NOT_HANDLED;
1028                }
1029                return HANDLED;
1030            }
1031
1032            @Override
1033            public void exit() {
1034                mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
1035            }
1036        }
1037
1038        class P2pDisabledState extends State {
1039            @Override
1040            public void enter() {
1041                if (DBG) logd(getName());
1042            }
1043
1044            @Override
1045            public boolean processMessage(Message message) {
1046                if (DBG) logd(getName() + message.toString());
1047                switch (message.what) {
1048                    case WifiStateMachine.CMD_ENABLE_P2P:
1049                        try {
1050                            mNwService.setInterfaceUp(mWifiNative.getInterfaceName());
1051                        } catch (RemoteException re) {
1052                            loge("Unable to change interface settings: " + re);
1053                        } catch (IllegalStateException ie) {
1054                            loge("Unable to change interface settings: " + ie);
1055                        }
1056                        mWifiMonitor.startMonitoring(mWifiNative.getInterfaceName());
1057                        transitionTo(mP2pEnablingState);
1058                        break;
1059                    default:
1060                        return NOT_HANDLED;
1061                }
1062                return HANDLED;
1063            }
1064        }
1065
1066        class P2pEnablingState extends State {
1067            @Override
1068            public void enter() {
1069                if (DBG) logd(getName());
1070            }
1071
1072            @Override
1073            public boolean processMessage(Message message) {
1074                if (DBG) logd(getName() + message.toString());
1075                switch (message.what) {
1076                    case WifiMonitor.SUP_CONNECTION_EVENT:
1077                        if (DBG) logd("P2p socket connection successful");
1078                        transitionTo(mInactiveState);
1079                        break;
1080                    case WifiMonitor.SUP_DISCONNECTION_EVENT:
1081                        loge("P2p socket connection failed");
1082                        transitionTo(mP2pDisabledState);
1083                        break;
1084                    case WifiStateMachine.CMD_ENABLE_P2P:
1085                    case WifiStateMachine.CMD_DISABLE_P2P_REQ:
1086                        deferMessage(message);
1087                        break;
1088                    default:
1089                        return NOT_HANDLED;
1090                }
1091                return HANDLED;
1092            }
1093        }
1094
1095        class P2pEnabledState extends State {
1096            @Override
1097            public void enter() {
1098                if (DBG) logd(getName());
1099                sendP2pStateChangedBroadcast(true);
1100                mNetworkInfo.setIsAvailable(true);
1101                sendP2pConnectionChangedBroadcast();
1102                initializeP2pSettings();
1103            }
1104
1105            @Override
1106            public boolean processMessage(Message message) {
1107                if (DBG) logd(getName() + message.toString());
1108                switch (message.what) {
1109                    case WifiMonitor.SUP_DISCONNECTION_EVENT:
1110                        loge("Unexpected loss of p2p socket connection");
1111                        transitionTo(mP2pDisabledState);
1112                        break;
1113                    case WifiStateMachine.CMD_ENABLE_P2P:
1114                        // Nothing to do
1115                        break;
1116                    case WifiStateMachine.CMD_DISABLE_P2P_REQ:
1117                        if (mPeers.clear()) {
1118                            sendPeersChangedBroadcast();
1119                        }
1120                        if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
1121
1122                        mWifiMonitor.stopMonitoring(mWifiNative.getInterfaceName());
1123                        transitionTo(mP2pDisablingState);
1124                        break;
1125                    case WifiP2pManager.SET_DEVICE_NAME:
1126                    {
1127                        WifiP2pDevice d = (WifiP2pDevice) message.obj;
1128                        if (d != null && setAndPersistDeviceName(d.deviceName)) {
1129                            if (DBG) logd("set device name " + d.deviceName);
1130                            replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED);
1131                        } else {
1132                            replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
1133                                    WifiP2pManager.ERROR);
1134                        }
1135                        break;
1136                    }
1137                    case WifiP2pManager.SET_WFD_INFO:
1138                    {
1139                        WifiP2pWfdInfo d = (WifiP2pWfdInfo) message.obj;
1140                        if (!getWfdPermission(message.sendingUid)) {
1141                            replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
1142                                    WifiP2pManager.ERROR);
1143                        } else if (d != null && setWfdInfo(d)) {
1144                            replyToMessage(message, WifiP2pManager.SET_WFD_INFO_SUCCEEDED);
1145                        } else {
1146                            replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
1147                                    WifiP2pManager.ERROR);
1148                        }
1149                        break;
1150                    }
1151                    case BLOCK_DISCOVERY:
1152                        boolean blocked = (message.arg1 == ENABLED ? true : false);
1153                        if (mDiscoveryBlocked == blocked) break;
1154                        mDiscoveryBlocked = blocked;
1155                        if (blocked && mDiscoveryStarted) {
1156                            mWifiNative.p2pStopFind();
1157                            mDiscoveryPostponed = true;
1158                        }
1159                        if (!blocked && mDiscoveryPostponed) {
1160                            mDiscoveryPostponed = false;
1161                            mWifiNative.p2pFind(DISCOVER_TIMEOUT_S);
1162                        }
1163                        if (blocked) {
1164                            try {
1165                                StateMachine m = (StateMachine) message.obj;
1166                                m.sendMessage(message.arg2);
1167                            } catch (Exception e) {
1168                                loge("unable to send BLOCK_DISCOVERY response: " + e);
1169                            }
1170                        }
1171                        break;
1172                    case WifiP2pManager.DISCOVER_PEERS:
1173                        if (mDiscoveryBlocked) {
1174                            replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
1175                                    WifiP2pManager.BUSY);
1176                            break;
1177                        }
1178                        // do not send service discovery request while normal find operation.
1179                        clearSupplicantServiceRequest();
1180                        if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
1181                            replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
1182                            sendP2pDiscoveryChangedBroadcast(true);
1183                        } else {
1184                            replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
1185                                    WifiP2pManager.ERROR);
1186                        }
1187                        break;
1188                    case WifiMonitor.P2P_FIND_STOPPED_EVENT:
1189                        sendP2pDiscoveryChangedBroadcast(false);
1190                        break;
1191                    case WifiP2pManager.STOP_DISCOVERY:
1192                        if (mWifiNative.p2pStopFind()) {
1193                            replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
1194                        } else {
1195                            replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
1196                                    WifiP2pManager.ERROR);
1197                        }
1198                        break;
1199                    case WifiP2pManager.DISCOVER_SERVICES:
1200                        if (mDiscoveryBlocked) {
1201                            replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
1202                                    WifiP2pManager.BUSY);
1203                            break;
1204                        }
1205                        if (DBG) logd(getName() + " discover services");
1206                        if (!updateSupplicantServiceRequest()) {
1207                            replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
1208                                    WifiP2pManager.NO_SERVICE_REQUESTS);
1209                            break;
1210                        }
1211                        if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
1212                            replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED);
1213                        } else {
1214                            replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
1215                                    WifiP2pManager.ERROR);
1216                        }
1217                        break;
1218                    case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
1219                        WifiP2pDevice device = (WifiP2pDevice) message.obj;
1220                        if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
1221                        mPeers.updateSupplicantDetails(device);
1222                        sendPeersChangedBroadcast();
1223                        break;
1224                    case WifiMonitor.P2P_DEVICE_LOST_EVENT:
1225                        device = (WifiP2pDevice) message.obj;
1226                        // Gets current details for the one removed
1227                        device = mPeers.remove(device.deviceAddress);
1228                        if (device != null) {
1229                            sendPeersChangedBroadcast();
1230                        }
1231                        break;
1232                    case WifiP2pManager.ADD_LOCAL_SERVICE:
1233                        if (DBG) logd(getName() + " add service");
1234                        WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo) message.obj;
1235                        if (addLocalService(message.replyTo, servInfo)) {
1236                            replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_SUCCEEDED);
1237                        } else {
1238                            replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED);
1239                        }
1240                        break;
1241                    case WifiP2pManager.REMOVE_LOCAL_SERVICE:
1242                        if (DBG) logd(getName() + " remove service");
1243                        servInfo = (WifiP2pServiceInfo) message.obj;
1244                        removeLocalService(message.replyTo, servInfo);
1245                        replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_SUCCEEDED);
1246                        break;
1247                    case WifiP2pManager.CLEAR_LOCAL_SERVICES:
1248                        if (DBG) logd(getName() + " clear service");
1249                        clearLocalServices(message.replyTo);
1250                        replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_SUCCEEDED);
1251                        break;
1252                    case WifiP2pManager.ADD_SERVICE_REQUEST:
1253                        if (DBG) logd(getName() + " add service request");
1254                        if (!addServiceRequest(message.replyTo,
1255                                (WifiP2pServiceRequest) message.obj)) {
1256                            replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED);
1257                            break;
1258                        }
1259                        replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_SUCCEEDED);
1260                        break;
1261                    case WifiP2pManager.REMOVE_SERVICE_REQUEST:
1262                        if (DBG) logd(getName() + " remove service request");
1263                        removeServiceRequest(message.replyTo, (WifiP2pServiceRequest) message.obj);
1264                        replyToMessage(message, WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED);
1265                        break;
1266                    case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
1267                        if (DBG) logd(getName() + " clear service request");
1268                        clearServiceRequests(message.replyTo);
1269                        replyToMessage(message, WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED);
1270                        break;
1271                    case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
1272                        if (DBG) logd(getName() + " receive service response");
1273                        List<WifiP2pServiceResponse> sdRespList =
1274                                (List<WifiP2pServiceResponse>) message.obj;
1275                        for (WifiP2pServiceResponse resp : sdRespList) {
1276                            WifiP2pDevice dev =
1277                                    mPeers.get(resp.getSrcDevice().deviceAddress);
1278                            resp.setSrcDevice(dev);
1279                            sendServiceResponse(resp);
1280                        }
1281                        break;
1282                    case WifiP2pManager.DELETE_PERSISTENT_GROUP:
1283                        if (DBG) logd(getName() + " delete persistent group");
1284                        mGroups.remove(message.arg1);
1285                        replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED);
1286                        break;
1287                    case SET_MIRACAST_MODE:
1288                        mWifiNative.setMiracastMode(message.arg1);
1289                        break;
1290                    case WifiP2pManager.START_LISTEN:
1291                        if (DBG) logd(getName() + " start listen mode");
1292                        mWifiNative.p2pFlush();
1293                        if (mWifiNative.p2pExtListen(true, 500, 500)) {
1294                            replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
1295                        } else {
1296                            replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
1297                        }
1298                        break;
1299                    case WifiP2pManager.STOP_LISTEN:
1300                        if (DBG) logd(getName() + " stop listen mode");
1301                        if (mWifiNative.p2pExtListen(false, 0, 0)) {
1302                            replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
1303                        } else {
1304                            replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
1305                        }
1306                        mWifiNative.p2pFlush();
1307                        break;
1308                    case WifiP2pManager.SET_CHANNEL:
1309                        Bundle p2pChannels = (Bundle) message.obj;
1310                        int lc = p2pChannels.getInt("lc", 0);
1311                        int oc = p2pChannels.getInt("oc", 0);
1312                        if (DBG) logd(getName() + " set listen and operating channel");
1313                        if (mWifiNative.p2pSetChannel(lc, oc)) {
1314                            replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
1315                        } else {
1316                            replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
1317                        }
1318                        break;
1319                    case WifiP2pManager.GET_HANDOVER_REQUEST:
1320                        Bundle requestBundle = new Bundle();
1321                        requestBundle.putString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE,
1322                                mWifiNative.getNfcHandoverRequest());
1323                        replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE,
1324                                requestBundle);
1325                        break;
1326                    case WifiP2pManager.GET_HANDOVER_SELECT:
1327                        Bundle selectBundle = new Bundle();
1328                        selectBundle.putString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE,
1329                                mWifiNative.getNfcHandoverSelect());
1330                        replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE,
1331                                selectBundle);
1332                        break;
1333                    default:
1334                        return NOT_HANDLED;
1335                }
1336                return HANDLED;
1337            }
1338
1339            @Override
1340            public void exit() {
1341                sendP2pDiscoveryChangedBroadcast(false);
1342                sendP2pStateChangedBroadcast(false);
1343                mNetworkInfo.setIsAvailable(false);
1344            }
1345        }
1346
1347        class InactiveState extends State {
1348            @Override
1349            public void enter() {
1350                if (DBG) logd(getName());
1351                mSavedPeerConfig.invalidate();
1352            }
1353
1354            @Override
1355            public boolean processMessage(Message message) {
1356                if (DBG) logd(getName() + message.toString());
1357                switch (message.what) {
1358                    case WifiP2pManager.CONNECT:
1359                        if (DBG) logd(getName() + " sending connect");
1360                        WifiP2pConfig config = (WifiP2pConfig) message.obj;
1361                        if (isConfigInvalid(config)) {
1362                            loge("Dropping connect requeset " + config);
1363                            replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
1364                            break;
1365                        }
1366
1367                        mAutonomousGroup = false;
1368                        mWifiNative.p2pStopFind();
1369                        if (reinvokePersistentGroup(config)) {
1370                            transitionTo(mGroupNegotiationState);
1371                        } else {
1372                            transitionTo(mProvisionDiscoveryState);
1373                        }
1374                        mSavedPeerConfig = config;
1375                        mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1376                        sendPeersChangedBroadcast();
1377                        replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
1378                        break;
1379                    case WifiP2pManager.STOP_DISCOVERY:
1380                        if (mWifiNative.p2pStopFind()) {
1381                            // When discovery stops in inactive state, flush to clear
1382                            // state peer data
1383                            mWifiNative.p2pFlush();
1384                            mServiceDiscReqId = null;
1385                            replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
1386                        } else {
1387                            replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
1388                                    WifiP2pManager.ERROR);
1389                        }
1390                        break;
1391                    case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
1392                        config = (WifiP2pConfig) message.obj;
1393                        if (isConfigInvalid(config)) {
1394                            loge("Dropping GO neg request " + config);
1395                            break;
1396                        }
1397                        mSavedPeerConfig = config;
1398                        mAutonomousGroup = false;
1399                        mJoinExistingGroup = false;
1400                        transitionTo(mUserAuthorizingNegotiationRequestState);
1401                        break;
1402                    case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
1403                        WifiP2pGroup group = (WifiP2pGroup) message.obj;
1404                        WifiP2pDevice owner = group.getOwner();
1405
1406                        if (owner == null) {
1407                            int id = group.getNetworkId();
1408                            if (id < 0) {
1409                                loge("Ignored invitation from null owner");
1410                                break;
1411                            }
1412
1413                            String addr = mGroups.getOwnerAddr(id);
1414                            if (addr != null) {
1415                                group.setOwner(new WifiP2pDevice(addr));
1416                                owner = group.getOwner();
1417                            } else {
1418                                loge("Ignored invitation from null owner");
1419                                break;
1420                            }
1421                        }
1422
1423                        config = new WifiP2pConfig();
1424                        config.deviceAddress = group.getOwner().deviceAddress;
1425
1426                        if (isConfigInvalid(config)) {
1427                            loge("Dropping invitation request " + config);
1428                            break;
1429                        }
1430                        mSavedPeerConfig = config;
1431
1432                        // Check if we have the owner in peer list and use appropriate
1433                        // wps method. Default is to use PBC.
1434                        if ((owner = mPeers.get(owner.deviceAddress)) != null) {
1435                            if (owner.wpsPbcSupported()) {
1436                                mSavedPeerConfig.wps.setup = WpsInfo.PBC;
1437                            } else if (owner.wpsKeypadSupported()) {
1438                                mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
1439                            } else if (owner.wpsDisplaySupported()) {
1440                                mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
1441                            }
1442                        }
1443
1444                        mAutonomousGroup = false;
1445                        mJoinExistingGroup = true;
1446                        transitionTo(mUserAuthorizingInviteRequestState);
1447                        break;
1448                    case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1449                    case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1450                    case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1451                        // We let the supplicant handle the provision discovery response
1452                        // and wait instead for the GO_NEGOTIATION_REQUEST_EVENT.
1453                        // Handling provision discovery and issuing a p2p_connect before
1454                        // group negotiation comes through causes issues
1455                        break;
1456                    case WifiP2pManager.CREATE_GROUP:
1457                        mAutonomousGroup = true;
1458                        int netId = message.arg1;
1459                        boolean ret = false;
1460                        if (netId == WifiP2pGroup.PERSISTENT_NET_ID) {
1461                            // check if the go persistent group is present.
1462                            netId = mGroups.getNetworkId(mThisDevice.deviceAddress);
1463                            if (netId != -1) {
1464                                ret = mWifiNative.p2pGroupAdd(netId);
1465                            } else {
1466                                ret = mWifiNative.p2pGroupAdd(true);
1467                            }
1468                        } else {
1469                            ret = mWifiNative.p2pGroupAdd(false);
1470                        }
1471
1472                        if (ret) {
1473                            replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
1474                            transitionTo(mGroupNegotiationState);
1475                        } else {
1476                            replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
1477                                    WifiP2pManager.ERROR);
1478                            // remain at this state.
1479                        }
1480                        break;
1481                    case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1482                        mGroup = (WifiP2pGroup) message.obj;
1483                        if (DBG) logd(getName() + " group started");
1484
1485                        // We hit this scenario when a persistent group is reinvoked
1486                        if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
1487                            mAutonomousGroup = false;
1488                            deferMessage(message);
1489                            transitionTo(mGroupNegotiationState);
1490                        } else {
1491                            loge("Unexpected group creation, remove " + mGroup);
1492                            mWifiNative.p2pGroupRemove(mGroup.getInterface());
1493                        }
1494                        break;
1495                    case WifiP2pManager.START_LISTEN:
1496                        if (DBG) logd(getName() + " start listen mode");
1497                        mWifiNative.p2pFlush();
1498                        if (mWifiNative.p2pExtListen(true, 500, 500)) {
1499                            replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
1500                        } else {
1501                            replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
1502                        }
1503                        break;
1504                    case WifiP2pManager.STOP_LISTEN:
1505                        if (DBG) logd(getName() + " stop listen mode");
1506                        if (mWifiNative.p2pExtListen(false, 0, 0)) {
1507                            replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
1508                        } else {
1509                            replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
1510                        }
1511                        mWifiNative.p2pFlush();
1512                        break;
1513                    case WifiP2pManager.SET_CHANNEL:
1514                        Bundle p2pChannels = (Bundle) message.obj;
1515                        int lc = p2pChannels.getInt("lc", 0);
1516                        int oc = p2pChannels.getInt("oc", 0);
1517                        if (DBG) logd(getName() + " set listen and operating channel");
1518                        if (mWifiNative.p2pSetChannel(lc, oc)) {
1519                            replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
1520                        } else {
1521                            replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
1522                        }
1523                        break;
1524                    case WifiP2pManager.INITIATOR_REPORT_NFC_HANDOVER:
1525                        String handoverSelect = null;
1526
1527                        if (message.obj != null) {
1528                            handoverSelect = ((Bundle) message.obj)
1529                                    .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE);
1530                        }
1531
1532                        if (handoverSelect != null
1533                                && mWifiNative.initiatorReportNfcHandover(handoverSelect)) {
1534                            replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED);
1535                            transitionTo(mGroupCreatingState);
1536                        } else {
1537                            replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED);
1538                        }
1539                        break;
1540                    case WifiP2pManager.RESPONDER_REPORT_NFC_HANDOVER:
1541                        String handoverRequest = null;
1542
1543                        if (message.obj != null) {
1544                            handoverRequest = ((Bundle) message.obj)
1545                                    .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE);
1546                        }
1547
1548                        if (handoverRequest != null
1549                                && mWifiNative.responderReportNfcHandover(handoverRequest)) {
1550                            replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED);
1551                            transitionTo(mGroupCreatingState);
1552                        } else {
1553                            replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED);
1554                        }
1555                        break;
1556                    default:
1557                        return NOT_HANDLED;
1558                }
1559                return HANDLED;
1560            }
1561        }
1562
1563        class GroupCreatingState extends State {
1564            @Override
1565            public void enter() {
1566                if (DBG) logd(getName());
1567                sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,
1568                        ++sGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);
1569            }
1570
1571            @Override
1572            public boolean processMessage(Message message) {
1573                if (DBG) logd(getName() + message.toString());
1574                boolean ret = HANDLED;
1575                switch (message.what) {
1576                    case GROUP_CREATING_TIMED_OUT:
1577                        if (sGroupCreatingTimeoutIndex == message.arg1) {
1578                            if (DBG) logd("Group negotiation timed out");
1579                            handleGroupCreationFailure();
1580                            transitionTo(mInactiveState);
1581                        }
1582                        break;
1583                    case WifiMonitor.P2P_DEVICE_LOST_EVENT:
1584                        WifiP2pDevice device = (WifiP2pDevice) message.obj;
1585                        if (!mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
1586                            if (DBG) {
1587                                logd("mSavedPeerConfig " + mSavedPeerConfig.deviceAddress
1588                                        + "device " + device.deviceAddress);
1589                            }
1590                            // Do the regular device lost handling
1591                            ret = NOT_HANDLED;
1592                            break;
1593                        }
1594                        // Do nothing
1595                        if (DBG) logd("Add device to lost list " + device);
1596                        mPeersLostDuringConnection.updateSupplicantDetails(device);
1597                        break;
1598                    case WifiP2pManager.DISCOVER_PEERS:
1599                        // Discovery will break negotiation
1600                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
1601                                WifiP2pManager.BUSY);
1602                        break;
1603                    case WifiP2pManager.CANCEL_CONNECT:
1604                        // Do a supplicant p2p_cancel which only cancels an ongoing
1605                        // group negotiation. This will fail for a pending provision
1606                        // discovery or for a pending user action, but at the framework
1607                        // level, we always treat cancel as succeeded and enter
1608                        // an inactive state
1609                        mWifiNative.p2pCancelConnect();
1610                        handleGroupCreationFailure();
1611                        transitionTo(mInactiveState);
1612                        replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
1613                        break;
1614                    case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1615                        // We hit this scenario when NFC handover is invoked.
1616                        mAutonomousGroup = false;
1617                        transitionTo(mGroupNegotiationState);
1618                        break;
1619                    default:
1620                        ret = NOT_HANDLED;
1621                }
1622                return ret;
1623            }
1624        }
1625
1626        class UserAuthorizingNegotiationRequestState extends State {
1627            @Override
1628            public void enter() {
1629                if (DBG) logd(getName());
1630                notifyInvitationReceived();
1631            }
1632
1633            @Override
1634            public boolean processMessage(Message message) {
1635                if (DBG) logd(getName() + message.toString());
1636                boolean ret = HANDLED;
1637                switch (message.what) {
1638                    case PEER_CONNECTION_USER_ACCEPT:
1639                        mWifiNative.p2pStopFind();
1640                        p2pConnectWithPinDisplay(mSavedPeerConfig);
1641                        mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1642                        sendPeersChangedBroadcast();
1643                        transitionTo(mGroupNegotiationState);
1644                        break;
1645                    case PEER_CONNECTION_USER_REJECT:
1646                        if (DBG) logd("User rejected negotiation " + mSavedPeerConfig);
1647                        transitionTo(mInactiveState);
1648                        break;
1649                    default:
1650                        return NOT_HANDLED;
1651                }
1652                return ret;
1653            }
1654
1655            @Override
1656            public void exit() {
1657                // TODO: dismiss dialog if not already done
1658            }
1659        }
1660
1661        class UserAuthorizingInviteRequestState extends State {
1662            @Override
1663            public void enter() {
1664                if (DBG) logd(getName());
1665                notifyInvitationReceived();
1666            }
1667
1668            @Override
1669            public boolean processMessage(Message message) {
1670                if (DBG) logd(getName() + message.toString());
1671                boolean ret = HANDLED;
1672                switch (message.what) {
1673                    case PEER_CONNECTION_USER_ACCEPT:
1674                        mWifiNative.p2pStopFind();
1675                        if (!reinvokePersistentGroup(mSavedPeerConfig)) {
1676                            // Do negotiation when persistence fails
1677                            p2pConnectWithPinDisplay(mSavedPeerConfig);
1678                        }
1679                        mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1680                        sendPeersChangedBroadcast();
1681                        transitionTo(mGroupNegotiationState);
1682                        break;
1683                    case PEER_CONNECTION_USER_REJECT:
1684                        if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
1685                        transitionTo(mInactiveState);
1686                        break;
1687                    default:
1688                        return NOT_HANDLED;
1689                }
1690                return ret;
1691            }
1692
1693            @Override
1694            public void exit() {
1695                // TODO: dismiss dialog if not already done
1696            }
1697        }
1698
1699        class ProvisionDiscoveryState extends State {
1700            @Override
1701            public void enter() {
1702                if (DBG) logd(getName());
1703                mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
1704            }
1705
1706            @Override
1707            public boolean processMessage(Message message) {
1708                if (DBG) logd(getName() + message.toString());
1709                WifiP2pProvDiscEvent provDisc;
1710                WifiP2pDevice device;
1711                switch (message.what) {
1712                    case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
1713                        provDisc = (WifiP2pProvDiscEvent) message.obj;
1714                        device = provDisc.device;
1715                        if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1716
1717                        if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
1718                            if (DBG) logd("Found a match " + mSavedPeerConfig);
1719                            p2pConnectWithPinDisplay(mSavedPeerConfig);
1720                            transitionTo(mGroupNegotiationState);
1721                        }
1722                        break;
1723                    case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1724                        provDisc = (WifiP2pProvDiscEvent) message.obj;
1725                        device = provDisc.device;
1726                        if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1727
1728                        if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) {
1729                            if (DBG) logd("Found a match " + mSavedPeerConfig);
1730                            // we already have the pin
1731                            if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
1732                                p2pConnectWithPinDisplay(mSavedPeerConfig);
1733                                transitionTo(mGroupNegotiationState);
1734                            } else {
1735                                mJoinExistingGroup = false;
1736                                transitionTo(mUserAuthorizingNegotiationRequestState);
1737                            }
1738                        }
1739                        break;
1740                    case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1741                        provDisc = (WifiP2pProvDiscEvent) message.obj;
1742                        device = provDisc.device;
1743                        if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
1744
1745                        if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
1746                            if (DBG) logd("Found a match " + mSavedPeerConfig);
1747                            mSavedPeerConfig.wps.pin = provDisc.pin;
1748                            p2pConnectWithPinDisplay(mSavedPeerConfig);
1749                            notifyInvitationSent(provDisc.pin, device.deviceAddress);
1750                            transitionTo(mGroupNegotiationState);
1751                        }
1752                        break;
1753                    case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
1754                        loge("provision discovery failed");
1755                        handleGroupCreationFailure();
1756                        transitionTo(mInactiveState);
1757                        break;
1758                    default:
1759                        return NOT_HANDLED;
1760                }
1761                return HANDLED;
1762            }
1763        }
1764
1765        class GroupNegotiationState extends State {
1766            @Override
1767            public void enter() {
1768                if (DBG) logd(getName());
1769            }
1770
1771            @Override
1772            public boolean processMessage(Message message) {
1773                if (DBG) logd(getName() + message.toString());
1774                switch (message.what) {
1775                    // We ignore these right now, since we get a GROUP_STARTED notification
1776                    // afterwards
1777                    case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1778                    case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
1779                        if (DBG) logd(getName() + " go success");
1780                        break;
1781                    case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1782                        mGroup = (WifiP2pGroup) message.obj;
1783                        if (DBG) logd(getName() + " group started");
1784
1785                        if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
1786                             // update cache information and set network id to mGroup.
1787                            updatePersistentNetworks(NO_RELOAD);
1788                            String devAddr = mGroup.getOwner().deviceAddress;
1789                            mGroup.setNetworkId(mGroups.getNetworkId(devAddr,
1790                                    mGroup.getNetworkName()));
1791                        }
1792
1793                        if (mGroup.isGroupOwner()) {
1794                            // Setting an idle time out on GO causes issues with certain scenarios
1795                            // on clients where it can be off-channel for longer and with the power
1796                            // save modes used.
1797                            // TODO: Verify multi-channel scenarios and supplicant behavior are
1798                            // better before adding a time out in future
1799                            // Set group idle timeout of 10 sec, to avoid GO beaconing incase of any
1800                            // failure during 4-way Handshake.
1801                            if (!mAutonomousGroup) {
1802                                mWifiNative.setP2pGroupIdle(mGroup.getInterface(),
1803                                        GROUP_IDLE_TIME_S);
1804                            }
1805                            startDhcpServer(mGroup.getInterface());
1806                        } else {
1807                            mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
1808                            startIpManager(mGroup.getInterface());
1809                            WifiP2pDevice groupOwner = mGroup.getOwner();
1810                            WifiP2pDevice peer = mPeers.get(groupOwner.deviceAddress);
1811                            if (peer != null) {
1812                                // update group owner details with peer details found at discovery
1813                                groupOwner.updateSupplicantDetails(peer);
1814                                mPeers.updateStatus(groupOwner.deviceAddress,
1815                                        WifiP2pDevice.CONNECTED);
1816                                sendPeersChangedBroadcast();
1817                            } else {
1818                                // A supplicant bug can lead to reporting an invalid
1819                                // group owner address (all zeroes) at times. Avoid a
1820                                // crash, but continue group creation since it is not
1821                                // essential.
1822                                logw("Unknown group owner " + groupOwner);
1823                            }
1824                        }
1825                        transitionTo(mGroupCreatedState);
1826                        break;
1827                    case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
1828                        P2pStatus status = (P2pStatus) message.obj;
1829                        if (status == P2pStatus.NO_COMMON_CHANNEL) {
1830                            transitionTo(mFrequencyConflictState);
1831                            break;
1832                        }
1833                        // continue with group removal handling
1834                    case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1835                        if (DBG) logd(getName() + " go failure");
1836                        handleGroupCreationFailure();
1837                        transitionTo(mInactiveState);
1838                        break;
1839                    case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
1840                        // A group formation failure is always followed by
1841                        // a group removed event. Flushing things at group formation
1842                        // failure causes supplicant issues. Ignore right now.
1843                        status = (P2pStatus) message.obj;
1844                        if (status == P2pStatus.NO_COMMON_CHANNEL) {
1845                            transitionTo(mFrequencyConflictState);
1846                            break;
1847                        }
1848                        break;
1849                    case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
1850                        status = (P2pStatus) message.obj;
1851                        if (status == P2pStatus.SUCCESS) {
1852                            // invocation was succeeded.
1853                            // wait P2P_GROUP_STARTED_EVENT.
1854                            break;
1855                        }
1856                        loge("Invitation result " + status);
1857                        if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
1858                            // target device has already removed the credential.
1859                            // So, remove this credential accordingly.
1860                            int netId = mSavedPeerConfig.netId;
1861                            if (netId >= 0) {
1862                                if (DBG) logd("Remove unknown client from the list");
1863                                removeClientFromList(netId, mSavedPeerConfig.deviceAddress, true);
1864                            }
1865
1866                            // Reinvocation has failed, try group negotiation
1867                            mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
1868                            p2pConnectWithPinDisplay(mSavedPeerConfig);
1869                        } else if (status == P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE) {
1870
1871                            // Devices setting persistent_reconnect to 0 in wpa_supplicant
1872                            // always defer the invocation request and return
1873                            // "information is currently unavailable" error.
1874                            // So, try another way to connect for interoperability.
1875                            mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
1876                            p2pConnectWithPinDisplay(mSavedPeerConfig);
1877                        } else if (status == P2pStatus.NO_COMMON_CHANNEL) {
1878                            transitionTo(mFrequencyConflictState);
1879                        } else {
1880                            handleGroupCreationFailure();
1881                            transitionTo(mInactiveState);
1882                        }
1883                        break;
1884                    default:
1885                        return NOT_HANDLED;
1886                }
1887                return HANDLED;
1888            }
1889        }
1890
1891        class FrequencyConflictState extends State {
1892            private AlertDialog mFrequencyConflictDialog;
1893            @Override
1894            public void enter() {
1895                if (DBG) logd(getName());
1896                notifyFrequencyConflict();
1897            }
1898
1899            private void notifyFrequencyConflict() {
1900                logd("Notify frequency conflict");
1901                Resources r = Resources.getSystem();
1902
1903                AlertDialog dialog = new AlertDialog.Builder(mContext)
1904                        .setMessage(r.getString(R.string.wifi_p2p_frequency_conflict_message,
1905                            getDeviceName(mSavedPeerConfig.deviceAddress)))
1906                        .setPositiveButton(r.getString(R.string.dlg_ok), new OnClickListener() {
1907                            @Override
1908                            public void onClick(DialogInterface dialog, int which) {
1909                                sendMessage(DROP_WIFI_USER_ACCEPT);
1910                            }
1911                        })
1912                        .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
1913                            @Override
1914                            public void onClick(DialogInterface dialog, int which) {
1915                                sendMessage(DROP_WIFI_USER_REJECT);
1916                            }
1917                        })
1918                        .setOnCancelListener(new DialogInterface.OnCancelListener() {
1919                            @Override
1920                            public void onCancel(DialogInterface arg0) {
1921                                sendMessage(DROP_WIFI_USER_REJECT);
1922                            }
1923                        })
1924                        .create();
1925                dialog.setCanceledOnTouchOutside(false);
1926
1927                dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1928                WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
1929                attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
1930                dialog.getWindow().setAttributes(attrs);
1931                dialog.show();
1932                mFrequencyConflictDialog = dialog;
1933            }
1934
1935            @Override
1936            public boolean processMessage(Message message) {
1937                if (DBG) logd(getName() + message.toString());
1938                switch (message.what) {
1939                    case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1940                    case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
1941                        loge(getName() + "group sucess during freq conflict!");
1942                        break;
1943                    case WifiMonitor.P2P_GROUP_STARTED_EVENT:
1944                        loge(getName() + "group started after freq conflict, handle anyway");
1945                        deferMessage(message);
1946                        transitionTo(mGroupNegotiationState);
1947                        break;
1948                    case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
1949                    case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
1950                    case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
1951                        // Ignore failures since we retry again
1952                        break;
1953                    case DROP_WIFI_USER_REJECT:
1954                        // User rejected dropping wifi in favour of p2p
1955                        handleGroupCreationFailure();
1956                        transitionTo(mInactiveState);
1957                        break;
1958                    case DROP_WIFI_USER_ACCEPT:
1959                        // User accepted dropping wifi in favour of p2p
1960                        mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 1);
1961                        mTemporarilyDisconnectedWifi = true;
1962                        break;
1963                    case DISCONNECT_WIFI_RESPONSE:
1964                        // Got a response from wifistatemachine, retry p2p
1965                        if (DBG) logd(getName() + "Wifi disconnected, retry p2p");
1966                        transitionTo(mInactiveState);
1967                        sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
1968                        break;
1969                    default:
1970                        return NOT_HANDLED;
1971                }
1972                return HANDLED;
1973            }
1974
1975            public void exit() {
1976                if (mFrequencyConflictDialog != null) mFrequencyConflictDialog.dismiss();
1977            }
1978        }
1979
1980        class GroupCreatedState extends State {
1981            @Override
1982            public void enter() {
1983                if (DBG) logd(getName());
1984                // Once connected, peer config details are invalid
1985                mSavedPeerConfig.invalidate();
1986                mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
1987
1988                updateThisDevice(WifiP2pDevice.CONNECTED);
1989
1990                // DHCP server has already been started if I am a group owner
1991                if (mGroup.isGroupOwner()) {
1992                    setWifiP2pInfoOnGroupFormation(
1993                            NetworkUtils.numericToInetAddress(SERVER_ADDRESS));
1994                }
1995
1996                // In case of a negotiation group, connection changed is sent
1997                // after a client joins. For autonomous, send now
1998                if (mAutonomousGroup) {
1999                    sendP2pConnectionChangedBroadcast();
2000                }
2001            }
2002
2003            @Override
2004            public boolean processMessage(Message message) {
2005                if (DBG) logd(getName() + message.toString());
2006                switch (message.what) {
2007                    case WifiMonitor.AP_STA_CONNECTED_EVENT:
2008                        WifiP2pDevice device = (WifiP2pDevice) message.obj;
2009                        String deviceAddress = device.deviceAddress;
2010                        // Clear timeout that was set when group was started.
2011                        mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
2012                        if (deviceAddress != null) {
2013                            if (mPeers.get(deviceAddress) != null) {
2014                                mGroup.addClient(mPeers.get(deviceAddress));
2015                            } else {
2016                                mGroup.addClient(deviceAddress);
2017                            }
2018                            mPeers.updateStatus(deviceAddress, WifiP2pDevice.CONNECTED);
2019                            if (DBG) logd(getName() + " ap sta connected");
2020                            sendPeersChangedBroadcast();
2021                        } else {
2022                            loge("Connect on null device address, ignore");
2023                        }
2024                        sendP2pConnectionChangedBroadcast();
2025                        break;
2026                    case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
2027                        device = (WifiP2pDevice) message.obj;
2028                        deviceAddress = device.deviceAddress;
2029                        if (deviceAddress != null) {
2030                            mPeers.updateStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
2031                            if (mGroup.removeClient(deviceAddress)) {
2032                                if (DBG) logd("Removed client " + deviceAddress);
2033                                if (!mAutonomousGroup && mGroup.isClientListEmpty()) {
2034                                    logd("Client list empty, remove non-persistent p2p group");
2035                                    mWifiNative.p2pGroupRemove(mGroup.getInterface());
2036                                    // We end up sending connection changed broadcast
2037                                    // when this happens at exit()
2038                                } else {
2039                                    // Notify when a client disconnects from group
2040                                    sendP2pConnectionChangedBroadcast();
2041                                }
2042                            } else {
2043                                if (DBG) logd("Failed to remove client " + deviceAddress);
2044                                for (WifiP2pDevice c : mGroup.getClientList()) {
2045                                    if (DBG) logd("client " + c.deviceAddress);
2046                                }
2047                            }
2048                            sendPeersChangedBroadcast();
2049                            if (DBG) logd(getName() + " ap sta disconnected");
2050                        } else {
2051                            loge("Disconnect on unknown device: " + device);
2052                        }
2053                        break;
2054                    case IPM_PRE_DHCP_ACTION:
2055                        mWifiNative.setP2pPowerSave(mGroup.getInterface(), false);
2056                        mIpManager.completedPreDhcpAction();
2057                        break;
2058                    case IPM_POST_DHCP_ACTION:
2059                        mWifiNative.setP2pPowerSave(mGroup.getInterface(), true);
2060                        break;
2061                    case IPM_DHCP_RESULTS:
2062                        mDhcpResults = (DhcpResults) message.obj;
2063                        break;
2064                    case IPM_PROVISIONING_SUCCESS:
2065                        if (DBG) logd("mDhcpResults: " + mDhcpResults);
2066                        setWifiP2pInfoOnGroupFormation(mDhcpResults.serverAddress);
2067                        sendP2pConnectionChangedBroadcast();
2068                        try {
2069                            final String ifname = mGroup.getInterface();
2070                            mNwService.addInterfaceToLocalNetwork(
2071                                    ifname, mDhcpResults.getRoutes(ifname));
2072                        } catch (RemoteException e) {
2073                            loge("Failed to add iface to local network " + e);
2074                        }
2075                        break;
2076                    case IPM_PROVISIONING_FAILURE:
2077                        loge("IP provisioning failed");
2078                        mWifiNative.p2pGroupRemove(mGroup.getInterface());
2079                        break;
2080                    case WifiP2pManager.REMOVE_GROUP:
2081                        if (DBG) logd(getName() + " remove group");
2082                        if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
2083                            transitionTo(mOngoingGroupRemovalState);
2084                            replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
2085                        } else {
2086                            handleGroupRemoved();
2087                            transitionTo(mInactiveState);
2088                            replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
2089                                    WifiP2pManager.ERROR);
2090                        }
2091                        break;
2092                    case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
2093                        // We do not listen to NETWORK_DISCONNECTION_EVENT for group removal
2094                        // handling since supplicant actually tries to reconnect after a temporary
2095                        // disconnect until group idle time out. Eventually, a group removal event
2096                        // will come when group has been removed.
2097                        //
2098                        // When there are connectivity issues during temporary disconnect,
2099                        // the application will also just remove the group.
2100                        //
2101                        // Treating network disconnection as group removal causes race conditions
2102                        // since supplicant would still maintain the group at that stage.
2103                        if (DBG) logd(getName() + " group removed");
2104                        handleGroupRemoved();
2105                        transitionTo(mInactiveState);
2106                        break;
2107                    case WifiMonitor.P2P_DEVICE_LOST_EVENT:
2108                        device = (WifiP2pDevice) message.obj;
2109                        // Device loss for a connected device indicates
2110                        // it is not in discovery any more
2111                        if (mGroup.contains(device)) {
2112                            if (DBG) logd("Add device to lost list " + device);
2113                            mPeersLostDuringConnection.updateSupplicantDetails(device);
2114                            return HANDLED;
2115                        }
2116                        // Do the regular device lost handling
2117                        return NOT_HANDLED;
2118                    case WifiStateMachine.CMD_DISABLE_P2P_REQ:
2119                        sendMessage(WifiP2pManager.REMOVE_GROUP);
2120                        deferMessage(message);
2121                        break;
2122                        // This allows any client to join the GO during the
2123                        // WPS window
2124                    case WifiP2pManager.START_WPS:
2125                        WpsInfo wps = (WpsInfo) message.obj;
2126                        if (wps == null) {
2127                            replyToMessage(message, WifiP2pManager.START_WPS_FAILED);
2128                            break;
2129                        }
2130                        boolean ret = true;
2131                        if (wps.setup == WpsInfo.PBC) {
2132                            ret = mWifiNative.startWpsPbc(mGroup.getInterface(), null);
2133                        } else {
2134                            if (wps.pin == null) {
2135                                String pin = mWifiNative.startWpsPinDisplay(mGroup.getInterface());
2136                                try {
2137                                    Integer.parseInt(pin);
2138                                    notifyInvitationSent(pin, "any");
2139                                } catch (NumberFormatException ignore) {
2140                                    ret = false;
2141                                }
2142                            } else {
2143                                ret = mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
2144                                        wps.pin);
2145                            }
2146                        }
2147                        replyToMessage(message, ret ? WifiP2pManager.START_WPS_SUCCEEDED :
2148                                WifiP2pManager.START_WPS_FAILED);
2149                        break;
2150                    case WifiP2pManager.CONNECT:
2151                        WifiP2pConfig config = (WifiP2pConfig) message.obj;
2152                        if (isConfigInvalid(config)) {
2153                            loge("Dropping connect requeset " + config);
2154                            replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
2155                            break;
2156                        }
2157                        logd("Inviting device : " + config.deviceAddress);
2158                        mSavedPeerConfig = config;
2159                        if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
2160                            mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
2161                            sendPeersChangedBroadcast();
2162                            replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
2163                        } else {
2164                            replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
2165                                    WifiP2pManager.ERROR);
2166                        }
2167                        // TODO: figure out updating the status to declined
2168                        // when invitation is rejected
2169                        break;
2170                    case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
2171                        P2pStatus status = (P2pStatus) message.obj;
2172                        if (status == P2pStatus.SUCCESS) {
2173                            // invocation was succeeded.
2174                            break;
2175                        }
2176                        loge("Invitation result " + status);
2177                        if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
2178                            // target device has already removed the credential.
2179                            // So, remove this credential accordingly.
2180                            int netId = mGroup.getNetworkId();
2181                            if (netId >= 0) {
2182                                if (DBG) logd("Remove unknown client from the list");
2183                                if (!removeClientFromList(netId,
2184                                        mSavedPeerConfig.deviceAddress, false)) {
2185                                    // not found the client on the list
2186                                    loge("Already removed the client, ignore");
2187                                    break;
2188                                }
2189                                // try invitation.
2190                                sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
2191                            }
2192                        }
2193                        break;
2194                    case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
2195                    case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
2196                    case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
2197                        WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
2198                        mSavedPeerConfig = new WifiP2pConfig();
2199                        mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
2200                        if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
2201                            mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
2202                        } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
2203                            mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
2204                            mSavedPeerConfig.wps.pin = provDisc.pin;
2205                        } else {
2206                            mSavedPeerConfig.wps.setup = WpsInfo.PBC;
2207                        }
2208                        transitionTo(mUserAuthorizingJoinState);
2209                        break;
2210                    case WifiMonitor.P2P_GROUP_STARTED_EVENT:
2211                        loge("Duplicate group creation event notice, ignore");
2212                        break;
2213                    default:
2214                        return NOT_HANDLED;
2215                }
2216                return HANDLED;
2217            }
2218
2219            public void exit() {
2220                updateThisDevice(WifiP2pDevice.AVAILABLE);
2221                resetWifiP2pInfo();
2222                mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
2223                sendP2pConnectionChangedBroadcast();
2224            }
2225        }
2226
2227        class UserAuthorizingJoinState extends State {
2228            @Override
2229            public void enter() {
2230                if (DBG) logd(getName());
2231                notifyInvitationReceived();
2232            }
2233
2234            @Override
2235            public boolean processMessage(Message message) {
2236                if (DBG) logd(getName() + message.toString());
2237                switch (message.what) {
2238                    case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
2239                    case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
2240                    case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
2241                        // Ignore more client requests
2242                        break;
2243                    case PEER_CONNECTION_USER_ACCEPT:
2244                        // Stop discovery to avoid failure due to channel switch
2245                        mWifiNative.p2pStopFind();
2246                        if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
2247                            mWifiNative.startWpsPbc(mGroup.getInterface(), null);
2248                        } else {
2249                            mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
2250                                    mSavedPeerConfig.wps.pin);
2251                        }
2252                        transitionTo(mGroupCreatedState);
2253                        break;
2254                    case PEER_CONNECTION_USER_REJECT:
2255                        if (DBG) logd("User rejected incoming request");
2256                        transitionTo(mGroupCreatedState);
2257                        break;
2258                    default:
2259                        return NOT_HANDLED;
2260                }
2261                return HANDLED;
2262            }
2263
2264            @Override
2265            public void exit() {
2266                // TODO: dismiss dialog if not already done
2267            }
2268        }
2269
2270        class OngoingGroupRemovalState extends State {
2271            @Override
2272            public void enter() {
2273                if (DBG) logd(getName());
2274            }
2275
2276            @Override
2277            public boolean processMessage(Message message) {
2278                if (DBG) logd(getName() + message.toString());
2279                switch (message.what) {
2280                    // Group removal ongoing. Multiple calls
2281                    // end up removing persisted network. Do nothing.
2282                    case WifiP2pManager.REMOVE_GROUP:
2283                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
2284                        break;
2285                    // Parent state will transition out of this state
2286                    // when removal is complete
2287                    default:
2288                        return NOT_HANDLED;
2289                }
2290                return HANDLED;
2291            }
2292        }
2293
2294        @Override
2295        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2296            super.dump(fd, pw, args);
2297            pw.println("mWifiP2pInfo " + mWifiP2pInfo);
2298            pw.println("mGroup " + mGroup);
2299            pw.println("mSavedPeerConfig " + mSavedPeerConfig);
2300            pw.println();
2301        }
2302
2303        private void sendP2pStateChangedBroadcast(boolean enabled) {
2304            final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
2305            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2306            if (enabled) {
2307                intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
2308                        WifiP2pManager.WIFI_P2P_STATE_ENABLED);
2309            } else {
2310                intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
2311                        WifiP2pManager.WIFI_P2P_STATE_DISABLED);
2312            }
2313            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2314        }
2315
2316        private void sendP2pDiscoveryChangedBroadcast(boolean started) {
2317            if (mDiscoveryStarted == started) return;
2318            mDiscoveryStarted = started;
2319
2320            if (DBG) logd("discovery change broadcast " + started);
2321
2322            final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
2323            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2324            intent.putExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, started
2325                    ? WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED :
2326                    WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
2327            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2328        }
2329
2330        private void sendThisDeviceChangedBroadcast() {
2331            final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
2332            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2333            intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
2334            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2335        }
2336
2337        private void sendPeersChangedBroadcast() {
2338            final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
2339            intent.putExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, new WifiP2pDeviceList(mPeers));
2340            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2341            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2342        }
2343
2344        private void sendP2pConnectionChangedBroadcast() {
2345            if (DBG) logd("sending p2p connection changed broadcast");
2346            Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
2347            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2348                    | Intent.FLAG_RECEIVER_REPLACE_PENDING);
2349            intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
2350            intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
2351            intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, new WifiP2pGroup(mGroup));
2352            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2353            mWifiChannel.sendMessage(WifiP2pServiceImpl.P2P_CONNECTION_CHANGED,
2354                    new NetworkInfo(mNetworkInfo));
2355        }
2356
2357        private void sendP2pPersistentGroupsChangedBroadcast() {
2358            if (DBG) logd("sending p2p persistent groups changed broadcast");
2359            Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
2360            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2361            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2362        }
2363
2364        private void startDhcpServer(String intf) {
2365            InterfaceConfiguration ifcg = null;
2366            try {
2367                ifcg = mNwService.getInterfaceConfig(intf);
2368                ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(
2369                            SERVER_ADDRESS), 24));
2370                ifcg.setInterfaceUp();
2371                mNwService.setInterfaceConfig(intf, ifcg);
2372                // This starts the dnsmasq server
2373                ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
2374                        Context.CONNECTIVITY_SERVICE);
2375                String[] tetheringDhcpRanges = cm.getTetheredDhcpRanges();
2376                if (mNwService.isTetheringStarted()) {
2377                    if (DBG) logd("Stop existing tethering and restart it");
2378                    mNwService.stopTethering();
2379                }
2380                mNwService.tetherInterface(intf);
2381                mNwService.startTethering(tetheringDhcpRanges);
2382            } catch (Exception e) {
2383                loge("Error configuring interface " + intf + ", :" + e);
2384                return;
2385            }
2386
2387            logd("Started Dhcp server on " + intf);
2388        }
2389
2390        private void stopDhcpServer(String intf) {
2391            try {
2392                mNwService.untetherInterface(intf);
2393                for (String temp : mNwService.listTetheredInterfaces()) {
2394                    logd("List all interfaces " + temp);
2395                    if (temp.compareTo(intf) != 0) {
2396                        logd("Found other tethering interfaces, so keep tethering alive");
2397                        return;
2398                    }
2399                }
2400                mNwService.stopTethering();
2401            } catch (Exception e) {
2402                loge("Error stopping Dhcp server" + e);
2403                return;
2404            } finally {
2405                logd("Stopped Dhcp server");
2406            }
2407        }
2408
2409        private void notifyP2pEnableFailure() {
2410            Resources r = Resources.getSystem();
2411            AlertDialog dialog = new AlertDialog.Builder(mContext)
2412                    .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
2413                    .setMessage(r.getString(R.string.wifi_p2p_failed_message))
2414                    .setPositiveButton(r.getString(R.string.ok), null)
2415                    .create();
2416            dialog.setCanceledOnTouchOutside(false);
2417            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
2418            WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
2419            attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
2420            dialog.getWindow().setAttributes(attrs);
2421            dialog.show();
2422        }
2423
2424        private void addRowToDialog(ViewGroup group, int stringId, String value) {
2425            Resources r = Resources.getSystem();
2426            View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row,
2427                    group, false);
2428            ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId));
2429            ((TextView) row.findViewById(R.id.value)).setText(value);
2430            group.addView(row);
2431        }
2432
2433        private void notifyInvitationSent(String pin, String peerAddress) {
2434            Resources r = Resources.getSystem();
2435
2436            final View textEntryView = LayoutInflater.from(mContext)
2437                    .inflate(R.layout.wifi_p2p_dialog, null);
2438
2439            ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
2440            addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress));
2441            addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin);
2442
2443            AlertDialog dialog = new AlertDialog.Builder(mContext)
2444                    .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title))
2445                    .setView(textEntryView)
2446                    .setPositiveButton(r.getString(R.string.ok), null)
2447                    .create();
2448            dialog.setCanceledOnTouchOutside(false);
2449            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
2450            WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
2451            attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
2452            dialog.getWindow().setAttributes(attrs);
2453            dialog.show();
2454        }
2455
2456        private void notifyInvitationReceived() {
2457            Resources r = Resources.getSystem();
2458            final WpsInfo wps = mSavedPeerConfig.wps;
2459            final View textEntryView = LayoutInflater.from(mContext)
2460                    .inflate(R.layout.wifi_p2p_dialog, null);
2461
2462            ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
2463            addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName(
2464                    mSavedPeerConfig.deviceAddress));
2465
2466            final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin);
2467
2468            AlertDialog dialog = new AlertDialog.Builder(mContext)
2469                    .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title))
2470                    .setView(textEntryView)
2471                    .setPositiveButton(r.getString(R.string.accept), new OnClickListener() {
2472                            public void onClick(DialogInterface dialog, int which) {
2473                                if (wps.setup == WpsInfo.KEYPAD) {
2474                                    mSavedPeerConfig.wps.pin = pin.getText().toString();
2475                                }
2476                                if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig);
2477                                sendMessage(PEER_CONNECTION_USER_ACCEPT);
2478                            }
2479                        })
2480                    .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
2481                            @Override
2482                            public void onClick(DialogInterface dialog, int which) {
2483                                if (DBG) logd(getName() + " ignore connect");
2484                                sendMessage(PEER_CONNECTION_USER_REJECT);
2485                            }
2486                        })
2487                    .setOnCancelListener(new DialogInterface.OnCancelListener() {
2488                            @Override
2489                            public void onCancel(DialogInterface arg0) {
2490                                if (DBG) logd(getName() + " ignore connect");
2491                                sendMessage(PEER_CONNECTION_USER_REJECT);
2492                            }
2493                        })
2494                    .create();
2495            dialog.setCanceledOnTouchOutside(false);
2496
2497            // make the enter pin area or the display pin area visible
2498            switch (wps.setup) {
2499                case WpsInfo.KEYPAD:
2500                    if (DBG) logd("Enter pin section visible");
2501                    textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE);
2502                    break;
2503                case WpsInfo.DISPLAY:
2504                    if (DBG) logd("Shown pin section visible");
2505                    addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin);
2506                    break;
2507                default:
2508                    break;
2509            }
2510
2511            if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_APPLIANCE)
2512                    == Configuration.UI_MODE_TYPE_APPLIANCE) {
2513                // For appliance devices, add a key listener which accepts.
2514                dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
2515
2516                    @Override
2517                    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
2518                        // TODO: make the actual key come from a config value.
2519                        if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
2520                            sendMessage(PEER_CONNECTION_USER_ACCEPT);
2521                            dialog.dismiss();
2522                            return true;
2523                        }
2524                        return false;
2525                    }
2526                });
2527                // TODO: add timeout for this dialog.
2528                // TODO: update UI in appliance mode to tell user what to do.
2529            }
2530
2531            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
2532            WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
2533            attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
2534            dialog.getWindow().setAttributes(attrs);
2535            dialog.show();
2536        }
2537
2538        /**
2539         * This method unifies the persisent group list, cleans up unused
2540         * networks and if required, updates corresponding broadcast receivers
2541         * @param boolean if true, reload the group list from scratch
2542         *                and send broadcast message with fresh list
2543         */
2544        private void updatePersistentNetworks(boolean reload) {
2545            String listStr = mWifiNative.listNetworks();
2546            if (listStr == null) return;
2547
2548            boolean isSaveRequired = false;
2549            String[] lines = listStr.split("\n");
2550            if (lines == null) return;
2551
2552            if (reload) mGroups.clear();
2553
2554            // Skip the first line, which is a header
2555            for (int i = 1; i < lines.length; i++) {
2556                String[] result = lines[i].split("\t");
2557                if (result == null || result.length < 4) {
2558                    continue;
2559                }
2560                // network-id | ssid | bssid | flags
2561                int netId = -1;
2562                String ssid = result[1];
2563                String bssid = result[2];
2564                String flags = result[3];
2565                try {
2566                    netId = Integer.parseInt(result[0]);
2567                } catch (NumberFormatException e) {
2568                    e.printStackTrace();
2569                    continue;
2570                }
2571
2572                if (flags.indexOf("[CURRENT]") != -1) {
2573                    continue;
2574                }
2575                if (flags.indexOf("[P2P-PERSISTENT]") == -1) {
2576                    // The unused profile is sometimes remained when the p2p group formation
2577                    // is failed. So, we clean up the p2p group here.
2578                    if (DBG) logd("clean up the unused persistent group. netId=" + netId);
2579                    mWifiNative.removeNetwork(netId);
2580                    isSaveRequired = true;
2581                    continue;
2582                }
2583
2584                if (mGroups.contains(netId)) {
2585                    continue;
2586                }
2587
2588                WifiP2pGroup group = new WifiP2pGroup();
2589                group.setNetworkId(netId);
2590                group.setNetworkName(ssid);
2591                String mode = mWifiNative.getNetworkVariable(netId, "mode");
2592                if (mode != null && mode.equals("3")) {
2593                    group.setIsGroupOwner(true);
2594                }
2595                if (bssid.equalsIgnoreCase(mThisDevice.deviceAddress)) {
2596                    group.setOwner(mThisDevice);
2597                } else {
2598                    WifiP2pDevice device = new WifiP2pDevice();
2599                    device.deviceAddress = bssid;
2600                    group.setOwner(device);
2601                }
2602                mGroups.add(group);
2603                isSaveRequired = true;
2604            }
2605
2606            if (reload || isSaveRequired) {
2607                mWifiNative.saveConfig();
2608                sendP2pPersistentGroupsChangedBroadcast();
2609            }
2610        }
2611
2612        /**
2613         * A config is valid if it has a peer address that has already been
2614         * discovered
2615         * @param WifiP2pConfig config to be validated
2616         * @return true if it is invalid, false otherwise
2617         */
2618        private boolean isConfigInvalid(WifiP2pConfig config) {
2619            if (config == null) return true;
2620            if (TextUtils.isEmpty(config.deviceAddress)) return true;
2621            if (mPeers.get(config.deviceAddress) == null) return true;
2622            return false;
2623        }
2624
2625        private WifiP2pDevice fetchCurrentDeviceDetails(WifiP2pConfig config) {
2626            // Fetch & update group capability from supplicant on the device
2627            int gc = mWifiNative.getGroupCapability(config.deviceAddress);
2628            // TODO: The supplicant does not provide group capability changes as an event.
2629            // Having it pushed as an event would avoid polling for this information right
2630            // before a connection
2631            mPeers.updateGroupCapability(config.deviceAddress, gc);
2632            return mPeers.get(config.deviceAddress);
2633        }
2634
2635        /**
2636         * Start a p2p group negotiation and display pin if necessary
2637         * @param config for the peer
2638         */
2639        private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
2640            WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
2641
2642            String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
2643            try {
2644                Integer.parseInt(pin);
2645                notifyInvitationSent(pin, config.deviceAddress);
2646            } catch (NumberFormatException ignore) {
2647                // do nothing if p2pConnect did not return a pin
2648            }
2649        }
2650
2651        /**
2652         * Reinvoke a persistent group.
2653         *
2654         * @param config for the peer
2655         * @return true on success, false on failure
2656         */
2657        private boolean reinvokePersistentGroup(WifiP2pConfig config) {
2658            WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
2659
2660            boolean join = dev.isGroupOwner();
2661            String ssid = mWifiNative.p2pGetSsid(dev.deviceAddress);
2662            if (DBG) logd("target ssid is " + ssid + " join:" + join);
2663
2664            if (join && dev.isGroupLimit()) {
2665                if (DBG) logd("target device reaches group limit.");
2666
2667                // if the target group has reached the limit,
2668                // try group formation.
2669                join = false;
2670            } else if (join) {
2671                int netId = mGroups.getNetworkId(dev.deviceAddress, ssid);
2672                if (netId >= 0) {
2673                    // Skip WPS and start 4way handshake immediately.
2674                    if (!mWifiNative.p2pGroupAdd(netId)) {
2675                        return false;
2676                    }
2677                    return true;
2678                }
2679            }
2680
2681            if (!join && dev.isDeviceLimit()) {
2682                loge("target device reaches the device limit.");
2683                return false;
2684            }
2685
2686            if (!join && dev.isInvitationCapable()) {
2687                int netId = WifiP2pGroup.PERSISTENT_NET_ID;
2688                if (config.netId >= 0) {
2689                    if (config.deviceAddress.equals(mGroups.getOwnerAddr(config.netId))) {
2690                        netId = config.netId;
2691                    }
2692                } else {
2693                    netId = mGroups.getNetworkId(dev.deviceAddress);
2694                }
2695                if (netId < 0) {
2696                    netId = getNetworkIdFromClientList(dev.deviceAddress);
2697                }
2698                if (DBG) logd("netId related with " + dev.deviceAddress + " = " + netId);
2699                if (netId >= 0) {
2700                    // Invoke the persistent group.
2701                    if (mWifiNative.p2pReinvoke(netId, dev.deviceAddress)) {
2702                        // Save network id. It'll be used when an invitation
2703                        // result event is received.
2704                        config.netId = netId;
2705                        return true;
2706                    } else {
2707                        loge("p2pReinvoke() failed, update networks");
2708                        updatePersistentNetworks(RELOAD);
2709                        return false;
2710                    }
2711                }
2712            }
2713            return false;
2714        }
2715
2716        /**
2717         * Return the network id of the group owner profile which has the p2p client with
2718         * the specified device address in it's client list.
2719         * If more than one persistent group of the same address is present in its client
2720         * lists, return the first one.
2721         *
2722         * @param deviceAddress p2p device address.
2723         * @return the network id. if not found, return -1.
2724         */
2725        private int getNetworkIdFromClientList(String deviceAddress) {
2726            if (deviceAddress == null) return -1;
2727
2728            Collection<WifiP2pGroup> groups = mGroups.getGroupList();
2729            for (WifiP2pGroup group : groups) {
2730                int netId = group.getNetworkId();
2731                String[] p2pClientList = getClientList(netId);
2732                if (p2pClientList == null) continue;
2733                for (String client : p2pClientList) {
2734                    if (deviceAddress.equalsIgnoreCase(client)) {
2735                        return netId;
2736                    }
2737                }
2738            }
2739            return -1;
2740        }
2741
2742        /**
2743         * Return p2p client list associated with the specified network id.
2744         * @param netId network id.
2745         * @return p2p client list. if not found, return null.
2746         */
2747        private String[] getClientList(int netId) {
2748            String p2pClients = mWifiNative.getNetworkVariable(netId, "p2p_client_list");
2749            if (p2pClients == null) {
2750                return null;
2751            }
2752            return p2pClients.split(" ");
2753        }
2754
2755        /**
2756         * Remove the specified p2p client from the specified profile.
2757         * @param netId network id of the profile.
2758         * @param addr p2p client address to be removed.
2759         * @param isRemovable if true, remove the specified profile if its client
2760         *             list becomes empty.
2761         * @return whether removing the specified p2p client is successful or not.
2762         */
2763        private boolean removeClientFromList(int netId, String addr, boolean isRemovable) {
2764            StringBuilder modifiedClientList =  new StringBuilder();
2765            String[] currentClientList = getClientList(netId);
2766            boolean isClientRemoved = false;
2767            if (currentClientList != null) {
2768                for (String client : currentClientList) {
2769                    if (!client.equalsIgnoreCase(addr)) {
2770                        modifiedClientList.append(" ");
2771                        modifiedClientList.append(client);
2772                    } else {
2773                        isClientRemoved = true;
2774                    }
2775                }
2776            }
2777            if (modifiedClientList.length() == 0 && isRemovable) {
2778                // the client list is empty. so remove it.
2779                if (DBG) logd("Remove unknown network");
2780                mGroups.remove(netId);
2781                return true;
2782            }
2783
2784            if (!isClientRemoved) {
2785                // specified p2p client is not found. already removed.
2786                return false;
2787            }
2788
2789            if (DBG) logd("Modified client list: " + modifiedClientList);
2790            if (modifiedClientList.length() == 0) {
2791                modifiedClientList.append("\"\"");
2792            }
2793            mWifiNative.setNetworkVariable(netId,
2794                    "p2p_client_list", modifiedClientList.toString());
2795            mWifiNative.saveConfig();
2796            return true;
2797        }
2798
2799        private void setWifiP2pInfoOnGroupFormation(InetAddress serverInetAddress) {
2800            mWifiP2pInfo.groupFormed = true;
2801            mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
2802            mWifiP2pInfo.groupOwnerAddress = serverInetAddress;
2803        }
2804
2805        private void resetWifiP2pInfo() {
2806            mWifiP2pInfo.groupFormed = false;
2807            mWifiP2pInfo.isGroupOwner = false;
2808            mWifiP2pInfo.groupOwnerAddress = null;
2809        }
2810
2811        private String getDeviceName(String deviceAddress) {
2812            WifiP2pDevice d = mPeers.get(deviceAddress);
2813            if (d != null) {
2814                return d.deviceName;
2815            }
2816            //Treat the address as name if there is no match
2817            return deviceAddress;
2818        }
2819
2820        private String getPersistedDeviceName() {
2821            String deviceName = Settings.Global.getString(mContext.getContentResolver(),
2822                    Settings.Global.WIFI_P2P_DEVICE_NAME);
2823            if (deviceName == null) {
2824                // We use the 4 digits of the ANDROID_ID to have a friendly
2825                // default that has low likelihood of collision with a peer
2826                String id = Settings.Secure.getString(mContext.getContentResolver(),
2827                        Settings.Secure.ANDROID_ID);
2828                return "Android_" + id.substring(0, 4);
2829            }
2830            return deviceName;
2831        }
2832
2833        private boolean setAndPersistDeviceName(String devName) {
2834            if (devName == null) return false;
2835
2836            if (!mWifiNative.setDeviceName(devName)) {
2837                loge("Failed to set device name " + devName);
2838                return false;
2839            }
2840
2841            mThisDevice.deviceName = devName;
2842            mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
2843
2844            Settings.Global.putString(mContext.getContentResolver(),
2845                    Settings.Global.WIFI_P2P_DEVICE_NAME, devName);
2846            sendThisDeviceChangedBroadcast();
2847            return true;
2848        }
2849
2850        private boolean setWfdInfo(WifiP2pWfdInfo wfdInfo) {
2851            boolean success;
2852
2853            if (!wfdInfo.isWfdEnabled()) {
2854                success = mWifiNative.setWfdEnable(false);
2855            } else {
2856                success =
2857                    mWifiNative.setWfdEnable(true)
2858                    && mWifiNative.setWfdDeviceInfo(wfdInfo.getDeviceInfoHex());
2859            }
2860
2861            if (!success) {
2862                loge("Failed to set wfd properties");
2863                return false;
2864            }
2865
2866            mThisDevice.wfdInfo = wfdInfo;
2867            sendThisDeviceChangedBroadcast();
2868            return true;
2869        }
2870
2871        private void initializeP2pSettings() {
2872            mWifiNative.setPersistentReconnect(true);
2873            mThisDevice.deviceName = getPersistedDeviceName();
2874            mWifiNative.setDeviceName(mThisDevice.deviceName);
2875            // DIRECT-XY-DEVICENAME (XY is randomly generated)
2876            mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
2877            mWifiNative.setDeviceType(mThisDevice.primaryDeviceType);
2878            // Supplicant defaults to using virtual display with display
2879            // which refers to a remote display. Use physical_display
2880            mWifiNative.setConfigMethods("virtual_push_button physical_display keypad");
2881            // STA has higher priority over P2P
2882            mWifiNative.setConcurrencyPriority("sta");
2883
2884            mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
2885            updateThisDevice(WifiP2pDevice.AVAILABLE);
2886            if (DBG) logd("DeviceAddress: " + mThisDevice.deviceAddress);
2887
2888            mClientInfoList.clear();
2889            mWifiNative.p2pFlush();
2890            mWifiNative.p2pServiceFlush();
2891            mServiceTransactionId = 0;
2892            mServiceDiscReqId = null;
2893
2894            updatePersistentNetworks(RELOAD);
2895        }
2896
2897        private void updateThisDevice(int status) {
2898            mThisDevice.status = status;
2899            sendThisDeviceChangedBroadcast();
2900        }
2901
2902        private void handleGroupCreationFailure() {
2903            resetWifiP2pInfo();
2904            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.FAILED, null, null);
2905            sendP2pConnectionChangedBroadcast();
2906
2907            // Remove only the peer we failed to connect to so that other devices discovered
2908            // that have not timed out still remain in list for connection
2909            boolean peersChanged = mPeers.remove(mPeersLostDuringConnection);
2910            if (!TextUtils.isEmpty(mSavedPeerConfig.deviceAddress)
2911                    && mPeers.remove(mSavedPeerConfig.deviceAddress) != null) {
2912                peersChanged = true;
2913            }
2914            if (peersChanged) {
2915                sendPeersChangedBroadcast();
2916            }
2917
2918            mPeersLostDuringConnection.clear();
2919            mServiceDiscReqId = null;
2920            sendMessage(WifiP2pManager.DISCOVER_PEERS);
2921        }
2922
2923        private void handleGroupRemoved() {
2924            if (mGroup.isGroupOwner()) {
2925                stopDhcpServer(mGroup.getInterface());
2926            } else {
2927                if (DBG) logd("stop IpManager");
2928                stopIpManager();
2929                try {
2930                    mNwService.removeInterfaceFromLocalNetwork(mGroup.getInterface());
2931                } catch (RemoteException e) {
2932                    loge("Failed to remove iface from local network " + e);
2933                }
2934            }
2935
2936            try {
2937                mNwService.clearInterfaceAddresses(mGroup.getInterface());
2938            } catch (Exception e) {
2939                loge("Failed to clear addresses " + e);
2940            }
2941
2942            // Clear any timeout that was set. This is essential for devices
2943            // that reuse the main p2p interface for a created group.
2944            mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
2945
2946            boolean peersChanged = false;
2947            // Remove only peers part of the group, so that other devices discovered
2948            // that have not timed out still remain in list for connection
2949            for (WifiP2pDevice d : mGroup.getClientList()) {
2950                if (mPeers.remove(d)) peersChanged = true;
2951            }
2952            if (mPeers.remove(mGroup.getOwner())) peersChanged = true;
2953            if (mPeers.remove(mPeersLostDuringConnection)) peersChanged = true;
2954            if (peersChanged) {
2955                sendPeersChangedBroadcast();
2956            }
2957
2958            mGroup = null;
2959            mPeersLostDuringConnection.clear();
2960            mServiceDiscReqId = null;
2961
2962            if (mTemporarilyDisconnectedWifi) {
2963                mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 0);
2964                mTemporarilyDisconnectedWifi = false;
2965            }
2966        }
2967
2968        private void replyToMessage(Message msg, int what) {
2969            // State machine initiated requests can have replyTo set to null
2970            // indicating there are no recipients, we ignore those reply actions
2971            if (msg.replyTo == null) return;
2972            Message dstMsg = obtainMessage(msg);
2973            dstMsg.what = what;
2974            mReplyChannel.replyToMessage(msg, dstMsg);
2975        }
2976
2977        private void replyToMessage(Message msg, int what, int arg1) {
2978            if (msg.replyTo == null) return;
2979            Message dstMsg = obtainMessage(msg);
2980            dstMsg.what = what;
2981            dstMsg.arg1 = arg1;
2982            mReplyChannel.replyToMessage(msg, dstMsg);
2983        }
2984
2985        private void replyToMessage(Message msg, int what, Object obj) {
2986            if (msg.replyTo == null) return;
2987            Message dstMsg = obtainMessage(msg);
2988            dstMsg.what = what;
2989            dstMsg.obj = obj;
2990            mReplyChannel.replyToMessage(msg, dstMsg);
2991        }
2992
2993        private Message obtainMessage(Message srcMsg) {
2994            // arg2 on the source message has a hash code that needs to
2995            // be retained in replies see WifiP2pManager for details
2996            Message msg = Message.obtain();
2997            msg.arg2 = srcMsg.arg2;
2998            return msg;
2999        }
3000
3001        @Override
3002        protected void logd(String s) {
3003            Slog.d(TAG, s);
3004        }
3005
3006        @Override
3007        protected void loge(String s) {
3008            Slog.e(TAG, s);
3009        }
3010
3011        /**
3012         * Update service discovery request to wpa_supplicant.
3013         */
3014        private boolean updateSupplicantServiceRequest() {
3015            clearSupplicantServiceRequest();
3016
3017            StringBuffer sb = new StringBuffer();
3018            for (ClientInfo c: mClientInfoList.values()) {
3019                int key;
3020                WifiP2pServiceRequest req;
3021                for (int i = 0; i < c.mReqList.size(); i++) {
3022                    req = c.mReqList.valueAt(i);
3023                    if (req != null) {
3024                        sb.append(req.getSupplicantQuery());
3025                    }
3026                }
3027            }
3028
3029            if (sb.length() == 0) {
3030                return false;
3031            }
3032
3033            mServiceDiscReqId = mWifiNative.p2pServDiscReq("00:00:00:00:00:00", sb.toString());
3034            if (mServiceDiscReqId == null) {
3035                return false;
3036            }
3037            return true;
3038        }
3039
3040        /**
3041         * Clear service discovery request in wpa_supplicant
3042         */
3043        private void clearSupplicantServiceRequest() {
3044            if (mServiceDiscReqId == null) return;
3045
3046            mWifiNative.p2pServDiscCancelReq(mServiceDiscReqId);
3047            mServiceDiscReqId = null;
3048        }
3049
3050        private boolean addServiceRequest(Messenger m, WifiP2pServiceRequest req) {
3051            // TODO: We could track individual service adds separately and avoid
3052            // having to do update all service requests on every new request
3053            clearClientDeadChannels();
3054            ClientInfo clientInfo = getClientInfo(m, true);
3055            if (clientInfo == null) {
3056                return false;
3057            }
3058
3059            ++mServiceTransactionId;
3060            //The Wi-Fi p2p spec says transaction id should be non-zero
3061            if (mServiceTransactionId == 0) ++mServiceTransactionId;
3062            req.setTransactionId(mServiceTransactionId);
3063            clientInfo.mReqList.put(mServiceTransactionId, req);
3064
3065            if (mServiceDiscReqId == null) {
3066                return true;
3067            }
3068
3069            return updateSupplicantServiceRequest();
3070        }
3071
3072        private void removeServiceRequest(Messenger m, WifiP2pServiceRequest req) {
3073            ClientInfo clientInfo = getClientInfo(m, false);
3074            if (clientInfo == null) {
3075                return;
3076            }
3077
3078            // Application does not have transaction id information
3079            // go through stored requests to remove
3080            boolean removed = false;
3081            for (int i = 0; i < clientInfo.mReqList.size(); i++) {
3082                if (req.equals(clientInfo.mReqList.valueAt(i))) {
3083                    removed = true;
3084                    clientInfo.mReqList.removeAt(i);
3085                    break;
3086                }
3087            }
3088
3089            if (!removed) return;
3090
3091            if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
3092                if (DBG) logd("remove client information from framework");
3093                mClientInfoList.remove(clientInfo.mMessenger);
3094            }
3095
3096            if (mServiceDiscReqId == null) {
3097                return;
3098            }
3099
3100            updateSupplicantServiceRequest();
3101        }
3102
3103        private void clearServiceRequests(Messenger m) {
3104
3105            ClientInfo clientInfo = getClientInfo(m, false);
3106            if (clientInfo == null) {
3107                return;
3108            }
3109
3110            if (clientInfo.mReqList.size() == 0) {
3111                return;
3112            }
3113
3114            clientInfo.mReqList.clear();
3115
3116            if (clientInfo.mServList.size() == 0) {
3117                if (DBG) logd("remove channel information from framework");
3118                mClientInfoList.remove(clientInfo.mMessenger);
3119            }
3120
3121            if (mServiceDiscReqId == null) {
3122                return;
3123            }
3124
3125            updateSupplicantServiceRequest();
3126        }
3127
3128        private boolean addLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
3129            clearClientDeadChannels();
3130            ClientInfo clientInfo = getClientInfo(m, true);
3131            if (clientInfo == null) {
3132                return false;
3133            }
3134
3135            if (!clientInfo.mServList.add(servInfo)) {
3136                return false;
3137            }
3138
3139            if (!mWifiNative.p2pServiceAdd(servInfo)) {
3140                clientInfo.mServList.remove(servInfo);
3141                return false;
3142            }
3143
3144            return true;
3145        }
3146
3147        private void removeLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
3148            ClientInfo clientInfo = getClientInfo(m, false);
3149            if (clientInfo == null) {
3150                return;
3151            }
3152
3153            mWifiNative.p2pServiceDel(servInfo);
3154
3155            clientInfo.mServList.remove(servInfo);
3156            if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
3157                if (DBG) logd("remove client information from framework");
3158                mClientInfoList.remove(clientInfo.mMessenger);
3159            }
3160        }
3161
3162        private void clearLocalServices(Messenger m) {
3163            ClientInfo clientInfo = getClientInfo(m, false);
3164            if (clientInfo == null) {
3165                return;
3166            }
3167
3168            for (WifiP2pServiceInfo servInfo: clientInfo.mServList) {
3169                mWifiNative.p2pServiceDel(servInfo);
3170            }
3171
3172            clientInfo.mServList.clear();
3173            if (clientInfo.mReqList.size() == 0) {
3174                if (DBG) logd("remove client information from framework");
3175                mClientInfoList.remove(clientInfo.mMessenger);
3176            }
3177        }
3178
3179        private void clearClientInfo(Messenger m) {
3180            clearLocalServices(m);
3181            clearServiceRequests(m);
3182        }
3183
3184        /**
3185         * Send the service response to the WifiP2pManager.Channel.
3186         * @param WifiP2pServiceResponse response to service discovery
3187         */
3188        private void sendServiceResponse(WifiP2pServiceResponse resp) {
3189            for (ClientInfo c : mClientInfoList.values()) {
3190                WifiP2pServiceRequest req = c.mReqList.get(resp.getTransactionId());
3191                if (req != null) {
3192                    Message msg = Message.obtain();
3193                    msg.what = WifiP2pManager.RESPONSE_SERVICE;
3194                    msg.arg1 = 0;
3195                    msg.arg2 = 0;
3196                    msg.obj = resp;
3197                    try {
3198                        c.mMessenger.send(msg);
3199                    } catch (RemoteException e) {
3200                        if (DBG) logd("detect dead channel");
3201                        clearClientInfo(c.mMessenger);
3202                        return;
3203                    }
3204                }
3205            }
3206        }
3207
3208        /**
3209         * We don't get notifications of clients that have gone away.
3210         * We detect this actively when services are added and throw
3211         * them away.
3212         *
3213         * TODO: This can be done better with full async channels.
3214         */
3215        private void clearClientDeadChannels() {
3216            ArrayList<Messenger> deadClients = new ArrayList<Messenger>();
3217
3218            for (ClientInfo c : mClientInfoList.values()) {
3219                Message msg = Message.obtain();
3220                msg.what = WifiP2pManager.PING;
3221                msg.arg1 = 0;
3222                msg.arg2 = 0;
3223                msg.obj = null;
3224                try {
3225                    c.mMessenger.send(msg);
3226                } catch (RemoteException e) {
3227                    if (DBG) logd("detect dead channel");
3228                    deadClients.add(c.mMessenger);
3229                }
3230            }
3231
3232            for (Messenger m : deadClients) {
3233                clearClientInfo(m);
3234            }
3235        }
3236
3237        /**
3238         * Return the specified ClientInfo.
3239         * @param m Messenger
3240         * @param createIfNotExist if true and the specified channel info does not exist,
3241         * create new client info.
3242         * @return the specified ClientInfo.
3243         */
3244        private ClientInfo getClientInfo(Messenger m, boolean createIfNotExist) {
3245            ClientInfo clientInfo = mClientInfoList.get(m);
3246
3247            if (clientInfo == null && createIfNotExist) {
3248                if (DBG) logd("add a new client");
3249                clientInfo = new ClientInfo(m);
3250                mClientInfoList.put(m, clientInfo);
3251            }
3252
3253            return clientInfo;
3254        }
3255
3256        /**
3257         * Enforces permissions on the caller who is requesting for P2p Peers
3258         * @param pkg Bundle containing the calling package string
3259         * @param uid of the caller
3260         * @return WifiP2pDeviceList the peer list
3261         */
3262        private WifiP2pDeviceList getPeers(Bundle pkg, int uid) {
3263            String pkgName = pkg.getString(WifiP2pManager.CALLING_PACKAGE);
3264            boolean scanPermission = false;
3265            WifiPermissionsUtil wifiPermissionsUtil;
3266            // getPeers() is guaranteed to be invoked after Wifi Service is up
3267            // This ensures getInstance() will return a non-null object now
3268            if (mWifiInjector == null) {
3269                mWifiInjector = WifiInjector.getInstance();
3270            }
3271            wifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil();
3272            // Minimum Version to enforce location permission on apps build on the next
3273            // release. Bug 32288318 tracks updates to the version code. This code to check
3274            // version will serve as a place holder till the next release is available.
3275            try {
3276                scanPermission = wifiPermissionsUtil.canAccessScanResults(pkgName,
3277                        uid, Build.VERSION_CODES.N + 2);
3278            } catch (SecurityException e) {
3279                Log.e(TAG, "Security Exception, cannot access peer list");
3280            }
3281            if (scanPermission) {
3282                return new WifiP2pDeviceList(mPeers);
3283            } else {
3284                return new WifiP2pDeviceList();
3285            }
3286        }
3287    }
3288
3289    /**
3290     * Information about a particular client and we track the service discovery requests
3291     * and the local services registered by the client.
3292     */
3293    private class ClientInfo {
3294
3295        // A reference to WifiP2pManager.Channel handler.
3296        // The response of this request is notified to WifiP2pManager.Channel handler
3297        private Messenger mMessenger;
3298
3299        // A service discovery request list.
3300        private SparseArray<WifiP2pServiceRequest> mReqList;
3301
3302        // A local service information list.
3303        private List<WifiP2pServiceInfo> mServList;
3304
3305        private ClientInfo(Messenger m) {
3306            mMessenger = m;
3307            mReqList = new SparseArray();
3308            mServList = new ArrayList<WifiP2pServiceInfo>();
3309        }
3310    }
3311}
3312