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