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