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