WifiService.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server; 18 19import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; 20import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; 21import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; 22import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; 23import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; 24 25import android.app.ActivityManagerNative; 26import android.app.AlarmManager; 27import android.app.PendingIntent; 28import android.content.BroadcastReceiver; 29import android.content.ContentResolver; 30import android.content.Context; 31import android.content.Intent; 32import android.content.IntentFilter; 33import android.content.pm.PackageManager; 34import android.net.wifi.IWifiManager; 35import android.net.wifi.WifiInfo; 36import android.net.wifi.WifiManager; 37import android.net.wifi.WifiNative; 38import android.net.wifi.WifiStateTracker; 39import android.net.wifi.ScanResult; 40import android.net.wifi.WifiConfiguration; 41import android.net.NetworkStateTracker; 42import android.net.DhcpInfo; 43import android.os.Binder; 44import android.os.Handler; 45import android.os.HandlerThread; 46import android.os.IBinder; 47import android.os.Looper; 48import android.os.Message; 49import android.os.PowerManager; 50import android.os.RemoteException; 51import android.provider.Settings; 52import android.util.Log; 53import android.text.TextUtils; 54 55import java.util.ArrayList; 56import java.util.BitSet; 57import java.util.HashMap; 58import java.util.LinkedHashMap; 59import java.util.List; 60import java.util.Map; 61import java.util.regex.Pattern; 62import java.io.FileDescriptor; 63import java.io.PrintWriter; 64 65/** 66 * WifiService handles remote WiFi operation requests by implementing 67 * the IWifiManager interface. It also creates a WifiMonitor to listen 68 * for Wifi-related events. 69 * 70 * @hide 71 */ 72public class WifiService extends IWifiManager.Stub { 73 private static final String TAG = "WifiService"; 74 private static final boolean DBG = false; 75 private static final Pattern scanResultPattern = Pattern.compile("\t+"); 76 private final WifiStateTracker mWifiStateTracker; 77 78 private Context mContext; 79 private int mWifiState; 80 81 private AlarmManager mAlarmManager; 82 private PendingIntent mIdleIntent; 83 private static final int IDLE_REQUEST = 0; 84 private boolean mScreenOff; 85 private boolean mDeviceIdle; 86 private int mPluggedType; 87 88 private final LockList mLocks = new LockList(); 89 /** 90 * See {@link Settings.Gservices#WIFI_IDLE_MS}. This is the default value if a 91 * Settings.Gservices value is not present. This timeout value is chosen as 92 * the approximate point at which the battery drain caused by Wi-Fi 93 * being enabled but not active exceeds the battery drain caused by 94 * re-establishing a connection to the mobile data network. 95 */ 96 private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */ 97 98 private static final String WAKELOCK_TAG = "WifiService"; 99 100 /** 101 * The maximum amount of time to hold the wake lock after a disconnect 102 * caused by stopping the driver. Establishing an EDGE connection has been 103 * observed to take about 5 seconds under normal circumstances. This 104 * provides a bit of extra margin. 105 * <p> 106 * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}. 107 * This is the default value if a Settings.Secure value is not present. 108 */ 109 private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000; 110 111 // Wake lock used by driver-stop operation 112 private static PowerManager.WakeLock sDriverStopWakeLock; 113 // Wake lock used by other operations 114 private static PowerManager.WakeLock sWakeLock; 115 116 private static final int MESSAGE_ENABLE_WIFI = 0; 117 private static final int MESSAGE_DISABLE_WIFI = 1; 118 private static final int MESSAGE_STOP_WIFI = 2; 119 private static final int MESSAGE_START_WIFI = 3; 120 private static final int MESSAGE_RELEASE_WAKELOCK = 4; 121 122 private final WifiHandler mWifiHandler; 123 124 /* 125 * Map used to keep track of hidden networks presence, which 126 * is needed to switch between active and passive scan modes. 127 * If there is at least one hidden network that is currently 128 * present (enabled), we want to do active scans instead of 129 * passive. 130 */ 131 private final Map<Integer, Boolean> mIsHiddenNetworkPresent; 132 /* 133 * The number of currently present hidden networks. When this 134 * counter goes from 0 to 1 or from 1 to 0, we change the 135 * scan mode to active or passive respectively. Initially, we 136 * set the counter to 0 and we increment it every time we add 137 * a new present (enabled) hidden network. 138 */ 139 private int mNumHiddenNetworkPresent; 140 /* 141 * Whether we change the scan mode is due to a hidden network 142 * (in this class, this is always the case) 143 */ 144 private final static boolean SET_DUE_TO_A_HIDDEN_NETWORK = true; 145 146 /* 147 * Cache of scan results objects (size is somewhat arbitrary) 148 */ 149 private static final int SCAN_RESULT_CACHE_SIZE = 80; 150 private final LinkedHashMap<String, ScanResult> mScanResultCache; 151 152 /* 153 * Character buffer used to parse scan results (optimization) 154 */ 155 private static final int SCAN_RESULT_BUFFER_SIZE = 512; 156 private char[] mScanResultBuffer; 157 private boolean mNeedReconfig; 158 159 /** 160 * Number of allowed radio frequency channels in various regulatory domains. 161 * This list is sufficient for 802.11b/g networks (2.4GHz range). 162 */ 163 private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14}; 164 165 private static final String ACTION_DEVICE_IDLE = 166 "com.android.server.WifiManager.action.DEVICE_IDLE"; 167 168 WifiService(Context context, WifiStateTracker tracker) { 169 mContext = context; 170 mWifiStateTracker = tracker; 171 172 /* 173 * Initialize the hidden-networks state 174 */ 175 mIsHiddenNetworkPresent = new HashMap<Integer, Boolean>(); 176 mNumHiddenNetworkPresent = 0; 177 178 mScanResultCache = new LinkedHashMap<String, ScanResult>( 179 SCAN_RESULT_CACHE_SIZE, 0.75f, true) { 180 /* 181 * Limit the cache size by SCAN_RESULT_CACHE_SIZE 182 * elements 183 */ 184 public boolean removeEldestEntry(Map.Entry eldest) { 185 return SCAN_RESULT_CACHE_SIZE < this.size(); 186 } 187 }; 188 189 mScanResultBuffer = new char [SCAN_RESULT_BUFFER_SIZE]; 190 191 HandlerThread wifiThread = new HandlerThread("WifiService"); 192 wifiThread.start(); 193 mWifiHandler = new WifiHandler(wifiThread.getLooper()); 194 195 mWifiState = WIFI_STATE_DISABLED; 196 boolean wifiEnabled = getPersistedWifiEnabled(); 197 198 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 199 Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); 200 mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0); 201 202 PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); 203 sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); 204 sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); 205 mWifiStateTracker.setReleaseWakeLockCallback( 206 new Runnable() { 207 public void run() { 208 mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK); 209 synchronized (sDriverStopWakeLock) { 210 if (sDriverStopWakeLock.isHeld()) { 211 sDriverStopWakeLock.release(); 212 } 213 } 214 } 215 } 216 ); 217 218 Log.i(TAG, "WifiService starting up with Wi-Fi " + 219 (wifiEnabled ? "enabled" : "disabled")); 220 221 mContext.registerReceiver( 222 new BroadcastReceiver() { 223 @Override 224 public void onReceive(Context context, Intent intent) { 225 updateWifiState(); 226 } 227 }, 228 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); 229 230 setWifiEnabledBlocking(wifiEnabled, false); 231 } 232 233 /** 234 * Initializes the hidden networks state. Must be called when we 235 * enable Wi-Fi. 236 */ 237 private synchronized void initializeHiddenNetworksState() { 238 // First, reset the state 239 resetHiddenNetworksState(); 240 241 // ... then add networks that are marked as hidden 242 List<WifiConfiguration> networks = getConfiguredNetworks(); 243 if (!networks.isEmpty()) { 244 for (WifiConfiguration config : networks) { 245 if (config != null && config.hiddenSSID) { 246 addOrUpdateHiddenNetwork( 247 config.networkId, 248 config.status != WifiConfiguration.Status.DISABLED); 249 } 250 } 251 252 } 253 } 254 255 /** 256 * Resets the hidden networks state. 257 */ 258 private synchronized void resetHiddenNetworksState() { 259 mNumHiddenNetworkPresent = 0; 260 mIsHiddenNetworkPresent.clear(); 261 } 262 263 /** 264 * Marks all but netId network as not present. 265 */ 266 private synchronized void markAllHiddenNetworksButOneAsNotPresent(int netId) { 267 for (Map.Entry<Integer, Boolean> entry : mIsHiddenNetworkPresent.entrySet()) { 268 if (entry != null) { 269 Integer networkId = entry.getKey(); 270 if (networkId != netId) { 271 updateNetworkIfHidden( 272 networkId, false); 273 } 274 } 275 } 276 } 277 278 /** 279 * Updates the netId network presence status if netId is an existing 280 * hidden network. 281 */ 282 private synchronized void updateNetworkIfHidden(int netId, boolean present) { 283 if (isHiddenNetwork(netId)) { 284 addOrUpdateHiddenNetwork(netId, present); 285 } 286 } 287 288 /** 289 * Updates the netId network presence status if netId is an existing 290 * hidden network. If the network does not exist, adds the network. 291 */ 292 private synchronized void addOrUpdateHiddenNetwork(int netId, boolean present) { 293 if (0 <= netId) { 294 295 // If we are adding a new entry or modifying an existing one 296 Boolean isPresent = mIsHiddenNetworkPresent.get(netId); 297 if (isPresent == null || isPresent != present) { 298 if (present) { 299 incrementHiddentNetworkPresentCounter(); 300 } else { 301 // If we add a new hidden network, no need to change 302 // the counter (it must be 0) 303 if (isPresent != null) { 304 decrementHiddentNetworkPresentCounter(); 305 } 306 } 307 mIsHiddenNetworkPresent.put(netId, present); 308 } 309 } else { 310 Log.e(TAG, "addOrUpdateHiddenNetwork(): Invalid (negative) network id!"); 311 } 312 } 313 314 /** 315 * Removes the netId network if it is hidden (being kept track of). 316 */ 317 private synchronized void removeNetworkIfHidden(int netId) { 318 if (isHiddenNetwork(netId)) { 319 removeHiddenNetwork(netId); 320 } 321 } 322 323 /** 324 * Removes the netId network. For the call to be successful, the network 325 * must be hidden. 326 */ 327 private synchronized void removeHiddenNetwork(int netId) { 328 if (0 <= netId) { 329 Boolean isPresent = 330 mIsHiddenNetworkPresent.remove(netId); 331 if (isPresent != null) { 332 // If we remove an existing hidden network that is not 333 // present, no need to change the counter 334 if (isPresent) { 335 decrementHiddentNetworkPresentCounter(); 336 } 337 } else { 338 if (DBG) { 339 Log.d(TAG, "removeHiddenNetwork(): Removing a non-existent network!"); 340 } 341 } 342 } else { 343 Log.e(TAG, "removeHiddenNetwork(): Invalid (negative) network id!"); 344 } 345 } 346 347 /** 348 * Returns true if netId is an existing hidden network. 349 */ 350 private synchronized boolean isHiddenNetwork(int netId) { 351 return mIsHiddenNetworkPresent.containsKey(netId); 352 } 353 354 /** 355 * Increments the present (enabled) hidden networks counter. If the 356 * counter value goes from 0 to 1, changes the scan mode to active. 357 */ 358 private void incrementHiddentNetworkPresentCounter() { 359 ++mNumHiddenNetworkPresent; 360 if (1 == mNumHiddenNetworkPresent) { 361 // Switch the scan mode to "active" 362 mWifiStateTracker.setScanMode(true, SET_DUE_TO_A_HIDDEN_NETWORK); 363 } 364 } 365 366 /** 367 * Decrements the present (enabled) hidden networks counter. If the 368 * counter goes from 1 to 0, changes the scan mode back to passive. 369 */ 370 private void decrementHiddentNetworkPresentCounter() { 371 if (0 < mNumHiddenNetworkPresent) { 372 --mNumHiddenNetworkPresent; 373 if (0 == mNumHiddenNetworkPresent) { 374 // Switch the scan mode to "passive" 375 mWifiStateTracker.setScanMode(false, SET_DUE_TO_A_HIDDEN_NETWORK); 376 } 377 } else { 378 Log.e(TAG, "Hidden-network counter invariant violation!"); 379 } 380 } 381 382 private boolean getPersistedWifiEnabled() { 383 final ContentResolver cr = mContext.getContentResolver(); 384 try { 385 return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1; 386 } catch (Settings.SettingNotFoundException e) { 387 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0); 388 return false; 389 } 390 } 391 392 private void persistWifiEnabled(boolean enabled) { 393 final ContentResolver cr = mContext.getContentResolver(); 394 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0); 395 } 396 397 NetworkStateTracker getNetworkStateTracker() { 398 return mWifiStateTracker; 399 } 400 401 /** 402 * see {@link android.net.wifi.WifiManager#pingSupplicant()} 403 * @return {@code true} if the operation succeeds 404 */ 405 public boolean pingSupplicant() { 406 enforceChangePermission(); 407 synchronized (mWifiStateTracker) { 408 return WifiNative.pingCommand(); 409 } 410 } 411 412 /** 413 * see {@link android.net.wifi.WifiManager#startScan()} 414 * @return {@code true} if the operation succeeds 415 */ 416 public boolean startScan() { 417 enforceChangePermission(); 418 synchronized (mWifiStateTracker) { 419 switch (mWifiStateTracker.getSupplicantState()) { 420 case DISCONNECTED: 421 case INACTIVE: 422 case SCANNING: 423 case DORMANT: 424 break; 425 default: 426 WifiNative.setScanResultHandlingCommand( 427 WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY); 428 break; 429 } 430 return WifiNative.scanCommand(); 431 } 432 } 433 434 /** 435 * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} 436 * @param enable {@code true} to enable, {@code false} to disable. 437 * @return {@code true} if the enable/disable operation was 438 * started or is already in the queue. 439 */ 440 public boolean setWifiEnabled(boolean enable) { 441 enforceChangePermission(); 442 if (mWifiHandler == null) return false; 443 444 synchronized (mWifiHandler) { 445 sWakeLock.acquire(); 446 sendEnableMessage(enable, true); 447 } 448 449 return true; 450 } 451 452 /** 453 * Enables/disables Wi-Fi synchronously. 454 * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off. 455 * @param persist {@code true} if the setting should be persisted. 456 * @return {@code true} if the operation succeeds (or if the existing state 457 * is the same as the requested state) 458 */ 459 private boolean setWifiEnabledBlocking(boolean enable, boolean persist) { 460 final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED; 461 462 if (mWifiState == eventualWifiState) { 463 return true; 464 } 465 if (enable && isAirplaneModeOn()) { 466 return false; 467 } 468 469 setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING); 470 471 if (enable) { 472 if (!WifiNative.loadDriver()) { 473 Log.e(TAG, "Failed to load Wi-Fi driver."); 474 setWifiEnabledState(WIFI_STATE_UNKNOWN); 475 return false; 476 } 477 if (!WifiNative.startSupplicant()) { 478 WifiNative.unloadDriver(); 479 Log.e(TAG, "Failed to start supplicant daemon."); 480 setWifiEnabledState(WIFI_STATE_UNKNOWN); 481 return false; 482 } 483 registerForBroadcasts(); 484 mWifiStateTracker.startEventLoop(); 485 } else { 486 487 mContext.unregisterReceiver(mReceiver); 488 // Remove notification (it will no-op if it isn't visible) 489 mWifiStateTracker.setNotificationVisible(false, 0, false, 0); 490 491 boolean failedToStopSupplicantOrUnloadDriver = false; 492 if (!WifiNative.stopSupplicant()) { 493 Log.e(TAG, "Failed to stop supplicant daemon."); 494 setWifiEnabledState(WIFI_STATE_UNKNOWN); 495 failedToStopSupplicantOrUnloadDriver = true; 496 } 497 498 // We must reset the interface before we unload the driver 499 mWifiStateTracker.resetInterface(); 500 501 if (!WifiNative.unloadDriver()) { 502 Log.e(TAG, "Failed to unload Wi-Fi driver."); 503 if (!failedToStopSupplicantOrUnloadDriver) { 504 setWifiEnabledState(WIFI_STATE_UNKNOWN); 505 failedToStopSupplicantOrUnloadDriver = true; 506 } 507 } 508 if (failedToStopSupplicantOrUnloadDriver) { 509 return false; 510 } 511 } 512 513 // Success! 514 515 if (persist) { 516 persistWifiEnabled(enable); 517 } 518 setWifiEnabledState(eventualWifiState); 519 520 /* 521 * Initialize the hidden networks state and the number of allowed 522 * radio channels if Wi-Fi is being turned on. 523 */ 524 if (enable) { 525 mWifiStateTracker.setNumAllowedChannels(); 526 initializeHiddenNetworksState(); 527 } 528 529 return true; 530 } 531 532 private void setWifiEnabledState(int wifiState) { 533 final int previousWifiState = mWifiState; 534 535 // Update state 536 mWifiState = wifiState; 537 538 // Broadcast 539 final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); 540 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 541 intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState); 542 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState); 543 mContext.sendStickyBroadcast(intent); 544 } 545 546 private void enforceAccessPermission() { 547 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, 548 "WifiService"); 549 } 550 551 private void enforceChangePermission() { 552 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, 553 "WifiService"); 554 555 } 556 557 /** 558 * see {@link WifiManager#getWifiState()} 559 * @return One of {@link WifiManager#WIFI_STATE_DISABLED}, 560 * {@link WifiManager#WIFI_STATE_DISABLING}, 561 * {@link WifiManager#WIFI_STATE_ENABLED}, 562 * {@link WifiManager#WIFI_STATE_ENABLING}, 563 * {@link WifiManager#WIFI_STATE_UNKNOWN} 564 */ 565 public int getWifiEnabledState() { 566 enforceAccessPermission(); 567 return mWifiState; 568 } 569 570 /** 571 * see {@link android.net.wifi.WifiManager#disconnect()} 572 * @return {@code true} if the operation succeeds 573 */ 574 public boolean disconnect() { 575 enforceChangePermission(); 576 synchronized (mWifiStateTracker) { 577 return WifiNative.disconnectCommand(); 578 } 579 } 580 581 /** 582 * see {@link android.net.wifi.WifiManager#reconnect()} 583 * @return {@code true} if the operation succeeds 584 */ 585 public boolean reconnect() { 586 enforceChangePermission(); 587 synchronized (mWifiStateTracker) { 588 return WifiNative.reconnectCommand(); 589 } 590 } 591 592 /** 593 * see {@link android.net.wifi.WifiManager#reassociate()} 594 * @return {@code true} if the operation succeeds 595 */ 596 public boolean reassociate() { 597 enforceChangePermission(); 598 synchronized (mWifiStateTracker) { 599 return WifiNative.reassociateCommand(); 600 } 601 } 602 603 /** 604 * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()} 605 * @return the list of configured networks 606 */ 607 public List<WifiConfiguration> getConfiguredNetworks() { 608 enforceAccessPermission(); 609 String listStr; 610 /* 611 * We don't cache the list, because we want to allow 612 * for the possibility that the configuration file 613 * has been modified through some external means, 614 * such as the wpa_cli command line program. 615 */ 616 synchronized (mWifiStateTracker) { 617 listStr = WifiNative.listNetworksCommand(); 618 } 619 List<WifiConfiguration> networks = 620 new ArrayList<WifiConfiguration>(); 621 if (listStr == null) 622 return networks; 623 624 String[] lines = listStr.split("\n"); 625 // Skip the first line, which is a header 626 for (int i = 1; i < lines.length; i++) { 627 String[] result = lines[i].split("\t"); 628 // network-id | ssid | bssid | flags 629 WifiConfiguration config = new WifiConfiguration(); 630 try { 631 config.networkId = Integer.parseInt(result[0]); 632 } catch(NumberFormatException e) { 633 continue; 634 } 635 if (result.length > 3) { 636 if (result[3].indexOf("[CURRENT]") != -1) 637 config.status = WifiConfiguration.Status.CURRENT; 638 else if (result[3].indexOf("[DISABLED]") != -1) 639 config.status = WifiConfiguration.Status.DISABLED; 640 else 641 config.status = WifiConfiguration.Status.ENABLED; 642 } else 643 config.status = WifiConfiguration.Status.ENABLED; 644 synchronized (mWifiStateTracker) { 645 readNetworkVariables(config); 646 } 647 networks.add(config); 648 } 649 650 return networks; 651 } 652 653 /** 654 * Read the variables from the supplicant daemon that are needed to 655 * fill in the WifiConfiguration object. 656 * <p/> 657 * The caller must hold the synchronization monitor. 658 * @param config the {@link WifiConfiguration} object to be filled in. 659 */ 660 private static void readNetworkVariables(WifiConfiguration config) { 661 662 int netId = config.networkId; 663 if (netId < 0) 664 return; 665 666 /* 667 * TODO: maybe should have a native method that takes an array of 668 * variable names and returns an array of values. But we'd still 669 * be doing a round trip to the supplicant daemon for each variable. 670 */ 671 String value; 672 673 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName); 674 if (!TextUtils.isEmpty(value)) { 675 config.SSID = value; 676 } else { 677 config.SSID = null; 678 } 679 680 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName); 681 if (!TextUtils.isEmpty(value)) { 682 config.BSSID = value; 683 } else { 684 config.BSSID = null; 685 } 686 687 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); 688 config.priority = -1; 689 if (!TextUtils.isEmpty(value)) { 690 try { 691 config.priority = Integer.parseInt(value); 692 } catch (NumberFormatException ignore) { 693 } 694 } 695 696 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName); 697 config.hiddenSSID = false; 698 if (!TextUtils.isEmpty(value)) { 699 try { 700 config.hiddenSSID = Integer.parseInt(value) != 0; 701 } catch (NumberFormatException ignore) { 702 } 703 } 704 705 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName); 706 config.wepTxKeyIndex = -1; 707 if (!TextUtils.isEmpty(value)) { 708 try { 709 config.wepTxKeyIndex = Integer.parseInt(value); 710 } catch (NumberFormatException ignore) { 711 } 712 } 713 714 /* 715 * Get up to 4 WEP keys. Note that the actual keys are not passed back, 716 * just a "*" if the key is set, or the null string otherwise. 717 */ 718 for (int i = 0; i < 4; i++) { 719 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepKeyVarNames[i]); 720 if (!TextUtils.isEmpty(value)) { 721 config.wepKeys[i] = value; 722 } else { 723 config.wepKeys[i] = null; 724 } 725 } 726 727 /* 728 * Get the private shared key. Note that the actual keys are not passed back, 729 * just a "*" if the key is set, or the null string otherwise. 730 */ 731 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName); 732 if (!TextUtils.isEmpty(value)) { 733 config.preSharedKey = value; 734 } else { 735 config.preSharedKey = null; 736 } 737 738 value = WifiNative.getNetworkVariableCommand(config.networkId, 739 WifiConfiguration.Protocol.varName); 740 if (!TextUtils.isEmpty(value)) { 741 String vals[] = value.split(" "); 742 for (String val : vals) { 743 int index = 744 lookupString(val, WifiConfiguration.Protocol.strings); 745 if (0 <= index) { 746 config.allowedProtocols.set(index); 747 } 748 } 749 } 750 751 value = WifiNative.getNetworkVariableCommand(config.networkId, 752 WifiConfiguration.KeyMgmt.varName); 753 if (!TextUtils.isEmpty(value)) { 754 String vals[] = value.split(" "); 755 for (String val : vals) { 756 int index = 757 lookupString(val, WifiConfiguration.KeyMgmt.strings); 758 if (0 <= index) { 759 config.allowedKeyManagement.set(index); 760 } 761 } 762 } 763 764 value = WifiNative.getNetworkVariableCommand(config.networkId, 765 WifiConfiguration.AuthAlgorithm.varName); 766 if (!TextUtils.isEmpty(value)) { 767 String vals[] = value.split(" "); 768 for (String val : vals) { 769 int index = 770 lookupString(val, WifiConfiguration.AuthAlgorithm.strings); 771 if (0 <= index) { 772 config.allowedAuthAlgorithms.set(index); 773 } 774 } 775 } 776 777 value = WifiNative.getNetworkVariableCommand(config.networkId, 778 WifiConfiguration.PairwiseCipher.varName); 779 if (!TextUtils.isEmpty(value)) { 780 String vals[] = value.split(" "); 781 for (String val : vals) { 782 int index = 783 lookupString(val, WifiConfiguration.PairwiseCipher.strings); 784 if (0 <= index) { 785 config.allowedPairwiseCiphers.set(index); 786 } 787 } 788 } 789 790 value = WifiNative.getNetworkVariableCommand(config.networkId, 791 WifiConfiguration.GroupCipher.varName); 792 if (!TextUtils.isEmpty(value)) { 793 String vals[] = value.split(" "); 794 for (String val : vals) { 795 int index = 796 lookupString(val, WifiConfiguration.GroupCipher.strings); 797 if (0 <= index) { 798 config.allowedGroupCiphers.set(index); 799 } 800 } 801 } 802 } 803 804 /** 805 * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)} 806 * @return the supplicant-assigned identifier for the new or updated 807 * network if the operation succeeds, or {@code -1} if it fails 808 */ 809 public synchronized int addOrUpdateNetwork(WifiConfiguration config) { 810 enforceChangePermission(); 811 /* 812 * If the supplied networkId is -1, we create a new empty 813 * network configuration. Otherwise, the networkId should 814 * refer to an existing configuration. 815 */ 816 int netId = config.networkId; 817 boolean newNetwork = netId == -1; 818 boolean doReconfig; 819 int currentPriority; 820 // networkId of -1 means we want to create a new network 821 if (newNetwork) { 822 netId = WifiNative.addNetworkCommand(); 823 if (netId < 0) { 824 if (DBG) { 825 Log.d(TAG, "Failed to add a network!"); 826 } 827 return -1; 828 } 829 doReconfig = true; 830 } else { 831 String priorityVal = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); 832 currentPriority = -1; 833 if (!TextUtils.isEmpty(priorityVal)) { 834 try { 835 currentPriority = Integer.parseInt(priorityVal); 836 } catch (NumberFormatException ignore) { 837 } 838 } 839 doReconfig = currentPriority != config.priority; 840 } 841 mNeedReconfig = mNeedReconfig || doReconfig; 842 843 /* 844 * If we have hidden networks, we may have to change the scan mode 845 */ 846 if (config.hiddenSSID) { 847 // Mark the network as present unless it is disabled 848 addOrUpdateHiddenNetwork( 849 netId, config.status != WifiConfiguration.Status.DISABLED); 850 } 851 852 setVariables: { 853 /* 854 * Note that if a networkId for a non-existent network 855 * was supplied, then the first setNetworkVariableCommand() 856 * will fail, so we don't bother to make a separate check 857 * for the validity of the ID up front. 858 */ 859 860 if (config.SSID != null && 861 !WifiNative.setNetworkVariableCommand( 862 netId, 863 WifiConfiguration.ssidVarName, 864 config.SSID)) { 865 if (DBG) { 866 Log.d(TAG, "failed to set SSID: "+config.SSID); 867 } 868 break setVariables; 869 } 870 871 if (config.BSSID != null && 872 !WifiNative.setNetworkVariableCommand( 873 netId, 874 WifiConfiguration.bssidVarName, 875 config.BSSID)) { 876 if (DBG) { 877 Log.d(TAG, "failed to set BSSID: "+config.BSSID); 878 } 879 break setVariables; 880 } 881 882 String allowedKeyManagementString = 883 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 884 if (config.allowedKeyManagement.cardinality() != 0 && 885 !WifiNative.setNetworkVariableCommand( 886 netId, 887 WifiConfiguration.KeyMgmt.varName, 888 allowedKeyManagementString)) { 889 if (DBG) { 890 Log.d(TAG, "failed to set key_mgmt: "+ 891 allowedKeyManagementString); 892 } 893 break setVariables; 894 } 895 896 String allowedProtocolsString = 897 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 898 if (config.allowedProtocols.cardinality() != 0 && 899 !WifiNative.setNetworkVariableCommand( 900 netId, 901 WifiConfiguration.Protocol.varName, 902 allowedProtocolsString)) { 903 if (DBG) { 904 Log.d(TAG, "failed to set proto: "+ 905 allowedProtocolsString); 906 } 907 break setVariables; 908 } 909 910 String allowedAuthAlgorithmsString = 911 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); 912 if (config.allowedAuthAlgorithms.cardinality() != 0 && 913 !WifiNative.setNetworkVariableCommand( 914 netId, 915 WifiConfiguration.AuthAlgorithm.varName, 916 allowedAuthAlgorithmsString)) { 917 if (DBG) { 918 Log.d(TAG, "failed to set auth_alg: "+ 919 allowedAuthAlgorithmsString); 920 } 921 break setVariables; 922 } 923 924 String allowedPairwiseCiphersString = 925 makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings); 926 if (config.allowedPairwiseCiphers.cardinality() != 0 && 927 !WifiNative.setNetworkVariableCommand( 928 netId, 929 WifiConfiguration.PairwiseCipher.varName, 930 allowedPairwiseCiphersString)) { 931 if (DBG) { 932 Log.d(TAG, "failed to set pairwise: "+ 933 allowedPairwiseCiphersString); 934 } 935 break setVariables; 936 } 937 938 String allowedGroupCiphersString = 939 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); 940 if (config.allowedGroupCiphers.cardinality() != 0 && 941 !WifiNative.setNetworkVariableCommand( 942 netId, 943 WifiConfiguration.GroupCipher.varName, 944 allowedGroupCiphersString)) { 945 if (DBG) { 946 Log.d(TAG, "failed to set group: "+ 947 allowedGroupCiphersString); 948 } 949 break setVariables; 950 } 951 952 // Prevent client screw-up by passing in a WifiConfiguration we gave it 953 // by preventing "*" as a key. 954 if (config.preSharedKey != null && !config.preSharedKey.equals("*") && 955 !WifiNative.setNetworkVariableCommand( 956 netId, 957 WifiConfiguration.pskVarName, 958 config.preSharedKey)) { 959 if (DBG) { 960 Log.d(TAG, "failed to set psk: "+config.preSharedKey); 961 } 962 break setVariables; 963 } 964 965 boolean hasSetKey = false; 966 if (config.wepKeys != null) { 967 for (int i = 0; i < config.wepKeys.length; i++) { 968 // Prevent client screw-up by passing in a WifiConfiguration we gave it 969 // by preventing "*" as a key. 970 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 971 if (!WifiNative.setNetworkVariableCommand( 972 netId, 973 WifiConfiguration.wepKeyVarNames[i], 974 config.wepKeys[i])) { 975 if (DBG) { 976 Log.d(TAG, 977 "failed to set wep_key"+i+": " + 978 config.wepKeys[i]); 979 } 980 break setVariables; 981 } 982 hasSetKey = true; 983 } 984 } 985 } 986 987 if (hasSetKey) { 988 if (!WifiNative.setNetworkVariableCommand( 989 netId, 990 WifiConfiguration.wepTxKeyIdxVarName, 991 Integer.toString(config.wepTxKeyIndex))) { 992 if (DBG) { 993 Log.d(TAG, 994 "failed to set wep_tx_keyidx: "+ 995 config.wepTxKeyIndex); 996 } 997 break setVariables; 998 } 999 } 1000 1001 if (!WifiNative.setNetworkVariableCommand( 1002 netId, 1003 WifiConfiguration.priorityVarName, 1004 Integer.toString(config.priority))) { 1005 if (DBG) { 1006 Log.d(TAG, config.SSID + ": failed to set priority: " 1007 +config.priority); 1008 } 1009 break setVariables; 1010 } 1011 1012 if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand( 1013 netId, 1014 WifiConfiguration.hiddenSSIDVarName, 1015 Integer.toString(config.hiddenSSID ? 1 : 0))) { 1016 if (DBG) { 1017 Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+ 1018 config.hiddenSSID); 1019 } 1020 break setVariables; 1021 } 1022 1023 return netId; 1024 } 1025 1026 /* 1027 * For an update, if one of the setNetworkVariable operations fails, 1028 * we might want to roll back all the changes already made. But the 1029 * chances are that if anything is going to go wrong, it'll happen 1030 * the first time we try to set one of the variables. 1031 */ 1032 if (newNetwork) { 1033 removeNetwork(netId); 1034 if (DBG) { 1035 Log.d(TAG, 1036 "Failed to set a network variable, removed network: " 1037 + netId); 1038 } 1039 } 1040 return -1; 1041 } 1042 1043 private static String makeString(BitSet set, String[] strings) { 1044 StringBuffer buf = new StringBuffer(); 1045 int nextSetBit = -1; 1046 1047 /* Make sure all set bits are in [0, strings.length) to avoid 1048 * going out of bounds on strings. (Shouldn't happen, but...) */ 1049 set = set.get(0, strings.length); 1050 1051 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { 1052 buf.append(strings[nextSetBit].replace('_', '-')).append(' '); 1053 } 1054 1055 // remove trailing space 1056 if (set.cardinality() > 0) { 1057 buf.setLength(buf.length() - 1); 1058 } 1059 1060 return buf.toString(); 1061 } 1062 1063 private static int lookupString(String string, String[] strings) { 1064 int size = strings.length; 1065 1066 string = string.replace('-', '_'); 1067 1068 for (int i = 0; i < size; i++) 1069 if (string.equals(strings[i])) 1070 return i; 1071 1072 if (DBG) { 1073 // if we ever get here, we should probably add the 1074 // value to WifiConfiguration to reflect that it's 1075 // supported by the WPA supplicant 1076 Log.w(TAG, "Failed to look-up a string: " + string); 1077 } 1078 1079 return -1; 1080 } 1081 1082 /** 1083 * See {@link android.net.wifi.WifiManager#removeNetwork(int)} 1084 * @param netId the integer that identifies the network configuration 1085 * to the supplicant 1086 * @return {@code true} if the operation succeeded 1087 */ 1088 public boolean removeNetwork(int netId) { 1089 enforceChangePermission(); 1090 1091 /* 1092 * If we have hidden networks, we may have to change the scan mode 1093 */ 1094 removeNetworkIfHidden(netId); 1095 1096 return mWifiStateTracker.removeNetwork(netId); 1097 } 1098 1099 /** 1100 * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)} 1101 * @param netId the integer that identifies the network configuration 1102 * to the supplicant 1103 * @param disableOthers if true, disable all other networks. 1104 * @return {@code true} if the operation succeeded 1105 */ 1106 public boolean enableNetwork(int netId, boolean disableOthers) { 1107 enforceChangePermission(); 1108 1109 /* 1110 * If we have hidden networks, we may have to change the scan mode 1111 */ 1112 synchronized(this) { 1113 if (disableOthers) { 1114 markAllHiddenNetworksButOneAsNotPresent(netId); 1115 } 1116 updateNetworkIfHidden(netId, true); 1117 } 1118 1119 synchronized (mWifiStateTracker) { 1120 return WifiNative.enableNetworkCommand(netId, disableOthers); 1121 } 1122 } 1123 1124 /** 1125 * See {@link android.net.wifi.WifiManager#disableNetwork(int)} 1126 * @param netId the integer that identifies the network configuration 1127 * to the supplicant 1128 * @return {@code true} if the operation succeeded 1129 */ 1130 public boolean disableNetwork(int netId) { 1131 enforceChangePermission(); 1132 1133 /* 1134 * If we have hidden networks, we may have to change the scan mode 1135 */ 1136 updateNetworkIfHidden(netId, false); 1137 1138 synchronized (mWifiStateTracker) { 1139 return WifiNative.disableNetworkCommand(netId); 1140 } 1141 } 1142 1143 /** 1144 * See {@link android.net.wifi.WifiManager#getConnectionInfo()} 1145 * @return the Wi-Fi information, contained in {@link WifiInfo}. 1146 */ 1147 public WifiInfo getConnectionInfo() { 1148 enforceAccessPermission(); 1149 /* 1150 * Make sure we have the latest information, by sending 1151 * a status request to the supplicant. 1152 */ 1153 return mWifiStateTracker.requestConnectionInfo(); 1154 } 1155 1156 /** 1157 * Return the results of the most recent access point scan, in the form of 1158 * a list of {@link ScanResult} objects. 1159 * @return the list of results 1160 */ 1161 public List<ScanResult> getScanResults() { 1162 enforceAccessPermission(); 1163 String reply; 1164 synchronized (mWifiStateTracker) { 1165 reply = WifiNative.scanResultsCommand(); 1166 } 1167 if (reply == null) { 1168 return null; 1169 } 1170 1171 List<ScanResult> scanList = new ArrayList<ScanResult>(); 1172 1173 int lineCount = 0; 1174 1175 int replyLen = reply.length(); 1176 // Parse the result string, keeping in mind that the last line does 1177 // not end with a newline. 1178 for (int lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) { 1179 if (lineEnd == replyLen || reply.charAt(lineEnd) == '\n') { 1180 ++lineCount; 1181 /* 1182 * Skip the first line, which is a header 1183 */ 1184 if (lineCount == 1) { 1185 lineBeg = lineEnd + 1; 1186 continue; 1187 } 1188 int lineLen = lineEnd - lineBeg; 1189 if (0 < lineLen && lineLen <= SCAN_RESULT_BUFFER_SIZE) { 1190 int scanResultLevel = 0; 1191 /* 1192 * At most one thread should have access to the buffer at a time! 1193 */ 1194 synchronized(mScanResultBuffer) { 1195 boolean parsingScanResultLevel = false; 1196 for (int i = lineBeg; i < lineEnd; ++i) { 1197 char ch = reply.charAt(i); 1198 /* 1199 * Assume that the signal level starts with a '-' 1200 */ 1201 if (ch == '-') { 1202 /* 1203 * Skip whatever instances of '-' we may have 1204 * after we parse the signal level 1205 */ 1206 parsingScanResultLevel = (scanResultLevel == 0); 1207 } else if (parsingScanResultLevel) { 1208 int digit = Character.digit(ch, 10); 1209 if (0 <= digit) { 1210 scanResultLevel = 1211 10 * scanResultLevel + digit; 1212 /* 1213 * Replace the signal level number in 1214 * the string with 0's for caching 1215 */ 1216 ch = '0'; 1217 } else { 1218 /* 1219 * Reset the flag if we meet a non-digit 1220 * character 1221 */ 1222 parsingScanResultLevel = false; 1223 } 1224 } 1225 mScanResultBuffer[i - lineBeg] = ch; 1226 } 1227 if (scanResultLevel != 0) { 1228 ScanResult scanResult = parseScanResult( 1229 new String(mScanResultBuffer, 0, lineLen)); 1230 if (scanResult != null) { 1231 scanResult.level = -scanResultLevel; 1232 scanList.add(scanResult); 1233 } 1234 } else if (DBG) { 1235 Log.w(TAG, 1236 "ScanResult.level=0: misformatted scan result?"); 1237 } 1238 } 1239 } else if (0 < lineLen) { 1240 if (DBG) { 1241 Log.w(TAG, "Scan result line is too long: " + 1242 (lineEnd - lineBeg) + ", skipping the line!"); 1243 } 1244 } 1245 lineBeg = lineEnd + 1; 1246 } 1247 } 1248 mWifiStateTracker.setScanResultsList(scanList); 1249 return scanList; 1250 } 1251 1252 /** 1253 * Parse the scan result line passed to us by wpa_supplicant (helper). 1254 * @param line the line to parse 1255 * @return the {@link ScanResult} object 1256 */ 1257 private ScanResult parseScanResult(String line) { 1258 ScanResult scanResult = null; 1259 if (line != null) { 1260 /* 1261 * Cache implementation (LinkedHashMap) is not synchronized, thus, 1262 * must synchronized here! 1263 */ 1264 synchronized (mScanResultCache) { 1265 scanResult = mScanResultCache.get(line); 1266 if (scanResult == null) { 1267 String[] result = scanResultPattern.split(line); 1268 if (3 <= result.length && result.length <= 5) { 1269 // bssid | frequency | level | flags | ssid 1270 int frequency; 1271 int level; 1272 try { 1273 frequency = Integer.parseInt(result[1]); 1274 level = Integer.parseInt(result[2]); 1275 } catch (NumberFormatException e) { 1276 frequency = 0; 1277 level = 0; 1278 } 1279 1280 /* 1281 * The formatting of the results returned by 1282 * wpa_supplicant is intended to make the fields 1283 * line up nicely when printed, 1284 * not to make them easy to parse. So we have to 1285 * apply some heuristics to figure out which field 1286 * is the SSID and which field is the flags. 1287 */ 1288 String ssid; 1289 String flags; 1290 if (result.length == 4) { 1291 if (result[3].charAt(0) == '[') { 1292 flags = result[3]; 1293 ssid = ""; 1294 } else { 1295 flags = ""; 1296 ssid = result[3]; 1297 } 1298 } else if (result.length == 5) { 1299 flags = result[3]; 1300 ssid = result[4]; 1301 } else { 1302 // Here, we must have 3 fields: no flags and ssid 1303 // set 1304 flags = ""; 1305 ssid = ""; 1306 } 1307 1308 // Do not add scan results that have no SSID set 1309 if (0 < ssid.trim().length()) { 1310 scanResult = 1311 new ScanResult( 1312 ssid, result[0], flags, level, frequency); 1313 mScanResultCache.put(line, scanResult); 1314 } 1315 } else { 1316 Log.w(TAG, "Misformatted scan result text with " + 1317 result.length + " fields: " + line); 1318 } 1319 } 1320 } 1321 } 1322 1323 return scanResult; 1324 } 1325 1326 /** 1327 * Parse the "flags" field passed back in a scan result by wpa_supplicant, 1328 * and construct a {@code WifiConfiguration} that describes the encryption, 1329 * key management, and authenticaion capabilities of the access point. 1330 * @param flags the string returned by wpa_supplicant 1331 * @return the {@link WifiConfiguration} object, filled in 1332 */ 1333 WifiConfiguration parseScanFlags(String flags) { 1334 WifiConfiguration config = new WifiConfiguration(); 1335 1336 if (flags.length() == 0) { 1337 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 1338 } 1339 // ... to be implemented 1340 return config; 1341 } 1342 1343 /** 1344 * Tell the supplicant to persist the current list of configured networks. 1345 * @return {@code true} if the operation succeeded 1346 */ 1347 public boolean saveConfiguration() { 1348 boolean result; 1349 enforceChangePermission(); 1350 synchronized (mWifiStateTracker) { 1351 result = WifiNative.saveConfigCommand(); 1352 if (result && mNeedReconfig) { 1353 mNeedReconfig = false; 1354 result = WifiNative.reloadConfigCommand(); 1355 1356 if (result) { 1357 Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION); 1358 mContext.sendBroadcast(intent); 1359 } 1360 } 1361 } 1362 return result; 1363 } 1364 1365 /** 1366 * Set the number of radio frequency channels that are allowed to be used 1367 * in the current regulatory domain. This method should be used only 1368 * if the correct number of channels cannot be determined automatically 1369 * for some reason. If the operation is successful, the new value is 1370 * persisted as a Secure setting. 1371 * @param numChannels the number of allowed channels. Must be greater than 0 1372 * and less than or equal to 16. 1373 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., 1374 * {@code numChannels} is outside the valid range. 1375 */ 1376 public boolean setNumAllowedChannels(int numChannels) { 1377 enforceChangePermission(); 1378 /* 1379 * Validate the argument. We'd like to let the Wi-Fi driver do this, 1380 * but if Wi-Fi isn't currently enabled, that's not possible, and 1381 * we want to persist the setting anyway,so that it will take 1382 * effect when Wi-Fi does become enabled. 1383 */ 1384 boolean found = false; 1385 for (int validChan : sValidRegulatoryChannelCounts) { 1386 if (validChan == numChannels) { 1387 found = true; 1388 break; 1389 } 1390 } 1391 if (!found) { 1392 return false; 1393 } 1394 1395 Settings.Secure.putInt(mContext.getContentResolver(), 1396 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, 1397 numChannels); 1398 mWifiStateTracker.setNumAllowedChannels(numChannels); 1399 return true; 1400 } 1401 1402 /** 1403 * Return the number of frequency channels that are allowed 1404 * to be used in the current regulatory domain. 1405 * @return the number of allowed channels, or {@code -1} if an error occurs 1406 */ 1407 public int getNumAllowedChannels() { 1408 int numChannels; 1409 1410 enforceAccessPermission(); 1411 synchronized (mWifiStateTracker) { 1412 /* 1413 * If we can't get the value from the driver (e.g., because 1414 * Wi-Fi is not currently enabled), get the value from 1415 * Settings. 1416 */ 1417 numChannels = WifiNative.getNumAllowedChannelsCommand(); 1418 if (numChannels < 0) { 1419 numChannels = Settings.Secure.getInt(mContext.getContentResolver(), 1420 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, 1421 -1); 1422 } 1423 } 1424 return numChannels; 1425 } 1426 1427 /** 1428 * Return the list of valid values for the number of allowed radio channels 1429 * for various regulatory domains. 1430 * @return the list of channel counts 1431 */ 1432 public int[] getValidChannelCounts() { 1433 enforceAccessPermission(); 1434 return sValidRegulatoryChannelCounts; 1435 } 1436 1437 /** 1438 * Return the DHCP-assigned addresses from the last successful DHCP request, 1439 * if any. 1440 * @return the DHCP information 1441 */ 1442 public DhcpInfo getDhcpInfo() { 1443 enforceAccessPermission(); 1444 return mWifiStateTracker.getDhcpInfo(); 1445 } 1446 1447 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 1448 @Override 1449 public void onReceive(Context context, Intent intent) { 1450 String action = intent.getAction(); 1451 1452 long idleMillis = Settings.Gservices.getLong(mContext.getContentResolver(), 1453 Settings.Gservices.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); 1454 int stayAwakeConditions = 1455 Settings.System.getInt(mContext.getContentResolver(), 1456 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); 1457 if (action.equals(Intent.ACTION_SCREEN_ON)) { 1458 mAlarmManager.cancel(mIdleIntent); 1459 mDeviceIdle = false; 1460 mScreenOff = false; 1461 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 1462 mScreenOff = true; 1463 /* 1464 * Set a timer to put Wi-Fi to sleep, but only if the screen is off 1465 * AND the "stay on while plugged in" setting doesn't match the 1466 * current power conditions (i.e, not plugged in, plugged in to USB, 1467 * or plugged in to AC). 1468 */ 1469 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) { 1470 long triggerTime = System.currentTimeMillis() + idleMillis; 1471 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); 1472 } 1473 /* we can return now -- there's nothing to do until we get the idle intent back */ 1474 return; 1475 } else if (action.equals(ACTION_DEVICE_IDLE)) { 1476 mDeviceIdle = true; 1477 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 1478 /* 1479 * Set a timer to put Wi-Fi to sleep, but only if the screen is off 1480 * AND we are transitioning from a state in which the device was supposed 1481 * to stay awake to a state in which it is not supposed to stay awake. 1482 * If "stay awake" state is not changing, we do nothing, to avoid resetting 1483 * the already-set timer. 1484 */ 1485 int pluggedType = intent.getIntExtra("plugged", 0); 1486 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) && 1487 !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) { 1488 long triggerTime = System.currentTimeMillis() + idleMillis; 1489 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); 1490 mPluggedType = pluggedType; 1491 return; 1492 } 1493 mPluggedType = pluggedType; 1494 } else { 1495 return; 1496 } 1497 1498 updateWifiState(); 1499 } 1500 1501 /** 1502 * Determines whether the Wi-Fi chipset should stay awake or be put to 1503 * sleep. Looks at the setting for the sleep policy and the current 1504 * conditions. 1505 * 1506 * @see #shouldDeviceStayAwake(int, int) 1507 */ 1508 private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) { 1509 int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(), 1510 Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT); 1511 1512 if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) { 1513 // Never sleep 1514 return true; 1515 } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) && 1516 (pluggedType != 0)) { 1517 // Never sleep while plugged, and we're plugged 1518 return true; 1519 } else { 1520 // Default 1521 return shouldDeviceStayAwake(stayAwakeConditions, pluggedType); 1522 } 1523 } 1524 1525 /** 1526 * Determine whether the bit value corresponding to {@code pluggedType} is set in 1527 * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value 1528 * of {@code 0} isn't really a plugged type, but rather an indication that the 1529 * device isn't plugged in at all, there is no bit value corresponding to a 1530 * {@code pluggedType} value of {@code 0}. That is why we shift by 1531 * {@code pluggedType — 1} instead of by {@code pluggedType}. 1532 * @param stayAwakeConditions a bit string specifying which "plugged types" should 1533 * keep the device (and hence Wi-Fi) awake. 1534 * @param pluggedType the type of plug (USB, AC, or none) for which the check is 1535 * being made 1536 * @return {@code true} if {@code pluggedType} indicates that the device is 1537 * supposed to stay awake, {@code false} otherwise. 1538 */ 1539 private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) { 1540 return (stayAwakeConditions & pluggedType) != 0; 1541 } 1542 }; 1543 1544 private void sendEnableMessage(boolean enable, boolean persist) { 1545 Message msg = Message.obtain(mWifiHandler, 1546 (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI), 1547 (persist ? 1 : 0), 0); 1548 msg.sendToTarget(); 1549 } 1550 1551 private void sendStartMessage(boolean scanOnlyMode) { 1552 Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget(); 1553 } 1554 1555 private void updateWifiState() { 1556 boolean wifiEnabled = getPersistedWifiEnabled(); 1557 boolean airplaneMode = isAirplaneModeOn(); 1558 boolean lockHeld = mLocks.hasLocks(); 1559 int strongestLockMode; 1560 boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode; 1561 boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld; 1562 if (mDeviceIdle && lockHeld) { 1563 strongestLockMode = mLocks.getStrongestLockMode(); 1564 } else { 1565 strongestLockMode = WifiManager.WIFI_MODE_FULL; 1566 } 1567 1568 synchronized (mWifiHandler) { 1569 if (mWifiState == WIFI_STATE_ENABLING && !airplaneMode) { 1570 return; 1571 } 1572 if (wifiShouldBeEnabled) { 1573 if (wifiShouldBeStarted) { 1574 sWakeLock.acquire(); 1575 sendEnableMessage(true, false); 1576 sWakeLock.acquire(); 1577 sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY); 1578 } else { 1579 int wakeLockTimeout = 1580 Settings.Secure.getInt( 1581 mContext.getContentResolver(), 1582 Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS, 1583 DEFAULT_WAKELOCK_TIMEOUT); 1584 /* 1585 * The following wakelock is held in order to ensure 1586 * that the connectivity manager has time to fail over 1587 * to the mobile data network. The connectivity manager 1588 * releases it once mobile data connectivity has been 1589 * established. If connectivity cannot be established, 1590 * the wakelock is released after wakeLockTimeout 1591 * milliseconds have elapsed. 1592 */ 1593 sDriverStopWakeLock.acquire(); 1594 mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI); 1595 mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout); 1596 } 1597 } else { 1598 sWakeLock.acquire(); 1599 sendEnableMessage(false, false); 1600 } 1601 } 1602 } 1603 1604 private void registerForBroadcasts() { 1605 IntentFilter intentFilter = new IntentFilter(); 1606 if (isAirplaneSensitive()) { 1607 intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); 1608 } 1609 intentFilter.addAction(Intent.ACTION_SCREEN_ON); 1610 intentFilter.addAction(Intent.ACTION_SCREEN_OFF); 1611 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); 1612 intentFilter.addAction(ACTION_DEVICE_IDLE); 1613 mContext.registerReceiver(mReceiver, intentFilter); 1614 } 1615 1616 private boolean isAirplaneSensitive() { 1617 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(), 1618 Settings.System.AIRPLANE_MODE_RADIOS); 1619 return airplaneModeRadios == null 1620 || airplaneModeRadios.contains(Settings.System.RADIO_WIFI); 1621 } 1622 1623 /** 1624 * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is 1625 * currently on. 1626 * @return {@code true} if airplane mode is on. 1627 */ 1628 private boolean isAirplaneModeOn() { 1629 return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(), 1630 Settings.System.AIRPLANE_MODE_ON, 0) == 1; 1631 } 1632 1633 /** 1634 * Handler that allows posting to the WifiThread. 1635 */ 1636 private class WifiHandler extends Handler { 1637 public WifiHandler(Looper looper) { 1638 super(looper); 1639 } 1640 1641 @Override 1642 public void handleMessage(Message msg) { 1643 switch (msg.what) { 1644 1645 case MESSAGE_ENABLE_WIFI: 1646 setWifiEnabledBlocking(true, msg.arg1 == 1); 1647 sWakeLock.release(); 1648 break; 1649 1650 case MESSAGE_START_WIFI: 1651 mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0); 1652 mWifiStateTracker.restart(); 1653 sWakeLock.release(); 1654 break; 1655 1656 case MESSAGE_DISABLE_WIFI: 1657 // a non-zero msg.arg1 value means the "enabled" setting 1658 // should be persisted 1659 setWifiEnabledBlocking(false, msg.arg1 == 1); 1660 sWakeLock.release(); 1661 break; 1662 1663 case MESSAGE_STOP_WIFI: 1664 mWifiStateTracker.disconnectAndStop(); 1665 // don't release wakelock 1666 break; 1667 1668 case MESSAGE_RELEASE_WAKELOCK: 1669 synchronized (sDriverStopWakeLock) { 1670 if (sDriverStopWakeLock.isHeld()) { 1671 sDriverStopWakeLock.release(); 1672 } 1673 } 1674 break; 1675 } 1676 } 1677 } 1678 1679 @Override 1680 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1681 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1682 != PackageManager.PERMISSION_GRANTED) { 1683 pw.println("Permission Denial: can't dump WifiService from from pid=" 1684 + Binder.getCallingPid() 1685 + ", uid=" + Binder.getCallingUid()); 1686 return; 1687 } 1688 pw.println("Wi-Fi is " + stateName(mWifiState)); 1689 pw.println("Stay-awake conditions: " + 1690 Settings.System.getInt(mContext.getContentResolver(), 1691 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0)); 1692 pw.println(); 1693 1694 pw.println("Internal state:"); 1695 pw.println(mWifiStateTracker); 1696 pw.println(); 1697 pw.println("Latest scan results:"); 1698 List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList(); 1699 if (scanResults != null && scanResults.size() != 0) { 1700 pw.println(" BSSID Frequency RSSI Flags SSID"); 1701 for (ScanResult r : scanResults) { 1702 pw.printf(" %17s %9d %5d %-16s %s%n", 1703 r.BSSID, 1704 r.frequency, 1705 r.level, 1706 r.capabilities, 1707 r.SSID == null ? "" : r.SSID); 1708 } 1709 } 1710 pw.println(); 1711 pw.println("Locks held:"); 1712 mLocks.dump(pw); 1713 } 1714 1715 private static String stateName(int wifiState) { 1716 switch (wifiState) { 1717 case WIFI_STATE_DISABLING: 1718 return "disabling"; 1719 case WIFI_STATE_DISABLED: 1720 return "disabled"; 1721 case WIFI_STATE_ENABLING: 1722 return "enabling"; 1723 case WIFI_STATE_ENABLED: 1724 return "enabled"; 1725 case WIFI_STATE_UNKNOWN: 1726 return "unknown state"; 1727 default: 1728 return "[invalid state]"; 1729 } 1730 } 1731 1732 private class WifiLock implements IBinder.DeathRecipient { 1733 String mTag; 1734 int mLockMode; 1735 IBinder mBinder; 1736 1737 WifiLock(int lockMode, String tag, IBinder binder) { 1738 super(); 1739 mTag = tag; 1740 mLockMode = lockMode; 1741 mBinder = binder; 1742 try { 1743 mBinder.linkToDeath(this, 0); 1744 } catch (RemoteException e) { 1745 binderDied(); 1746 } 1747 } 1748 1749 public void binderDied() { 1750 synchronized (mLocks) { 1751 releaseWifiLockLocked(mBinder); 1752 } 1753 } 1754 1755 public String toString() { 1756 return "WifiLock{" + mTag + " type=" + mLockMode + " binder=" + mBinder + "}"; 1757 } 1758 } 1759 1760 private class LockList { 1761 private List<WifiLock> mList; 1762 1763 private LockList() { 1764 mList = new ArrayList<WifiLock>(); 1765 } 1766 1767 private synchronized boolean hasLocks() { 1768 return !mList.isEmpty(); 1769 } 1770 1771 private synchronized int getStrongestLockMode() { 1772 if (mList.isEmpty()) { 1773 return WifiManager.WIFI_MODE_FULL; 1774 } 1775 for (WifiLock l : mList) { 1776 if (l.mLockMode == WifiManager.WIFI_MODE_FULL) { 1777 return WifiManager.WIFI_MODE_FULL; 1778 } 1779 } 1780 return WifiManager.WIFI_MODE_SCAN_ONLY; 1781 } 1782 1783 private void addLock(WifiLock lock) { 1784 if (findLockByBinder(lock.mBinder) < 0) { 1785 mList.add(lock); 1786 } 1787 } 1788 1789 private WifiLock removeLock(IBinder binder) { 1790 int index = findLockByBinder(binder); 1791 if (index >= 0) { 1792 return mList.remove(index); 1793 } else { 1794 return null; 1795 } 1796 } 1797 1798 private int findLockByBinder(IBinder binder) { 1799 int size = mList.size(); 1800 for (int i = size - 1; i >= 0; i--) 1801 if (mList.get(i).mBinder == binder) 1802 return i; 1803 return -1; 1804 } 1805 1806 private void dump(PrintWriter pw) { 1807 for (WifiLock l : mList) { 1808 pw.print(" "); 1809 pw.println(l); 1810 } 1811 } 1812 } 1813 1814 public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) { 1815 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); 1816 if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) { 1817 return false; 1818 } 1819 WifiLock wifiLock = new WifiLock(lockMode, tag, binder); 1820 synchronized (mLocks) { 1821 return acquireWifiLockLocked(wifiLock); 1822 } 1823 } 1824 1825 private boolean acquireWifiLockLocked(WifiLock wifiLock) { 1826 mLocks.addLock(wifiLock); 1827 updateWifiState(); 1828 return true; 1829 } 1830 1831 public boolean releaseWifiLock(IBinder lock) { 1832 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); 1833 synchronized (mLocks) { 1834 return releaseWifiLockLocked(lock); 1835 } 1836 } 1837 1838 private boolean releaseWifiLockLocked(IBinder lock) { 1839 boolean result; 1840 result = (mLocks.removeLock(lock) != null); 1841 updateWifiState(); 1842 return result; 1843 } 1844} 1845