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