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