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