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