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