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