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