WifiP2pService.java revision 1f095869536472c178046bb63c59947508eac4a6
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 17package android.net.wifi.p2p; 18 19import android.app.AlertDialog; 20import android.app.Notification; 21import android.app.NotificationManager; 22import android.app.PendingIntent; 23import android.content.BroadcastReceiver; 24import android.content.Context; 25import android.content.DialogInterface; 26import android.content.DialogInterface.OnClickListener; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.content.pm.PackageManager; 30import android.content.res.Resources; 31import android.net.IConnectivityManager; 32import android.net.ConnectivityManager; 33import android.net.DhcpInfoInternal; 34import android.net.DhcpStateMachine; 35import android.net.InterfaceConfiguration; 36import android.net.LinkAddress; 37import android.net.LinkProperties; 38import android.net.NetworkInfo; 39import android.net.NetworkUtils; 40import android.net.wifi.WifiManager; 41import android.net.wifi.WifiMonitor; 42import android.net.wifi.WifiNative; 43import android.net.wifi.WifiStateMachine; 44import android.net.wifi.WpsInfo; 45import android.os.Binder; 46import android.os.IBinder; 47import android.os.INetworkManagementService; 48import android.os.Handler; 49import android.os.HandlerThread; 50import android.os.Message; 51import android.os.Messenger; 52import android.os.ServiceManager; 53import android.os.SystemProperties; 54import android.provider.Settings; 55import android.text.TextUtils; 56import android.util.Slog; 57import android.view.LayoutInflater; 58import android.view.View; 59import android.view.ViewGroup; 60import android.view.WindowManager; 61import android.widget.EditText; 62import android.widget.TextView; 63 64import com.android.internal.R; 65import com.android.internal.telephony.TelephonyIntents; 66import com.android.internal.util.AsyncChannel; 67import com.android.internal.util.Protocol; 68import com.android.internal.util.State; 69import com.android.internal.util.StateMachine; 70 71import java.io.FileDescriptor; 72import java.io.PrintWriter; 73import java.util.Collection; 74 75/** 76 * WifiP2pService inclues a state machine to perform Wi-Fi p2p operations. Applications 77 * communicate with this service to issue device discovery and connectivity requests 78 * through the WifiP2pManager interface. The state machine communicates with the wifi 79 * driver through wpa_supplicant and handles the event responses through WifiMonitor. 80 * 81 * Note that the term Wifi when used without a p2p suffix refers to the client mode 82 * of Wifi operation 83 * @hide 84 */ 85public class WifiP2pService extends IWifiP2pManager.Stub { 86 private static final String TAG = "WifiP2pService"; 87 private static final boolean DBG = true; 88 private static final String NETWORKTYPE = "WIFI_P2P"; 89 90 private Context mContext; 91 private String mInterface; 92 private Notification mNotification; 93 94 INetworkManagementService mNwService; 95 private DhcpStateMachine mDhcpStateMachine; 96 97 //Tracked to notify the user about wifi client/hotspot being shut down 98 //during p2p bring up 99 private int mWifiState = WifiManager.WIFI_STATE_DISABLED; 100 private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED; 101 102 private P2pStateMachine mP2pStateMachine; 103 private AsyncChannel mReplyChannel = new AsyncChannel(); 104 private AsyncChannel mWifiChannel; 105 106 private static final Boolean JOIN_GROUP = true; 107 private static final Boolean FORM_GROUP = false; 108 109 /* Two minutes comes from the wpa_supplicant setting */ 110 private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000; 111 private static int mGroupCreatingTimeoutIndex = 0; 112 113 /** 114 * Delay between restarts upon failure to setup connection with supplicant 115 */ 116 private static final int P2P_RESTART_INTERVAL_MSECS = 5000; 117 118 /** 119 * Number of times we attempt to restart p2p 120 */ 121 private static final int P2P_RESTART_TRIES = 5; 122 123 private int mP2pRestartCount = 0; 124 125 private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE; 126 127 /* Message sent to WifiStateMachine to indicate p2p enable is pending */ 128 public static final int P2P_ENABLE_PENDING = BASE + 1; 129 /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */ 130 public static final int WIFI_ENABLE_PROCEED = BASE + 2; 131 132 /* Delayed message to timeout group creation */ 133 public static final int GROUP_CREATING_TIMED_OUT = BASE + 3; 134 135 /* User accepted to disable Wi-Fi in order to enable p2p */ 136 private static final int WIFI_DISABLE_USER_ACCEPT = BASE + 4; 137 /* User rejected to disable Wi-Fi in order to enable p2p */ 138 private static final int WIFI_DISABLE_USER_REJECT = BASE + 5; 139 140 /* User accepted a peer request */ 141 private static final int PEER_CONNECTION_USER_ACCEPT = BASE + 6; 142 /* User rejected a peer request */ 143 private static final int PEER_CONNECTION_USER_REJECT = BASE + 7; 144 145 /* Airplane mode changed */ 146 private static final int AIRPLANE_MODE_CHANGED = BASE + 8; 147 /* Emergency callback mode */ 148 private static final int EMERGENCY_CALLBACK_MODE = BASE + 9; 149 150 private final boolean mP2pSupported; 151 152 private WifiP2pDevice mThisDevice = new WifiP2pDevice(); 153 154 /* When a group has been explicitly created by an app, we persist the group 155 * even after all clients have been disconnected until an explicit remove 156 * is invoked */ 157 private boolean mPersistGroup; 158 159 private NetworkInfo mNetworkInfo; 160 161 /* Is chosen as a unique range to avoid conflict with 162 the range defined in Tethering.java */ 163 private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"}; 164 private static final String SERVER_ADDRESS = "192.168.49.1"; 165 166 public WifiP2pService(Context context) { 167 mContext = context; 168 169 mInterface = SystemProperties.get("wifi.interface", "wlan0"); 170 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, ""); 171 172 mP2pSupported = mContext.getPackageManager().hasSystemFeature( 173 PackageManager.FEATURE_WIFI_DIRECT); 174 175 mThisDevice.primaryDeviceType = mContext.getResources().getString( 176 com.android.internal.R.string.config_wifi_p2p_device_type); 177 mThisDevice.deviceName = getDefaultDeviceName(); 178 179 mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported); 180 mP2pStateMachine.start(); 181 182 // broadcasts 183 IntentFilter filter = new IntentFilter(); 184 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 185 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); 186 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 187 filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 188 mContext.registerReceiver(new WifiStateReceiver(), filter); 189 190 } 191 192 public void connectivityServiceReady() { 193 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 194 mNwService = INetworkManagementService.Stub.asInterface(b); 195 } 196 197 private class WifiStateReceiver extends BroadcastReceiver { 198 @Override 199 public void onReceive(Context context, Intent intent) { 200 String action = intent.getAction(); 201 if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 202 mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 203 WifiManager.WIFI_STATE_DISABLED); 204 } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { 205 mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 206 WifiManager.WIFI_AP_STATE_DISABLED); 207 } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { 208 mP2pStateMachine.sendMessage(AIRPLANE_MODE_CHANGED); 209 } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { 210 if (intent.getBooleanExtra("phoneinECMState", false) == true) { 211 mP2pStateMachine.sendMessage(EMERGENCY_CALLBACK_MODE); 212 } 213 } 214 } 215 } 216 217 private void enforceAccessPermission() { 218 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, 219 "WifiP2pService"); 220 } 221 222 private void enforceChangePermission() { 223 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, 224 "WifiP2pService"); 225 } 226 227 /* We use the 4 digits of the ANDROID_ID to have a friendly 228 * default that has low likelihood of collision with a peer */ 229 private String getDefaultDeviceName() { 230 String id = Settings.Secure.getString(mContext.getContentResolver(), 231 Settings.Secure.ANDROID_ID); 232 return "Android_" + id.substring(0,4); 233 } 234 235 /** 236 * Get a reference to handler. This is used by a client to establish 237 * an AsyncChannel communication with WifiP2pService 238 */ 239 public Messenger getMessenger() { 240 enforceAccessPermission(); 241 enforceChangePermission(); 242 return new Messenger(mP2pStateMachine.getHandler()); 243 } 244 245 @Override 246 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 247 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 248 != PackageManager.PERMISSION_GRANTED) { 249 pw.println("Permission Denial: can't dump WifiP2pService from from pid=" 250 + Binder.getCallingPid() 251 + ", uid=" + Binder.getCallingUid()); 252 return; 253 } 254 } 255 256 257 /** 258 * Handles interaction with WifiStateMachine 259 */ 260 private class P2pStateMachine extends StateMachine { 261 262 private DefaultState mDefaultState = new DefaultState(); 263 private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState(); 264 private P2pDisablingState mP2pDisablingState = new P2pDisablingState(); 265 private P2pDisabledState mP2pDisabledState = new P2pDisabledState(); 266 private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState(); 267 private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState(); 268 private P2pEnablingState mP2pEnablingState = new P2pEnablingState(); 269 private P2pEnabledState mP2pEnabledState = new P2pEnabledState(); 270 // Inactive is when p2p is enabled with no connectivity 271 private InactiveState mInactiveState = new InactiveState(); 272 private GroupCreatingState mGroupCreatingState = new GroupCreatingState(); 273 private UserAuthorizingInvitationState mUserAuthorizingInvitationState 274 = new UserAuthorizingInvitationState(); 275 private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState(); 276 private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState(); 277 278 private GroupCreatedState mGroupCreatedState = new GroupCreatedState(); 279 private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState(); 280 281 private WifiMonitor mWifiMonitor = new WifiMonitor(this); 282 283 private WifiP2pDeviceList mPeers = new WifiP2pDeviceList(); 284 private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo(); 285 private WifiP2pGroup mGroup; 286 287 // Saved WifiP2pConfig for a peer connection 288 private WifiP2pConfig mSavedPeerConfig; 289 290 // Saved WifiP2pGroup from invitation request 291 private WifiP2pGroup mSavedP2pGroup; 292 293 P2pStateMachine(String name, boolean p2pSupported) { 294 super(name); 295 296 addState(mDefaultState); 297 addState(mP2pNotSupportedState, mDefaultState); 298 addState(mP2pDisablingState, mDefaultState); 299 addState(mP2pDisabledState, mDefaultState); 300 addState(mWaitForUserActionState, mP2pDisabledState); 301 addState(mWaitForWifiDisableState, mP2pDisabledState); 302 addState(mP2pEnablingState, mDefaultState); 303 addState(mP2pEnabledState, mDefaultState); 304 addState(mInactiveState, mP2pEnabledState); 305 addState(mGroupCreatingState, mP2pEnabledState); 306 addState(mUserAuthorizingInvitationState, mGroupCreatingState); 307 addState(mProvisionDiscoveryState, mGroupCreatingState); 308 addState(mGroupNegotiationState, mGroupCreatingState); 309 addState(mGroupCreatedState, mP2pEnabledState); 310 addState(mUserAuthorizingJoinState, mGroupCreatedState); 311 312 if (p2pSupported) { 313 setInitialState(mP2pDisabledState); 314 } else { 315 setInitialState(mP2pNotSupportedState); 316 } 317 } 318 319 class DefaultState extends State { 320 @Override 321 public boolean processMessage(Message message) { 322 if (DBG) logd(getName() + message.toString()); 323 switch (message.what) { 324 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 325 if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 326 if (DBG) logd("Full connection with WifiStateMachine established"); 327 mWifiChannel = (AsyncChannel) message.obj; 328 } else { 329 loge("Full connection failure, error = " + message.arg1); 330 mWifiChannel = null; 331 } 332 break; 333 334 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 335 if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { 336 loge("Send failed, client connection lost"); 337 } else { 338 loge("Client connection lost with reason: " + message.arg1); 339 } 340 mWifiChannel = null; 341 break; 342 343 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: 344 AsyncChannel ac = new AsyncChannel(); 345 ac.connect(mContext, getHandler(), message.replyTo); 346 break; 347 case WifiStateMachine.WIFI_ENABLE_PENDING: 348 // Disable p2p operation before we can respond 349 sendMessage(WifiP2pManager.DISABLE_P2P); 350 deferMessage(message); 351 break; 352 case WifiP2pManager.ENABLE_P2P: 353 replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED, 354 WifiP2pManager.BUSY); 355 break; 356 case WifiP2pManager.DISABLE_P2P: 357 replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED, 358 WifiP2pManager.BUSY); 359 break; 360 case WifiP2pManager.DISCOVER_PEERS: 361 replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, 362 WifiP2pManager.BUSY); 363 break; 364 case WifiP2pManager.CONNECT: 365 replyToMessage(message, WifiP2pManager.CONNECT_FAILED, 366 WifiP2pManager.BUSY); 367 break; 368 case WifiP2pManager.CANCEL_CONNECT: 369 replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED, 370 WifiP2pManager.BUSY); 371 break; 372 case WifiP2pManager.CREATE_GROUP: 373 replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, 374 WifiP2pManager.BUSY); 375 break; 376 case WifiP2pManager.REMOVE_GROUP: 377 replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED, 378 WifiP2pManager.BUSY); 379 break; 380 case WifiP2pManager.REQUEST_PEERS: 381 replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers); 382 break; 383 case WifiP2pManager.REQUEST_CONNECTION_INFO: 384 replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, mWifiP2pInfo); 385 break; 386 case WifiP2pManager.REQUEST_GROUP_INFO: 387 replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup); 388 break; 389 case AIRPLANE_MODE_CHANGED: 390 if (isAirplaneModeOn()) sendMessage(WifiP2pManager.DISABLE_P2P); 391 break; 392 case EMERGENCY_CALLBACK_MODE: 393 sendMessage(WifiP2pManager.DISABLE_P2P); 394 break; 395 // Ignore 396 case WifiMonitor.P2P_INVITATION_RESULT_EVENT: 397 case WIFI_DISABLE_USER_ACCEPT: 398 case WIFI_DISABLE_USER_REJECT: 399 case PEER_CONNECTION_USER_ACCEPT: 400 case PEER_CONNECTION_USER_REJECT: 401 case GROUP_CREATING_TIMED_OUT: 402 break; 403 default: 404 loge("Unhandled message " + message); 405 return NOT_HANDLED; 406 } 407 return HANDLED; 408 } 409 } 410 411 class P2pNotSupportedState extends State { 412 @Override 413 public boolean processMessage(Message message) { 414 switch (message.what) { 415 // Allow Wi-Fi to proceed 416 case WifiStateMachine.WIFI_ENABLE_PENDING: 417 replyToMessage(message, WIFI_ENABLE_PROCEED); 418 break; 419 case WifiP2pManager.ENABLE_P2P: 420 replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED, 421 WifiP2pManager.P2P_UNSUPPORTED); 422 break; 423 case WifiP2pManager.DISABLE_P2P: 424 replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED, 425 WifiP2pManager.P2P_UNSUPPORTED); 426 break; 427 case WifiP2pManager.DISCOVER_PEERS: 428 replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, 429 WifiP2pManager.P2P_UNSUPPORTED); 430 break; 431 case WifiP2pManager.CONNECT: 432 replyToMessage(message, WifiP2pManager.CONNECT_FAILED, 433 WifiP2pManager.P2P_UNSUPPORTED); 434 break; 435 case WifiP2pManager.CANCEL_CONNECT: 436 replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED, 437 WifiP2pManager.P2P_UNSUPPORTED); 438 break; 439 case WifiP2pManager.CREATE_GROUP: 440 replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, 441 WifiP2pManager.P2P_UNSUPPORTED); 442 break; 443 case WifiP2pManager.REMOVE_GROUP: 444 replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED, 445 WifiP2pManager.P2P_UNSUPPORTED); 446 break; 447 default: 448 return NOT_HANDLED; 449 } 450 return HANDLED; 451 } 452 } 453 454 class P2pDisablingState extends State { 455 @Override 456 public void enter() { 457 if (DBG) logd(getName()); 458 logd("stopping supplicant"); 459 if (!WifiNative.stopSupplicant()) { 460 loge("Failed to stop supplicant, issue kill"); 461 WifiNative.killSupplicant(); 462 } 463 } 464 465 @Override 466 public boolean processMessage(Message message) { 467 if (DBG) logd(getName() + message.toString()); 468 switch (message.what) { 469 case WifiMonitor.SUP_DISCONNECTION_EVENT: 470 logd("Supplicant connection lost"); 471 WifiNative.closeSupplicantConnection(); 472 transitionTo(mP2pDisabledState); 473 break; 474 case WifiP2pManager.ENABLE_P2P: 475 case WifiP2pManager.DISABLE_P2P: 476 deferMessage(message); 477 break; 478 default: 479 return NOT_HANDLED; 480 } 481 return HANDLED; 482 } 483 } 484 485 486 class P2pDisabledState extends State { 487 @Override 488 public void enter() { 489 if (DBG) logd(getName()); 490 } 491 492 @Override 493 public boolean processMessage(Message message) { 494 if (DBG) logd(getName() + message.toString()); 495 switch (message.what) { 496 case WifiP2pManager.ENABLE_P2P: 497 OnClickListener listener = new OnClickListener() { 498 @Override 499 public void onClick(DialogInterface dialog, int which) { 500 if (which == DialogInterface.BUTTON_POSITIVE) { 501 sendMessage(WIFI_DISABLE_USER_ACCEPT); 502 } else { 503 sendMessage(WIFI_DISABLE_USER_REJECT); 504 } 505 } 506 }; 507 508 // Show a user request dialog if we know Wi-Fi client/hotspot is in operation 509 if (mWifiState != WifiManager.WIFI_STATE_DISABLED || 510 mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) { 511 Resources r = Resources.getSystem(); 512 AlertDialog dialog = new AlertDialog.Builder(mContext) 513 .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) 514 .setMessage(r.getString(R.string.wifi_p2p_turnon_message)) 515 .setPositiveButton(r.getString(R.string.ok), listener) 516 .setNegativeButton(r.getString(R.string.cancel), listener) 517 .create(); 518 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 519 dialog.show(); 520 transitionTo(mWaitForUserActionState); 521 } else { 522 mWifiChannel.sendMessage(P2P_ENABLE_PENDING); 523 transitionTo(mWaitForWifiDisableState); 524 } 525 replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED); 526 break; 527 case WifiP2pManager.DISABLE_P2P: 528 replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED); 529 break; 530 case WifiStateMachine.WIFI_ENABLE_PENDING: 531 replyToMessage(message, WIFI_ENABLE_PROCEED); 532 break; 533 default: 534 return NOT_HANDLED; 535 } 536 return HANDLED; 537 } 538 } 539 540 class WaitForUserActionState extends State { 541 @Override 542 public void enter() { 543 if (DBG) logd(getName()); 544 } 545 546 @Override 547 public boolean processMessage(Message message) { 548 if (DBG) logd(getName() + message.toString()); 549 switch (message.what) { 550 case WIFI_DISABLE_USER_ACCEPT: 551 mWifiChannel.sendMessage(P2P_ENABLE_PENDING); 552 transitionTo(mWaitForWifiDisableState); 553 break; 554 case WIFI_DISABLE_USER_REJECT: 555 logd("User rejected enabling p2p"); 556 sendP2pStateChangedBroadcast(false); 557 transitionTo(mP2pDisabledState); 558 break; 559 case WifiP2pManager.ENABLE_P2P: 560 case WifiP2pManager.DISABLE_P2P: 561 deferMessage(message); 562 break; 563 default: 564 return NOT_HANDLED; 565 } 566 return HANDLED; 567 } 568 } 569 570 class WaitForWifiDisableState extends State { 571 @Override 572 public void enter() { 573 if (DBG) logd(getName()); 574 } 575 576 @Override 577 public boolean processMessage(Message message) { 578 if (DBG) logd(getName() + message.toString()); 579 switch (message.what) { 580 case WifiStateMachine.P2P_ENABLE_PROCEED: 581 try { 582 mNwService.wifiFirmwareReload(mInterface, "P2P"); 583 } catch (Exception e) { 584 loge("Failed to reload p2p firmware " + e); 585 // continue 586 } 587 588 //A runtime crash can leave the interface up and 589 //this affects p2p when supplicant starts up. 590 //Ensure interface is down before a supplicant start. 591 try { 592 mNwService.setInterfaceDown(mInterface); 593 } catch (Exception e) { 594 if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e); 595 } 596 597 if (WifiNative.startP2pSupplicant()) { 598 mWifiMonitor.startMonitoring(); 599 transitionTo(mP2pEnablingState); 600 } else { 601 notifyP2pEnableFailure(); 602 transitionTo(mP2pDisabledState); 603 } 604 break; 605 case WifiP2pManager.ENABLE_P2P: 606 case WifiP2pManager.DISABLE_P2P: 607 deferMessage(message); 608 break; 609 default: 610 return NOT_HANDLED; 611 } 612 return HANDLED; 613 } 614 } 615 616 class P2pEnablingState extends State { 617 @Override 618 public void enter() { 619 if (DBG) logd(getName()); 620 } 621 622 @Override 623 public boolean processMessage(Message message) { 624 if (DBG) logd(getName() + message.toString()); 625 switch (message.what) { 626 case WifiMonitor.SUP_CONNECTION_EVENT: 627 logd("P2p start successful"); 628 transitionTo(mInactiveState); 629 break; 630 case WifiMonitor.SUP_DISCONNECTION_EVENT: 631 if (++mP2pRestartCount <= P2P_RESTART_TRIES) { 632 loge("Failed to start p2p, retry"); 633 WifiNative.killSupplicant(); 634 sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS); 635 } else { 636 loge("Failed " + mP2pRestartCount + " times to start p2p, quit "); 637 mP2pRestartCount = 0; 638 } 639 transitionTo(mP2pDisabledState); 640 break; 641 case WifiP2pManager.ENABLE_P2P: 642 case WifiP2pManager.DISABLE_P2P: 643 deferMessage(message); 644 break; 645 default: 646 return NOT_HANDLED; 647 } 648 return HANDLED; 649 } 650 } 651 652 class P2pEnabledState extends State { 653 @Override 654 public void enter() { 655 if (DBG) logd(getName()); 656 sendP2pStateChangedBroadcast(true); 657 mNetworkInfo.setIsAvailable(true); 658 initializeP2pSettings(); 659 showNotification(); 660 } 661 662 @Override 663 public boolean processMessage(Message message) { 664 if (DBG) logd(getName() + message.toString()); 665 switch (message.what) { 666 case WifiP2pManager.ENABLE_P2P: 667 replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED); 668 break; 669 case WifiP2pManager.DISABLE_P2P: 670 if (mPeers.clear()) sendP2pPeersChangedBroadcast(); 671 replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED); 672 transitionTo(mP2pDisablingState); 673 break; 674 case WifiP2pManager.DISCOVER_PEERS: 675 int timeout = message.arg1; 676 if (WifiNative.p2pFind(timeout)) { 677 replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED); 678 } else { 679 replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, 680 WifiP2pManager.ERROR); 681 } 682 break; 683 case WifiMonitor.P2P_DEVICE_FOUND_EVENT: 684 WifiP2pDevice device = (WifiP2pDevice) message.obj; 685 if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break; 686 mPeers.update(device); 687 sendP2pPeersChangedBroadcast(); 688 break; 689 case WifiMonitor.P2P_DEVICE_LOST_EVENT: 690 device = (WifiP2pDevice) message.obj; 691 if (mPeers.remove(device)) sendP2pPeersChangedBroadcast(); 692 break; 693 case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant died */ 694 loge("Connection lost, restart p2p"); 695 WifiNative.killSupplicant(); 696 WifiNative.closeSupplicantConnection(); 697 if (mPeers.clear()) sendP2pPeersChangedBroadcast(); 698 transitionTo(mP2pDisabledState); 699 sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS); 700 break; 701 default: 702 return NOT_HANDLED; 703 } 704 return HANDLED; 705 } 706 707 @Override 708 public void exit() { 709 sendP2pStateChangedBroadcast(false); 710 mNetworkInfo.setIsAvailable(false); 711 clearNotification(); 712 } 713 } 714 715 class InactiveState extends State { 716 @Override 717 public void enter() { 718 if (DBG) logd(getName()); 719 //Start listening every time we get inactive 720 WifiNative.p2pListen(); 721 } 722 723 @Override 724 public boolean processMessage(Message message) { 725 if (DBG) logd(getName() + message.toString()); 726 switch (message.what) { 727 case WifiP2pManager.CONNECT: 728 if (DBG) logd(getName() + " sending connect"); 729 mSavedPeerConfig = (WifiP2pConfig) message.obj; 730 String updatedPeerDetails = WifiNative.p2pPeer(mSavedPeerConfig.deviceAddress); 731 mPeers.update(new WifiP2pDevice(updatedPeerDetails)); 732 mPersistGroup = false; 733 int netId = configuredNetworkId(mSavedPeerConfig.deviceAddress); 734 if (netId >= 0) { 735 //TODO: if failure, remove config and do a regular p2pConnect() 736 WifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress); 737 } else { 738 //If peer is a GO, we do not need to send provisional discovery, 739 //the supplicant takes care of it. 740 if (isGroupOwner(mSavedPeerConfig.deviceAddress)) { 741 p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP); 742 transitionTo(mGroupNegotiationState); 743 } else { 744 transitionTo(mProvisionDiscoveryState); 745 } 746 } 747 updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED); 748 sendP2pPeersChangedBroadcast(); 749 replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED); 750 break; 751 case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT: 752 mSavedPeerConfig = (WifiP2pConfig) message.obj; 753 transitionTo(mUserAuthorizingInvitationState); 754 break; 755 case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT: 756 WifiP2pGroup group = (WifiP2pGroup) message.obj; 757 WifiP2pDevice owner = group.getOwner(); 758 759 if (owner == null) { 760 if (DBG) loge("Ignored invitation from null owner"); 761 break; 762 } 763 764 mSavedPeerConfig = new WifiP2pConfig(); 765 mSavedPeerConfig.deviceAddress = group.getOwner().deviceAddress; 766 767 //Check if we have the owner in peer list and use appropriate 768 //wps method. Default is to use PBC. 769 if ((owner = getDeviceFromPeerList(owner.deviceAddress)) != null) { 770 if (owner.wpsPbcSupported()) { 771 mSavedPeerConfig.wps.setup = WpsInfo.PBC; 772 } else if (owner.wpsKeypadSupported()) { 773 mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD; 774 } else if (owner.wpsDisplaySupported()) { 775 mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; 776 } 777 } 778 transitionTo(mUserAuthorizingInvitationState); 779 break; 780 case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: 781 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: 782 case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: 783 WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj; 784 mSavedPeerConfig = new WifiP2pConfig(); 785 mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress; 786 if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) { 787 mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD; 788 if (DBG) logd("Keypad prov disc request"); 789 } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) { 790 mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; 791 mSavedPeerConfig.wps.pin = provDisc.pin; 792 if (DBG) logd("Display prov disc request"); 793 } else { 794 mSavedPeerConfig.wps.setup = WpsInfo.PBC; 795 if (DBG) logd("PBC prov disc request"); 796 } 797 transitionTo(mUserAuthorizingInvitationState); 798 break; 799 case WifiP2pManager.CREATE_GROUP: 800 mPersistGroup = true; 801 if (WifiNative.p2pGroupAdd()) { 802 replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED); 803 } else { 804 replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, 805 WifiP2pManager.ERROR); 806 } 807 transitionTo(mGroupNegotiationState); 808 break; 809 default: 810 return NOT_HANDLED; 811 } 812 return HANDLED; 813 } 814 } 815 816 class GroupCreatingState extends State { 817 @Override 818 public void enter() { 819 if (DBG) logd(getName()); 820 sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT, 821 ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS); 822 } 823 824 @Override 825 public boolean processMessage(Message message) { 826 if (DBG) logd(getName() + message.toString()); 827 switch (message.what) { 828 case GROUP_CREATING_TIMED_OUT: 829 if (mGroupCreatingTimeoutIndex == message.arg1) { 830 if (DBG) logd("Group negotiation timed out"); 831 updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.FAILED); 832 mSavedPeerConfig = null; 833 sendP2pPeersChangedBroadcast(); 834 transitionTo(mInactiveState); 835 } 836 break; 837 case WifiP2pManager.DISCOVER_PEERS: 838 /* Discovery will break negotiation */ 839 replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, 840 WifiP2pManager.BUSY); 841 break; 842 case WifiP2pManager.CANCEL_CONNECT: 843 if (WifiNative.p2pCancelConnect()) { 844 replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED); 845 } else { 846 replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED, 847 WifiP2pManager.ERROR); 848 } 849 break; 850 default: 851 return NOT_HANDLED; 852 } 853 return HANDLED; 854 } 855 } 856 857 class UserAuthorizingInvitationState extends State { 858 @Override 859 public void enter() { 860 if (DBG) logd(getName()); 861 notifyInvitationReceived(); 862 } 863 864 @Override 865 public boolean processMessage(Message message) { 866 if (DBG) logd(getName() + message.toString()); 867 switch (message.what) { 868 case PEER_CONNECTION_USER_ACCEPT: 869 //TODO: handle persistence 870 if (isGroupOwner(mSavedPeerConfig.deviceAddress)) { 871 p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP); 872 } else { 873 p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP); 874 } 875 updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED); 876 sendP2pPeersChangedBroadcast(); 877 transitionTo(mGroupNegotiationState); 878 break; 879 case PEER_CONNECTION_USER_REJECT: 880 if (DBG) logd("User rejected invitation " + mSavedPeerConfig); 881 mSavedPeerConfig = null; 882 transitionTo(mInactiveState); 883 break; 884 default: 885 return NOT_HANDLED; 886 } 887 return HANDLED; 888 } 889 890 @Override 891 public void exit() { 892 //TODO: dismiss dialog if not already done 893 } 894 } 895 896 class ProvisionDiscoveryState extends State { 897 @Override 898 public void enter() { 899 if (DBG) logd(getName()); 900 WifiNative.p2pProvisionDiscovery(mSavedPeerConfig); 901 } 902 903 @Override 904 public boolean processMessage(Message message) { 905 if (DBG) logd(getName() + message.toString()); 906 WifiP2pProvDiscEvent provDisc; 907 WifiP2pDevice device; 908 switch (message.what) { 909 case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT: 910 provDisc = (WifiP2pProvDiscEvent) message.obj; 911 device = provDisc.device; 912 if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break; 913 914 if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) { 915 if (DBG) logd("Found a match " + mSavedPeerConfig); 916 WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); 917 transitionTo(mGroupNegotiationState); 918 } 919 break; 920 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: 921 provDisc = (WifiP2pProvDiscEvent) message.obj; 922 device = provDisc.device; 923 if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break; 924 925 if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) { 926 if (DBG) logd("Found a match " + mSavedPeerConfig); 927 /* we already have the pin */ 928 if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) { 929 WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); 930 transitionTo(mGroupNegotiationState); 931 } else { 932 transitionTo(mUserAuthorizingInvitationState); 933 } 934 } 935 break; 936 case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: 937 provDisc = (WifiP2pProvDiscEvent) message.obj; 938 device = provDisc.device; 939 if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break; 940 941 if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) { 942 if (DBG) logd("Found a match " + mSavedPeerConfig); 943 mSavedPeerConfig.wps.pin = provDisc.pin; 944 WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); 945 notifyInvitationSent(provDisc.pin, device.deviceAddress); 946 transitionTo(mGroupNegotiationState); 947 } 948 break; 949 default: 950 return NOT_HANDLED; 951 } 952 return HANDLED; 953 } 954 } 955 956 class GroupNegotiationState extends State { 957 @Override 958 public void enter() { 959 if (DBG) logd(getName()); 960 } 961 962 @Override 963 public boolean processMessage(Message message) { 964 if (DBG) logd(getName() + message.toString()); 965 switch (message.what) { 966 // We ignore these right now, since we get a GROUP_STARTED notification 967 // afterwards 968 case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT: 969 case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT: 970 if (DBG) logd(getName() + " go success"); 971 break; 972 case WifiMonitor.P2P_GROUP_STARTED_EVENT: 973 mGroup = (WifiP2pGroup) message.obj; 974 if (DBG) logd(getName() + " group started"); 975 if (mGroup.isGroupOwner()) { 976 startDhcpServer(mGroup.getInterface()); 977 } else { 978 mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext, 979 P2pStateMachine.this, mGroup.getInterface()); 980 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP); 981 WifiP2pDevice groupOwner = mGroup.getOwner(); 982 updateDeviceStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED); 983 sendP2pPeersChangedBroadcast(); 984 } 985 mSavedPeerConfig = null; 986 transitionTo(mGroupCreatedState); 987 break; 988 case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT: 989 case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT: 990 if (DBG) logd(getName() + " go failure"); 991 updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.FAILED); 992 mSavedPeerConfig = null; 993 sendP2pPeersChangedBroadcast(); 994 transitionTo(mInactiveState); 995 break; 996 default: 997 return NOT_HANDLED; 998 } 999 return HANDLED; 1000 } 1001 } 1002 1003 1004 1005 class GroupCreatedState extends State { 1006 @Override 1007 public void enter() { 1008 if (DBG) logd(getName()); 1009 mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); 1010 1011 updateThisDevice(WifiP2pDevice.CONNECTED); 1012 1013 //DHCP server has already been started if I am a group owner 1014 if (mGroup.isGroupOwner()) { 1015 setWifiP2pInfoOnGroupFormation(SERVER_ADDRESS); 1016 sendP2pConnectionChangedBroadcast(); 1017 } 1018 } 1019 1020 @Override 1021 public boolean processMessage(Message message) { 1022 if (DBG) logd(getName() + message.toString()); 1023 switch (message.what) { 1024 case WifiMonitor.AP_STA_CONNECTED_EVENT: 1025 WifiP2pDevice device = (WifiP2pDevice) message.obj; 1026 String deviceAddress = device.deviceAddress; 1027 if (deviceAddress != null) { 1028 mGroup.addClient(deviceAddress); 1029 mPeers.updateInterfaceAddress(device); 1030 updateDeviceStatus(deviceAddress, WifiP2pDevice.CONNECTED); 1031 if (DBG) logd(getName() + " ap sta connected"); 1032 sendP2pPeersChangedBroadcast(); 1033 } else { 1034 loge("Connect on null device address, ignore"); 1035 } 1036 break; 1037 case WifiMonitor.AP_STA_DISCONNECTED_EVENT: 1038 //TODO: the disconnection event is still inconsistent and reports 1039 //interface address. Fix this after wpa_supplicant is fixed. 1040 String interfaceAddress = (String) message.obj; 1041 deviceAddress = getDeviceAddress(interfaceAddress); 1042 if (deviceAddress != null) { 1043 updateDeviceStatus(deviceAddress, WifiP2pDevice.AVAILABLE); 1044 if (mGroup.removeClient(deviceAddress)) { 1045 if (DBG) logd("Removed client " + deviceAddress); 1046 if (!mPersistGroup && mGroup.isClientListEmpty()) { 1047 Slog.d(TAG, "Client list empty, remove non-persistent p2p group"); 1048 WifiNative.p2pGroupRemove(mGroup.getInterface()); 1049 } 1050 } else { 1051 if (DBG) logd("Failed to remove client " + deviceAddress); 1052 for (WifiP2pDevice c : mGroup.getClientList()) { 1053 if (DBG) logd("client " + c.deviceAddress); 1054 } 1055 } 1056 sendP2pPeersChangedBroadcast(); 1057 if (DBG) loge(getName() + " ap sta disconnected"); 1058 } else { 1059 loge("Disconnect on unknown interface address : " + interfaceAddress); 1060 } 1061 break; 1062 case DhcpStateMachine.CMD_POST_DHCP_ACTION: 1063 DhcpInfoInternal dhcpInfo = (DhcpInfoInternal) message.obj; 1064 if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS && 1065 dhcpInfo != null) { 1066 if (DBG) logd("DhcpInfo: " + dhcpInfo); 1067 setWifiP2pInfoOnGroupFormation(dhcpInfo.serverAddress); 1068 sendP2pConnectionChangedBroadcast(); 1069 } else { 1070 WifiNative.p2pGroupRemove(mGroup.getInterface()); 1071 } 1072 break; 1073 case WifiP2pManager.REMOVE_GROUP: 1074 if (DBG) loge(getName() + " remove group"); 1075 if (WifiNative.p2pGroupRemove(mGroup.getInterface())) { 1076 replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED); 1077 } else { 1078 replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED, 1079 WifiP2pManager.ERROR); 1080 } 1081 break; 1082 case WifiMonitor.P2P_GROUP_REMOVED_EVENT: 1083 if (DBG) loge(getName() + " group removed"); 1084 Collection <WifiP2pDevice> devices = mGroup.getClientList(); 1085 boolean changed = false; 1086 for (WifiP2pDevice d : mPeers.getDeviceList()) { 1087 if (devices.contains(d) || mGroup.getOwner().equals(d)) { 1088 d.status = WifiP2pDevice.AVAILABLE; 1089 changed = true; 1090 } 1091 } 1092 1093 if (mGroup.isGroupOwner()) { 1094 stopDhcpServer(); 1095 } else { 1096 if (DBG) logd("stop DHCP client"); 1097 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP); 1098 mDhcpStateMachine.quit(); 1099 mDhcpStateMachine = null; 1100 } 1101 1102 mGroup = null; 1103 if (changed) sendP2pPeersChangedBroadcast(); 1104 transitionTo(mInactiveState); 1105 break; 1106 case WifiMonitor.P2P_DEVICE_LOST_EVENT: 1107 device = (WifiP2pDevice) message.obj; 1108 //Device loss for a connected device indicates it is not in discovery any more 1109 if (mGroup.contains(device)) { 1110 if (DBG) logd("Lost " + device +" , do nothing"); 1111 return HANDLED; 1112 } 1113 // Do the regular device lost handling 1114 return NOT_HANDLED; 1115 case WifiP2pManager.DISABLE_P2P: 1116 sendMessage(WifiP2pManager.REMOVE_GROUP); 1117 deferMessage(message); 1118 break; 1119 case WifiP2pManager.CONNECT: 1120 WifiP2pConfig config = (WifiP2pConfig) message.obj; 1121 logd("Inviting device : " + config.deviceAddress); 1122 if (WifiNative.p2pInvite(mGroup, config.deviceAddress)) { 1123 updateDeviceStatus(config.deviceAddress, WifiP2pDevice.INVITED); 1124 sendP2pPeersChangedBroadcast(); 1125 replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED); 1126 } else { 1127 replyToMessage(message, WifiP2pManager.CONNECT_FAILED, 1128 WifiP2pManager.ERROR); 1129 } 1130 // TODO: figure out updating the status to declined when invitation is rejected 1131 break; 1132 case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: 1133 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: 1134 case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: 1135 WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj; 1136 mSavedPeerConfig = new WifiP2pConfig(); 1137 mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress; 1138 if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) { 1139 mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD; 1140 } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) { 1141 mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; 1142 mSavedPeerConfig.wps.pin = provDisc.pin; 1143 } else { 1144 mSavedPeerConfig.wps.setup = WpsInfo.PBC; 1145 } 1146 transitionTo(mUserAuthorizingJoinState); 1147 break; 1148 case WifiMonitor.P2P_GROUP_STARTED_EVENT: 1149 Slog.e(TAG, "Duplicate group creation event notice, ignore"); 1150 break; 1151 default: 1152 return NOT_HANDLED; 1153 } 1154 return HANDLED; 1155 } 1156 1157 public void exit() { 1158 updateThisDevice(WifiP2pDevice.AVAILABLE); 1159 setWifiP2pInfoOnGroupTermination(); 1160 mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null); 1161 sendP2pConnectionChangedBroadcast(); 1162 } 1163 } 1164 1165 class UserAuthorizingJoinState extends State { 1166 @Override 1167 public void enter() { 1168 if (DBG) logd(getName()); 1169 notifyInvitationReceived(); 1170 } 1171 1172 @Override 1173 public boolean processMessage(Message message) { 1174 if (DBG) logd(getName() + message.toString()); 1175 switch (message.what) { 1176 case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: 1177 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: 1178 case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: 1179 //Ignore more client requests 1180 break; 1181 case PEER_CONNECTION_USER_ACCEPT: 1182 if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) { 1183 WifiNative.startWpsPbc(); 1184 } else { 1185 WifiNative.startWpsPinKeypad(mSavedPeerConfig.wps.pin); 1186 } 1187 mSavedPeerConfig = null; 1188 transitionTo(mGroupCreatedState); 1189 break; 1190 case PEER_CONNECTION_USER_REJECT: 1191 if (DBG) logd("User rejected incoming request"); 1192 mSavedPeerConfig = null; 1193 transitionTo(mGroupCreatedState); 1194 break; 1195 default: 1196 return NOT_HANDLED; 1197 } 1198 return HANDLED; 1199 } 1200 1201 @Override 1202 public void exit() { 1203 //TODO: dismiss dialog if not already done 1204 } 1205 } 1206 1207 private void sendP2pStateChangedBroadcast(boolean enabled) { 1208 final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); 1209 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1210 if (enabled) { 1211 intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE, 1212 WifiP2pManager.WIFI_P2P_STATE_ENABLED); 1213 } else { 1214 intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE, 1215 WifiP2pManager.WIFI_P2P_STATE_DISABLED); 1216 } 1217 mContext.sendStickyBroadcast(intent); 1218 } 1219 1220 private void sendThisDeviceChangedBroadcast() { 1221 final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); 1222 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1223 intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice)); 1224 mContext.sendStickyBroadcast(intent); 1225 } 1226 1227 private void sendP2pPeersChangedBroadcast() { 1228 final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); 1229 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1230 mContext.sendBroadcast(intent); 1231 } 1232 1233 private void sendP2pConnectionChangedBroadcast() { 1234 if (DBG) logd("sending p2p connection changed broadcast"); 1235 Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); 1236 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1237 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 1238 intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo)); 1239 intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo)); 1240 mContext.sendStickyBroadcast(intent); 1241 } 1242 1243 private void startDhcpServer(String intf) { 1244 InterfaceConfiguration ifcg = null; 1245 try { 1246 ifcg = mNwService.getInterfaceConfig(intf); 1247 ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress( 1248 SERVER_ADDRESS), 24)); 1249 ifcg.setInterfaceUp(); 1250 mNwService.setInterfaceConfig(intf, ifcg); 1251 /* This starts the dnsmasq server */ 1252 mNwService.startTethering(DHCP_RANGE); 1253 } catch (Exception e) { 1254 loge("Error configuring interface " + intf + ", :" + e); 1255 return; 1256 } 1257 1258 logd("Started Dhcp server on " + intf); 1259 } 1260 1261 private void stopDhcpServer() { 1262 try { 1263 mNwService.stopTethering(); 1264 } catch (Exception e) { 1265 loge("Error stopping Dhcp server" + e); 1266 return; 1267 } 1268 1269 logd("Stopped Dhcp server"); 1270 } 1271 1272 private void notifyP2pEnableFailure() { 1273 Resources r = Resources.getSystem(); 1274 AlertDialog dialog = new AlertDialog.Builder(mContext) 1275 .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) 1276 .setMessage(r.getString(R.string.wifi_p2p_failed_message)) 1277 .setPositiveButton(r.getString(R.string.ok), null) 1278 .create(); 1279 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1280 dialog.show(); 1281 } 1282 1283 private void addRowToDialog(ViewGroup group, int stringId, String value) { 1284 Resources r = Resources.getSystem(); 1285 View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row, 1286 group, false); 1287 ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId)); 1288 ((TextView) row.findViewById(R.id.value)).setText(value); 1289 group.addView(row); 1290 } 1291 1292 private void notifyInvitationSent(String pin, String peerAddress) { 1293 Resources r = Resources.getSystem(); 1294 1295 final View textEntryView = LayoutInflater.from(mContext) 1296 .inflate(R.layout.wifi_p2p_dialog, null); 1297 1298 ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info); 1299 addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress)); 1300 addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin); 1301 1302 AlertDialog dialog = new AlertDialog.Builder(mContext) 1303 .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title)) 1304 .setView(textEntryView) 1305 .setPositiveButton(r.getString(R.string.ok), null) 1306 .create(); 1307 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1308 dialog.show(); 1309 } 1310 1311 private void notifyInvitationReceived() { 1312 Resources r = Resources.getSystem(); 1313 final WpsInfo wps = mSavedPeerConfig.wps; 1314 final View textEntryView = LayoutInflater.from(mContext) 1315 .inflate(R.layout.wifi_p2p_dialog, null); 1316 1317 ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info); 1318 addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName( 1319 mSavedPeerConfig.deviceAddress)); 1320 1321 final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin); 1322 1323 AlertDialog dialog = new AlertDialog.Builder(mContext) 1324 .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title)) 1325 .setView(textEntryView) 1326 .setPositiveButton(r.getString(R.string.accept), new OnClickListener() { 1327 public void onClick(DialogInterface dialog, int which) { 1328 if (wps.setup == WpsInfo.KEYPAD) { 1329 mSavedPeerConfig.wps.pin = pin.getText().toString(); 1330 } 1331 if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig); 1332 sendMessage(PEER_CONNECTION_USER_ACCEPT); 1333 } 1334 }) 1335 .setNegativeButton(r.getString(R.string.decline), new OnClickListener() { 1336 @Override 1337 public void onClick(DialogInterface dialog, int which) { 1338 if (DBG) logd(getName() + " ignore connect"); 1339 sendMessage(PEER_CONNECTION_USER_REJECT); 1340 } 1341 }) 1342 .create(); 1343 1344 //make the enter pin area or the display pin area visible 1345 switch (wps.setup) { 1346 case WpsInfo.KEYPAD: 1347 if (DBG) logd("Enter pin section visible"); 1348 textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE); 1349 break; 1350 case WpsInfo.DISPLAY: 1351 if (DBG) logd("Shown pin section visible"); 1352 addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin); 1353 break; 1354 default: 1355 break; 1356 } 1357 1358 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1359 dialog.show(); 1360 } 1361 1362 private void updateDeviceStatus(String deviceAddress, int status) { 1363 for (WifiP2pDevice d : mPeers.getDeviceList()) { 1364 if (d.deviceAddress.equals(deviceAddress)) { 1365 d.status = status; 1366 } 1367 } 1368 } 1369 1370 private boolean isGroupOwner(String deviceAddress) { 1371 for (WifiP2pDevice d : mPeers.getDeviceList()) { 1372 if (d.deviceAddress.equals(deviceAddress)) { 1373 return d.isGroupOwner(); 1374 } 1375 } 1376 return false; 1377 } 1378 1379 //TODO: implement when wpa_supplicant is fixed 1380 private int configuredNetworkId(String deviceAddress) { 1381 return -1; 1382 } 1383 1384 private void setWifiP2pInfoOnGroupFormation(String serverAddress) { 1385 mWifiP2pInfo.groupFormed = true; 1386 mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner(); 1387 mWifiP2pInfo.groupOwnerAddress = NetworkUtils.numericToInetAddress(serverAddress); 1388 } 1389 1390 private void setWifiP2pInfoOnGroupTermination() { 1391 mWifiP2pInfo.groupFormed = false; 1392 mWifiP2pInfo.isGroupOwner = false; 1393 mWifiP2pInfo.groupOwnerAddress = null; 1394 } 1395 1396 private String getDeviceName(String deviceAddress) { 1397 for (WifiP2pDevice d : mPeers.getDeviceList()) { 1398 if (d.deviceAddress.equals(deviceAddress)) { 1399 return d.deviceName; 1400 } 1401 } 1402 //Treat the address as name if there is no match 1403 return deviceAddress; 1404 } 1405 1406 private String getDeviceAddress(String interfaceAddress) { 1407 for (WifiP2pDevice d : mPeers.getDeviceList()) { 1408 if (interfaceAddress.equals(d.interfaceAddress)) { 1409 return d.deviceAddress; 1410 } 1411 } 1412 return null; 1413 } 1414 1415 private WifiP2pDevice getDeviceFromPeerList(String deviceAddress) { 1416 for (WifiP2pDevice d : mPeers.getDeviceList()) { 1417 if (d.deviceAddress.equals(deviceAddress)) { 1418 return d; 1419 } 1420 } 1421 return null; 1422 } 1423 1424 private void p2pConnectWithPinDisplay(WifiP2pConfig config, boolean join) { 1425 String pin = WifiNative.p2pConnect(config, join); 1426 try { 1427 Integer.parseInt(pin); 1428 notifyInvitationSent(pin, config.deviceAddress); 1429 } catch (NumberFormatException ignore) { 1430 // do nothing if p2pConnect did not return a pin 1431 } 1432 } 1433 1434 private void initializeP2pSettings() { 1435 WifiNative.setPersistentReconnect(true); 1436 WifiNative.setDeviceName(mThisDevice.deviceName); 1437 //DIRECT-XY-DEVICENAME (XY is randomly generated) 1438 WifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName); 1439 WifiNative.setDeviceType(mThisDevice.primaryDeviceType); 1440 //The supplicant default is to support everything, but a bug necessitates 1441 //the framework to specify this explicitly 1442 WifiNative.setConfigMethods("keypad display push_button"); 1443 1444 mThisDevice.deviceAddress = WifiNative.p2pGetDeviceAddress(); 1445 updateThisDevice(WifiP2pDevice.AVAILABLE); 1446 if (DBG) Slog.d(TAG, "DeviceAddress: " + mThisDevice.deviceAddress); 1447 } 1448 1449 private void updateThisDevice(int status) { 1450 mThisDevice.status = status; 1451 sendThisDeviceChangedBroadcast(); 1452 } 1453 1454 //State machine initiated requests can have replyTo set to null indicating 1455 //there are no recepients, we ignore those reply actions 1456 private void replyToMessage(Message msg, int what) { 1457 if (msg.replyTo == null) return; 1458 Message dstMsg = obtainMessage(msg); 1459 dstMsg.what = what; 1460 mReplyChannel.replyToMessage(msg, dstMsg); 1461 } 1462 1463 private void replyToMessage(Message msg, int what, int arg1) { 1464 if (msg.replyTo == null) return; 1465 Message dstMsg = obtainMessage(msg); 1466 dstMsg.what = what; 1467 dstMsg.arg1 = arg1; 1468 mReplyChannel.replyToMessage(msg, dstMsg); 1469 } 1470 1471 private void replyToMessage(Message msg, int what, Object obj) { 1472 if (msg.replyTo == null) return; 1473 Message dstMsg = obtainMessage(msg); 1474 dstMsg.what = what; 1475 dstMsg.obj = obj; 1476 mReplyChannel.replyToMessage(msg, dstMsg); 1477 } 1478 1479 /* arg2 on the source message has a hash code that needs to be retained in replies 1480 * see WifiP2pManager for details */ 1481 private Message obtainMessage(Message srcMsg) { 1482 Message msg = Message.obtain(); 1483 msg.arg2 = srcMsg.arg2; 1484 return msg; 1485 } 1486 1487 private void logd(String s) { 1488 Slog.d(TAG, s); 1489 } 1490 1491 private void loge(String s) { 1492 Slog.e(TAG, s); 1493 } 1494 1495 private void showNotification() { 1496 NotificationManager notificationManager = 1497 (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); 1498 if (notificationManager == null || mNotification != null) { 1499 return; 1500 } 1501 1502 Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS); 1503 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 1504 1505 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 1506 1507 Resources r = Resources.getSystem(); 1508 CharSequence title = r.getText(R.string.wifi_p2p_enabled_notification_title); 1509 CharSequence message = r.getText(R.string.wifi_p2p_enabled_notification_message); 1510 1511 mNotification = new Notification(); 1512 mNotification.when = 0; 1513 //TODO: might change to be a seperate icon 1514 mNotification.icon = R.drawable.stat_sys_tether_wifi; 1515 mNotification.defaults &= ~Notification.DEFAULT_SOUND; 1516 mNotification.flags = Notification.FLAG_ONGOING_EVENT; 1517 mNotification.tickerText = title; 1518 mNotification.setLatestEventInfo(mContext, title, message, pi); 1519 1520 notificationManager.notify(mNotification.icon, mNotification); 1521 } 1522 1523 private void clearNotification() { 1524 NotificationManager notificationManager = 1525 (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); 1526 if (notificationManager != null && mNotification != null) { 1527 notificationManager.cancel(mNotification.icon); 1528 mNotification = null; 1529 } 1530 } 1531 1532 private boolean isAirplaneSensitive() { 1533 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(), 1534 Settings.System.AIRPLANE_MODE_RADIOS); 1535 return airplaneModeRadios == null 1536 || airplaneModeRadios.contains(Settings.System.RADIO_WIFI); 1537 } 1538 1539 private boolean isAirplaneModeOn() { 1540 return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(), 1541 Settings.System.AIRPLANE_MODE_ON, 0) == 1; 1542 } 1543 1544 } 1545} 1546