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