WifiService.java revision 7440fc2e0e0257043b967a80dceb0b33797d1d12
1/* 2 * Copyright (C) 2010 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 android.app.AlarmManager; 20import android.app.Notification; 21import android.app.NotificationManager; 22import android.app.PendingIntent; 23import android.bluetooth.BluetoothA2dp; 24import android.bluetooth.BluetoothAdapter; 25import android.bluetooth.BluetoothDevice; 26import android.bluetooth.BluetoothProfile; 27import android.content.BroadcastReceiver; 28import android.content.ContentResolver; 29import android.content.Context; 30import android.content.Intent; 31import android.content.IntentFilter; 32import android.content.pm.PackageManager; 33import android.database.ContentObserver; 34import android.net.wifi.IWifiManager; 35import android.net.wifi.WifiInfo; 36import android.net.wifi.WifiManager; 37import android.net.wifi.WifiStateMachine; 38import android.net.wifi.ScanResult; 39import android.net.wifi.WifiConfiguration; 40import android.net.wifi.SupplicantState; 41import android.net.wifi.WifiConfiguration.KeyMgmt; 42import android.net.ConnectivityManager; 43import android.net.InterfaceConfiguration; 44import android.net.DhcpInfo; 45import android.net.NetworkInfo; 46import android.net.NetworkInfo.State; 47import android.os.Binder; 48import android.os.Handler; 49import android.os.HandlerThread; 50import android.os.IBinder; 51import android.os.INetworkManagementService; 52import android.os.Message; 53import android.os.RemoteException; 54import android.os.ServiceManager; 55import android.os.WorkSource; 56import android.provider.Settings; 57import android.text.TextUtils; 58import android.util.Slog; 59 60import java.util.ArrayList; 61import java.util.List; 62import java.util.Set; 63import java.util.concurrent.atomic.AtomicBoolean; 64import java.io.FileDescriptor; 65import java.io.PrintWriter; 66 67import com.android.internal.app.IBatteryStats; 68import com.android.server.am.BatteryStatsService; 69import com.android.internal.R; 70 71/** 72 * WifiService handles remote WiFi operation requests by implementing 73 * the IWifiManager interface. 74 * 75 * @hide 76 */ 77//TODO: Clean up multiple locks and implement WifiService 78// as a SM to track soft AP/client/adhoc bring up based 79// on device idle state, airplane mode and boot. 80 81public class WifiService extends IWifiManager.Stub { 82 private static final String TAG = "WifiService"; 83 private static final boolean DBG = true; 84 85 private final WifiStateMachine mWifiStateMachine; 86 87 private Context mContext; 88 89 private AlarmManager mAlarmManager; 90 private PendingIntent mIdleIntent; 91 private BluetoothA2dp mBluetoothA2dp; 92 private static final int IDLE_REQUEST = 0; 93 private boolean mScreenOff; 94 private boolean mDeviceIdle; 95 private int mPluggedType; 96 97 // true if the user enabled Wifi while in airplane mode 98 private AtomicBoolean mAirplaneModeOverwridden = new AtomicBoolean(false); 99 100 private final LockList mLocks = new LockList(); 101 // some wifi lock statistics 102 private int mFullLocksAcquired; 103 private int mFullLocksReleased; 104 private int mScanLocksAcquired; 105 private int mScanLocksReleased; 106 107 private final List<Multicaster> mMulticasters = 108 new ArrayList<Multicaster>(); 109 private int mMulticastEnabled; 110 private int mMulticastDisabled; 111 112 private final IBatteryStats mBatteryStats; 113 114 ConnectivityManager mCm; 115 private String[] mWifiRegexs; 116 117 /** 118 * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a 119 * Settings.Secure value is not present. This timeout value is chosen as 120 * the approximate point at which the battery drain caused by Wi-Fi 121 * being enabled but not active exceeds the battery drain caused by 122 * re-establishing a connection to the mobile data network. 123 */ 124 private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */ 125 126 /** 127 * Number of allowed radio frequency channels in various regulatory domains. 128 * This list is sufficient for 802.11b/g networks (2.4GHz range). 129 */ 130 private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14}; 131 132 private static final String ACTION_DEVICE_IDLE = 133 "com.android.server.WifiManager.action.DEVICE_IDLE"; 134 135 private boolean mIsReceiverRegistered = false; 136 137 138 NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", ""); 139 140 // Variables relating to the 'available networks' notification 141 /** 142 * The icon to show in the 'available networks' notification. This will also 143 * be the ID of the Notification given to the NotificationManager. 144 */ 145 private static final int ICON_NETWORKS_AVAILABLE = 146 com.android.internal.R.drawable.stat_notify_wifi_in_range; 147 /** 148 * When a notification is shown, we wait this amount before possibly showing it again. 149 */ 150 private final long NOTIFICATION_REPEAT_DELAY_MS; 151 /** 152 * Whether the user has set the setting to show the 'available networks' notification. 153 */ 154 private boolean mNotificationEnabled; 155 /** 156 * Observes the user setting to keep {@link #mNotificationEnabled} in sync. 157 */ 158 private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver; 159 /** 160 * The {@link System#currentTimeMillis()} must be at least this value for us 161 * to show the notification again. 162 */ 163 private long mNotificationRepeatTime; 164 /** 165 * The Notification object given to the NotificationManager. 166 */ 167 private Notification mNotification; 168 /** 169 * Whether the notification is being shown, as set by us. That is, if the 170 * user cancels the notification, we will not receive the callback so this 171 * will still be true. We only guarantee if this is false, then the 172 * notification is not showing. 173 */ 174 private boolean mNotificationShown; 175 /** 176 * The number of continuous scans that must occur before consider the 177 * supplicant in a scanning state. This allows supplicant to associate with 178 * remembered networks that are in the scan results. 179 */ 180 private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3; 181 /** 182 * The number of scans since the last network state change. When this 183 * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the 184 * supplicant to actually be scanning. When the network state changes to 185 * something other than scanning, we reset this to 0. 186 */ 187 private int mNumScansSinceNetworkStateChange; 188 189 /** 190 * Temporary for computing UIDS that are responsible for starting WIFI. 191 * Protected by mWifiStateTracker lock. 192 */ 193 private final WorkSource mTmpWorkSource = new WorkSource(); 194 195 WifiService(Context context) { 196 mContext = context; 197 mWifiStateMachine = new WifiStateMachine(mContext); 198 mWifiStateMachine.enableRssiPolling(true); 199 mBatteryStats = BatteryStatsService.getService(); 200 201 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 202 Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); 203 mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0); 204 205 HandlerThread wifiThread = new HandlerThread("WifiService"); 206 wifiThread.start(); 207 208 mContext.registerReceiver( 209 new BroadcastReceiver() { 210 @Override 211 public void onReceive(Context context, Intent intent) { 212 // clear our flag indicating the user has overwridden airplane mode 213 mAirplaneModeOverwridden.set(false); 214 // on airplane disable, restore Wifi if the saved state indicates so 215 if (!isAirplaneModeOn() && testAndClearWifiSavedState()) { 216 persistWifiEnabled(true); 217 } 218 updateWifiState(); 219 } 220 }, 221 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); 222 223 mContext.registerReceiver( 224 new BroadcastReceiver() { 225 @Override 226 public void onReceive(Context context, Intent intent) { 227 228 ArrayList<String> available = intent.getStringArrayListExtra( 229 ConnectivityManager.EXTRA_AVAILABLE_TETHER); 230 ArrayList<String> active = intent.getStringArrayListExtra( 231 ConnectivityManager.EXTRA_ACTIVE_TETHER); 232 updateTetherState(available, active); 233 234 } 235 },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); 236 237 IntentFilter filter = new IntentFilter(); 238 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 239 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 240 filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 241 242 mContext.registerReceiver( 243 new BroadcastReceiver() { 244 @Override 245 public void onReceive(Context context, Intent intent) { 246 if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 247 // reset & clear notification on any wifi state change 248 resetNotification(); 249 } else if (intent.getAction().equals( 250 WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 251 mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( 252 WifiManager.EXTRA_NETWORK_INFO); 253 // reset & clear notification on a network connect & disconnect 254 switch(mNetworkInfo.getDetailedState()) { 255 case CONNECTED: 256 case DISCONNECTED: 257 resetNotification(); 258 break; 259 } 260 } else if (intent.getAction().equals( 261 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { 262 checkAndSetNotification(); 263 } 264 } 265 }, filter); 266 267 // Setting is in seconds 268 NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), 269 Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; 270 mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler()); 271 mNotificationEnabledSettingObserver.register(); 272 } 273 274 /** 275 * Check if Wi-Fi needs to be enabled and start 276 * if needed 277 * 278 * This function is used only at boot time 279 */ 280 public void checkAndStartWifi() { 281 /* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */ 282 boolean wifiEnabled = !isAirplaneModeOn() 283 && (getPersistedWifiEnabled() || testAndClearWifiSavedState()); 284 Slog.i(TAG, "WifiService starting up with Wi-Fi " + 285 (wifiEnabled ? "enabled" : "disabled")); 286 setWifiEnabled(wifiEnabled); 287 } 288 289 private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) { 290 291 boolean wifiTethered = false; 292 boolean wifiAvailable = false; 293 294 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 295 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); 296 297 if (mCm == null) { 298 mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 299 } 300 301 mWifiRegexs = mCm.getTetherableWifiRegexs(); 302 303 for (String intf : available) { 304 for (String regex : mWifiRegexs) { 305 if (intf.matches(regex)) { 306 307 InterfaceConfiguration ifcg = null; 308 try { 309 ifcg = service.getInterfaceConfig(intf); 310 if (ifcg != null) { 311 /* IP/netmask: 192.168.43.1/255.255.255.0 */ 312 ifcg.ipAddr = (192 << 24) + (168 << 16) + (43 << 8) + 1; 313 ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0; 314 ifcg.interfaceFlags = "up"; 315 316 service.setInterfaceConfig(intf, ifcg); 317 } 318 } catch (Exception e) { 319 Slog.e(TAG, "Error configuring interface " + intf + ", :" + e); 320 setWifiApEnabled(null, false); 321 return; 322 } 323 324 if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { 325 Slog.e(TAG, "Error tethering on " + intf); 326 setWifiApEnabled(null, false); 327 return; 328 } 329 break; 330 } 331 } 332 } 333 } 334 335 private boolean testAndClearWifiSavedState() { 336 final ContentResolver cr = mContext.getContentResolver(); 337 int wifiSavedState = 0; 338 try { 339 wifiSavedState = Settings.Secure.getInt(cr, Settings.Secure.WIFI_SAVED_STATE); 340 if(wifiSavedState == 1) 341 Settings.Secure.putInt(cr, Settings.Secure.WIFI_SAVED_STATE, 0); 342 } catch (Settings.SettingNotFoundException e) { 343 ; 344 } 345 return (wifiSavedState == 1); 346 } 347 348 private boolean getPersistedWifiEnabled() { 349 final ContentResolver cr = mContext.getContentResolver(); 350 try { 351 return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1; 352 } catch (Settings.SettingNotFoundException e) { 353 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0); 354 return false; 355 } 356 } 357 358 private void persistWifiEnabled(boolean enabled) { 359 final ContentResolver cr = mContext.getContentResolver(); 360 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0); 361 } 362 363 /** 364 * see {@link android.net.wifi.WifiManager#pingSupplicant()} 365 * @return {@code true} if the operation succeeds, {@code false} otherwise 366 */ 367 public boolean pingSupplicant() { 368 enforceAccessPermission(); 369 return mWifiStateMachine.syncPingSupplicant(); 370 } 371 372 /** 373 * see {@link android.net.wifi.WifiManager#startScan()} 374 */ 375 public void startScan(boolean forceActive) { 376 enforceChangePermission(); 377 mWifiStateMachine.startScan(forceActive); 378 } 379 380 private void enforceAccessPermission() { 381 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, 382 "WifiService"); 383 } 384 385 private void enforceChangePermission() { 386 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, 387 "WifiService"); 388 389 } 390 391 private void enforceMulticastChangePermission() { 392 mContext.enforceCallingOrSelfPermission( 393 android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, 394 "WifiService"); 395 } 396 397 /** 398 * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} 399 * @param enable {@code true} to enable, {@code false} to disable. 400 * @return {@code true} if the enable/disable operation was 401 * started or is already in the queue. 402 */ 403 public synchronized boolean setWifiEnabled(boolean enable) { 404 enforceChangePermission(); 405 406 if (DBG) { 407 Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n"); 408 } 409 410 // set a flag if the user is enabling Wifi while in airplane mode 411 if (enable && isAirplaneModeOn() && isAirplaneToggleable()) { 412 mAirplaneModeOverwridden.set(true); 413 } 414 415 if (enable) { 416 reportStartWorkSource(); 417 } 418 mWifiStateMachine.setWifiEnabled(enable); 419 420 /* 421 * Caller might not have WRITE_SECURE_SETTINGS, 422 * only CHANGE_WIFI_STATE is enforced 423 */ 424 long ident = Binder.clearCallingIdentity(); 425 persistWifiEnabled(enable); 426 Binder.restoreCallingIdentity(ident); 427 428 if (enable) { 429 if (!mIsReceiverRegistered) { 430 registerForBroadcasts(); 431 mIsReceiverRegistered = true; 432 } 433 } else if (mIsReceiverRegistered){ 434 mContext.unregisterReceiver(mReceiver); 435 mIsReceiverRegistered = false; 436 } 437 438 return true; 439 } 440 441 /** 442 * see {@link WifiManager#getWifiState()} 443 * @return One of {@link WifiManager#WIFI_STATE_DISABLED}, 444 * {@link WifiManager#WIFI_STATE_DISABLING}, 445 * {@link WifiManager#WIFI_STATE_ENABLED}, 446 * {@link WifiManager#WIFI_STATE_ENABLING}, 447 * {@link WifiManager#WIFI_STATE_UNKNOWN} 448 */ 449 public int getWifiEnabledState() { 450 enforceAccessPermission(); 451 return mWifiStateMachine.syncGetWifiState(); 452 } 453 454 /** 455 * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)} 456 * @param wifiConfig SSID, security and channel details as 457 * part of WifiConfiguration 458 * @param enabled true to enable and false to disable 459 * @return {@code true} if the start operation was 460 * started or is already in the queue. 461 */ 462 public synchronized boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { 463 enforceChangePermission(); 464 465 if (enabled) { 466 /* Use default config if there is no existing config */ 467 if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) { 468 wifiConfig = new WifiConfiguration(); 469 wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default); 470 wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE); 471 } 472 /* 473 * Caller might not have WRITE_SECURE_SETTINGS, 474 * only CHANGE_WIFI_STATE is enforced 475 */ 476 long ident = Binder.clearCallingIdentity(); 477 setWifiApConfiguration(wifiConfig); 478 Binder.restoreCallingIdentity(ident); 479 } 480 481 mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled); 482 483 return true; 484 } 485 486 /** 487 * see {@link WifiManager#getWifiApState()} 488 * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, 489 * {@link WifiManager#WIFI_AP_STATE_DISABLING}, 490 * {@link WifiManager#WIFI_AP_STATE_ENABLED}, 491 * {@link WifiManager#WIFI_AP_STATE_ENABLING}, 492 * {@link WifiManager#WIFI_AP_STATE_FAILED} 493 */ 494 public int getWifiApEnabledState() { 495 enforceAccessPermission(); 496 return mWifiStateMachine.syncGetWifiApState(); 497 } 498 499 /** 500 * see {@link WifiManager#getWifiApConfiguration()} 501 * @return soft access point configuration 502 */ 503 public synchronized WifiConfiguration getWifiApConfiguration() { 504 final ContentResolver cr = mContext.getContentResolver(); 505 WifiConfiguration wifiConfig = new WifiConfiguration(); 506 int authType; 507 try { 508 wifiConfig.SSID = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_SSID); 509 if (wifiConfig.SSID == null) 510 return null; 511 authType = Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_SECURITY); 512 wifiConfig.allowedKeyManagement.set(authType); 513 wifiConfig.preSharedKey = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_PASSWD); 514 return wifiConfig; 515 } catch (Settings.SettingNotFoundException e) { 516 Slog.e(TAG,"AP settings not found, returning"); 517 return null; 518 } 519 } 520 521 /** 522 * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)} 523 * @param wifiConfig WifiConfiguration details for soft access point 524 */ 525 public synchronized void setWifiApConfiguration(WifiConfiguration wifiConfig) { 526 enforceChangePermission(); 527 final ContentResolver cr = mContext.getContentResolver(); 528 boolean isWpa; 529 if (wifiConfig == null) 530 return; 531 Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_SSID, wifiConfig.SSID); 532 isWpa = wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK); 533 Settings.Secure.putInt(cr, 534 Settings.Secure.WIFI_AP_SECURITY, 535 isWpa ? KeyMgmt.WPA_PSK : KeyMgmt.NONE); 536 if (isWpa) 537 Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_PASSWD, wifiConfig.preSharedKey); 538 } 539 540 /** 541 * see {@link android.net.wifi.WifiManager#disconnect()} 542 */ 543 public void disconnect() { 544 enforceChangePermission(); 545 mWifiStateMachine.disconnectCommand(); 546 } 547 548 /** 549 * see {@link android.net.wifi.WifiManager#reconnect()} 550 */ 551 public void reconnect() { 552 enforceChangePermission(); 553 mWifiStateMachine.reconnectCommand(); 554 } 555 556 /** 557 * see {@link android.net.wifi.WifiManager#reassociate()} 558 */ 559 public void reassociate() { 560 enforceChangePermission(); 561 mWifiStateMachine.reassociateCommand(); 562 } 563 564 /** 565 * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()} 566 * @return the list of configured networks 567 */ 568 public List<WifiConfiguration> getConfiguredNetworks() { 569 enforceAccessPermission(); 570 return mWifiStateMachine.syncGetConfiguredNetworks(); 571 } 572 573 /** 574 * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)} 575 * @return the supplicant-assigned identifier for the new or updated 576 * network if the operation succeeds, or {@code -1} if it fails 577 */ 578 public int addOrUpdateNetwork(WifiConfiguration config) { 579 enforceChangePermission(); 580 return mWifiStateMachine.syncAddOrUpdateNetwork(config); 581 } 582 583 /** 584 * See {@link android.net.wifi.WifiManager#removeNetwork(int)} 585 * @param netId the integer that identifies the network configuration 586 * to the supplicant 587 * @return {@code true} if the operation succeeded 588 */ 589 public boolean removeNetwork(int netId) { 590 enforceChangePermission(); 591 return mWifiStateMachine.syncRemoveNetwork(netId); 592 } 593 594 /** 595 * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)} 596 * @param netId the integer that identifies the network configuration 597 * to the supplicant 598 * @param disableOthers if true, disable all other networks. 599 * @return {@code true} if the operation succeeded 600 */ 601 public boolean enableNetwork(int netId, boolean disableOthers) { 602 enforceChangePermission(); 603 return mWifiStateMachine.syncEnableNetwork(netId, disableOthers); 604 } 605 606 /** 607 * See {@link android.net.wifi.WifiManager#disableNetwork(int)} 608 * @param netId the integer that identifies the network configuration 609 * to the supplicant 610 * @return {@code true} if the operation succeeded 611 */ 612 public boolean disableNetwork(int netId) { 613 enforceChangePermission(); 614 return mWifiStateMachine.syncDisableNetwork(netId); 615 } 616 617 /** 618 * See {@link android.net.wifi.WifiManager#getConnectionInfo()} 619 * @return the Wi-Fi information, contained in {@link WifiInfo}. 620 */ 621 public WifiInfo getConnectionInfo() { 622 enforceAccessPermission(); 623 /* 624 * Make sure we have the latest information, by sending 625 * a status request to the supplicant. 626 */ 627 return mWifiStateMachine.syncRequestConnectionInfo(); 628 } 629 630 /** 631 * Return the results of the most recent access point scan, in the form of 632 * a list of {@link ScanResult} objects. 633 * @return the list of results 634 */ 635 public List<ScanResult> getScanResults() { 636 enforceAccessPermission(); 637 return mWifiStateMachine.syncGetScanResultsList(); 638 } 639 640 /** 641 * Tell the supplicant to persist the current list of configured networks. 642 * @return {@code true} if the operation succeeded 643 * 644 * TODO: deprecate this 645 */ 646 public boolean saveConfiguration() { 647 boolean result = true; 648 enforceChangePermission(); 649 return mWifiStateMachine.syncSaveConfig(); 650 } 651 652 /** 653 * Set the number of radio frequency channels that are allowed to be used 654 * in the current regulatory domain. This method should be used only 655 * if the correct number of channels cannot be determined automatically 656 * for some reason. If the operation is successful, the new value may be 657 * persisted as a Secure setting. 658 * @param numChannels the number of allowed channels. Must be greater than 0 659 * and less than or equal to 16. 660 * @param persist {@code true} if the setting should be remembered. 661 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., 662 * {@code numChannels} is outside the valid range. 663 */ 664 public synchronized boolean setNumAllowedChannels(int numChannels, boolean persist) { 665 Slog.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+ 666 " with persist set to "+persist); 667 enforceChangePermission(); 668 669 /* 670 * Validate the argument. We'd like to let the Wi-Fi driver do this, 671 * but if Wi-Fi isn't currently enabled, that's not possible, and 672 * we want to persist the setting anyway,so that it will take 673 * effect when Wi-Fi does become enabled. 674 */ 675 boolean found = false; 676 for (int validChan : sValidRegulatoryChannelCounts) { 677 if (validChan == numChannels) { 678 found = true; 679 break; 680 } 681 } 682 if (!found) { 683 return false; 684 } 685 686 if (persist) { 687 Settings.Secure.putInt(mContext.getContentResolver(), 688 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, 689 numChannels); 690 } 691 692 mWifiStateMachine.setNumAllowedChannels(numChannels); 693 694 return true; 695 } 696 697 /** 698 * Return the number of frequency channels that are allowed 699 * to be used in the current regulatory domain. 700 * @return the number of allowed channels, or {@code -1} if an error occurs 701 */ 702 public int getNumAllowedChannels() { 703 int numChannels; 704 705 enforceAccessPermission(); 706 707 /* 708 * If we can't get the value from the driver (e.g., because 709 * Wi-Fi is not currently enabled), get the value from 710 * Settings. 711 */ 712 numChannels = mWifiStateMachine.getNumAllowedChannels(); 713 if (numChannels < 0) { 714 numChannels = Settings.Secure.getInt(mContext.getContentResolver(), 715 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, 716 -1); 717 } 718 return numChannels; 719 } 720 721 /** 722 * Return the list of valid values for the number of allowed radio channels 723 * for various regulatory domains. 724 * @return the list of channel counts 725 */ 726 public int[] getValidChannelCounts() { 727 enforceAccessPermission(); 728 return sValidRegulatoryChannelCounts; 729 } 730 731 /** 732 * Return the DHCP-assigned addresses from the last successful DHCP request, 733 * if any. 734 * @return the DHCP information 735 */ 736 public DhcpInfo getDhcpInfo() { 737 enforceAccessPermission(); 738 return mWifiStateMachine.syncGetDhcpInfo(); 739 } 740 741 /** 742 * see {@link android.net.wifi.WifiManager#startWifi} 743 * 744 */ 745 public void startWifi() { 746 enforceChangePermission(); 747 /* TODO: may be add permissions for access only to connectivity service 748 * TODO: if a start issued, keep wifi alive until a stop issued irrespective 749 * of WifiLock & device idle status unless wifi enabled status is toggled 750 */ 751 752 mWifiStateMachine.setDriverStart(true); 753 mWifiStateMachine.reconnectCommand(); 754 } 755 756 /** 757 * see {@link android.net.wifi.WifiManager#stopWifi} 758 * 759 */ 760 public void stopWifi() { 761 enforceChangePermission(); 762 /* TODO: may be add permissions for access only to connectivity service 763 * TODO: if a stop is issued, wifi is brought up only by startWifi 764 * unless wifi enabled status is toggled 765 */ 766 mWifiStateMachine.setDriverStart(false); 767 } 768 769 770 /** 771 * see {@link android.net.wifi.WifiManager#addToBlacklist} 772 * 773 */ 774 public void addToBlacklist(String bssid) { 775 enforceChangePermission(); 776 777 mWifiStateMachine.addToBlacklist(bssid); 778 } 779 780 /** 781 * see {@link android.net.wifi.WifiManager#clearBlacklist} 782 * 783 */ 784 public void clearBlacklist() { 785 enforceChangePermission(); 786 787 mWifiStateMachine.clearBlacklist(); 788 } 789 790 public void connectNetworkWithId(int networkId) { 791 enforceChangePermission(); 792 mWifiStateMachine.connectNetwork(networkId); 793 } 794 795 public void connectNetworkWithConfig(WifiConfiguration config) { 796 enforceChangePermission(); 797 mWifiStateMachine.connectNetwork(config); 798 } 799 800 public void saveNetwork(WifiConfiguration config) { 801 enforceChangePermission(); 802 mWifiStateMachine.saveNetwork(config); 803 } 804 805 public void forgetNetwork(int netId) { 806 enforceChangePermission(); 807 mWifiStateMachine.forgetNetwork(netId); 808 } 809 810 public void startWpsPbc(String bssid) { 811 enforceChangePermission(); 812 mWifiStateMachine.startWpsPbc(bssid); 813 } 814 815 public void startWpsPin(String bssid, int apPin) { 816 enforceChangePermission(); 817 mWifiStateMachine.startWpsPin(bssid, apPin); 818 } 819 820 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 821 @Override 822 public void onReceive(Context context, Intent intent) { 823 String action = intent.getAction(); 824 825 long idleMillis = 826 Settings.Secure.getLong(mContext.getContentResolver(), 827 Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); 828 int stayAwakeConditions = 829 Settings.System.getInt(mContext.getContentResolver(), 830 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); 831 if (action.equals(Intent.ACTION_SCREEN_ON)) { 832 Slog.d(TAG, "ACTION_SCREEN_ON"); 833 mAlarmManager.cancel(mIdleIntent); 834 mDeviceIdle = false; 835 mScreenOff = false; 836 // Once the screen is on, we are not keeping WIFI running 837 // because of any locks so clear that tracking immediately. 838 reportStartWorkSource(); 839 mWifiStateMachine.enableRssiPolling(true); 840 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 841 Slog.d(TAG, "ACTION_SCREEN_OFF"); 842 mScreenOff = true; 843 mWifiStateMachine.enableRssiPolling(false); 844 /* 845 * Set a timer to put Wi-Fi to sleep, but only if the screen is off 846 * AND the "stay on while plugged in" setting doesn't match the 847 * current power conditions (i.e, not plugged in, plugged in to USB, 848 * or plugged in to AC). 849 */ 850 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) { 851 WifiInfo info = mWifiStateMachine.syncRequestConnectionInfo(); 852 if (info.getSupplicantState() != SupplicantState.COMPLETED) { 853 // we used to go to sleep immediately, but this caused some race conditions 854 // we don't have time to track down for this release. Delay instead, 855 // but not as long as we would if connected (below) 856 // TODO - fix the race conditions and switch back to the immediate turn-off 857 long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min 858 Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms"); 859 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); 860 // // do not keep Wifi awake when screen is off if Wifi is not associated 861 // mDeviceIdle = true; 862 // updateWifiState(); 863 } else { 864 long triggerTime = System.currentTimeMillis() + idleMillis; 865 Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms"); 866 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); 867 } 868 } 869 /* we can return now -- there's nothing to do until we get the idle intent back */ 870 return; 871 } else if (action.equals(ACTION_DEVICE_IDLE)) { 872 Slog.d(TAG, "got ACTION_DEVICE_IDLE"); 873 mDeviceIdle = true; 874 reportStartWorkSource(); 875 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 876 /* 877 * Set a timer to put Wi-Fi to sleep, but only if the screen is off 878 * AND we are transitioning from a state in which the device was supposed 879 * to stay awake to a state in which it is not supposed to stay awake. 880 * If "stay awake" state is not changing, we do nothing, to avoid resetting 881 * the already-set timer. 882 */ 883 int pluggedType = intent.getIntExtra("plugged", 0); 884 Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType); 885 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) && 886 !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) { 887 long triggerTime = System.currentTimeMillis() + idleMillis; 888 Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms"); 889 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); 890 mPluggedType = pluggedType; 891 return; 892 } 893 mPluggedType = pluggedType; 894 } else if (action.equals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)) { 895 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 896 BluetoothA2dp.STATE_NOT_PLAYING); 897 mWifiStateMachine.setBluetoothScanMode(state == BluetoothA2dp.STATE_PLAYING); 898 } else { 899 return; 900 } 901 902 updateWifiState(); 903 } 904 905 /** 906 * Determines whether the Wi-Fi chipset should stay awake or be put to 907 * sleep. Looks at the setting for the sleep policy and the current 908 * conditions. 909 * 910 * @see #shouldDeviceStayAwake(int, int) 911 */ 912 private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) { 913 int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(), 914 Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT); 915 916 if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) { 917 // Never sleep 918 return true; 919 } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) && 920 (pluggedType != 0)) { 921 // Never sleep while plugged, and we're plugged 922 return true; 923 } else { 924 // Default 925 return shouldDeviceStayAwake(stayAwakeConditions, pluggedType); 926 } 927 } 928 929 /** 930 * Determine whether the bit value corresponding to {@code pluggedType} is set in 931 * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value 932 * of {@code 0} isn't really a plugged type, but rather an indication that the 933 * device isn't plugged in at all, there is no bit value corresponding to a 934 * {@code pluggedType} value of {@code 0}. That is why we shift by 935 * {@code pluggedType - 1} instead of by {@code pluggedType}. 936 * @param stayAwakeConditions a bit string specifying which "plugged types" should 937 * keep the device (and hence Wi-Fi) awake. 938 * @param pluggedType the type of plug (USB, AC, or none) for which the check is 939 * being made 940 * @return {@code true} if {@code pluggedType} indicates that the device is 941 * supposed to stay awake, {@code false} otherwise. 942 */ 943 private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) { 944 return (stayAwakeConditions & pluggedType) != 0; 945 } 946 }; 947 948 private synchronized void reportStartWorkSource() { 949 mTmpWorkSource.clear(); 950 if (mDeviceIdle) { 951 for (int i=0; i<mLocks.mList.size(); i++) { 952 mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource); 953 } 954 } 955 mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource); 956 } 957 958 private void updateWifiState() { 959 boolean wifiEnabled = getPersistedWifiEnabled(); 960 boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden.get(); 961 boolean lockHeld = mLocks.hasLocks(); 962 int strongestLockMode; 963 boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode; 964 boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld; 965 if (mDeviceIdle && lockHeld) { 966 strongestLockMode = mLocks.getStrongestLockMode(); 967 } else { 968 strongestLockMode = WifiManager.WIFI_MODE_FULL; 969 } 970 971 /* Disable tethering when airplane mode is enabled */ 972 if (airplaneMode) { 973 mWifiStateMachine.setWifiApEnabled(null, false); 974 } 975 976 if (wifiShouldBeEnabled) { 977 if (wifiShouldBeStarted) { 978 reportStartWorkSource(); 979 mWifiStateMachine.setWifiEnabled(true); 980 mWifiStateMachine.setScanOnlyMode( 981 strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY); 982 mWifiStateMachine.setDriverStart(true); 983 } else { 984 mWifiStateMachine.requestCmWakeLock(); 985 mWifiStateMachine.setDriverStart(false); 986 } 987 } else { 988 mWifiStateMachine.setWifiEnabled(false); 989 } 990 } 991 992 private void registerForBroadcasts() { 993 IntentFilter intentFilter = new IntentFilter(); 994 intentFilter.addAction(Intent.ACTION_SCREEN_ON); 995 intentFilter.addAction(Intent.ACTION_SCREEN_OFF); 996 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); 997 intentFilter.addAction(ACTION_DEVICE_IDLE); 998 intentFilter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED); 999 mContext.registerReceiver(mReceiver, intentFilter); 1000 } 1001 1002 private boolean isAirplaneSensitive() { 1003 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(), 1004 Settings.System.AIRPLANE_MODE_RADIOS); 1005 return airplaneModeRadios == null 1006 || airplaneModeRadios.contains(Settings.System.RADIO_WIFI); 1007 } 1008 1009 private boolean isAirplaneToggleable() { 1010 String toggleableRadios = Settings.System.getString(mContext.getContentResolver(), 1011 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS); 1012 return toggleableRadios != null 1013 && toggleableRadios.contains(Settings.System.RADIO_WIFI); 1014 } 1015 1016 /** 1017 * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is 1018 * currently on. 1019 * @return {@code true} if airplane mode is on. 1020 */ 1021 private boolean isAirplaneModeOn() { 1022 return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(), 1023 Settings.System.AIRPLANE_MODE_ON, 0) == 1; 1024 } 1025 1026 @Override 1027 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1028 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1029 != PackageManager.PERMISSION_GRANTED) { 1030 pw.println("Permission Denial: can't dump WifiService from from pid=" 1031 + Binder.getCallingPid() 1032 + ", uid=" + Binder.getCallingUid()); 1033 return; 1034 } 1035 pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName()); 1036 pw.println("Stay-awake conditions: " + 1037 Settings.System.getInt(mContext.getContentResolver(), 1038 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0)); 1039 pw.println(); 1040 1041 pw.println("Internal state:"); 1042 pw.println(mWifiStateMachine); 1043 pw.println(); 1044 pw.println("Latest scan results:"); 1045 List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList(); 1046 if (scanResults != null && scanResults.size() != 0) { 1047 pw.println(" BSSID Frequency RSSI Flags SSID"); 1048 for (ScanResult r : scanResults) { 1049 pw.printf(" %17s %9d %5d %-16s %s%n", 1050 r.BSSID, 1051 r.frequency, 1052 r.level, 1053 r.capabilities, 1054 r.SSID == null ? "" : r.SSID); 1055 } 1056 } 1057 pw.println(); 1058 pw.println("Locks acquired: " + mFullLocksAcquired + " full, " + 1059 mScanLocksAcquired + " scan"); 1060 pw.println("Locks released: " + mFullLocksReleased + " full, " + 1061 mScanLocksReleased + " scan"); 1062 pw.println(); 1063 pw.println("Locks held:"); 1064 mLocks.dump(pw); 1065 } 1066 1067 private class WifiLock extends DeathRecipient { 1068 WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { 1069 super(lockMode, tag, binder, ws); 1070 } 1071 1072 public void binderDied() { 1073 synchronized (mLocks) { 1074 releaseWifiLockLocked(mBinder); 1075 } 1076 } 1077 1078 public String toString() { 1079 return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}"; 1080 } 1081 } 1082 1083 private class LockList { 1084 private List<WifiLock> mList; 1085 1086 private LockList() { 1087 mList = new ArrayList<WifiLock>(); 1088 } 1089 1090 private synchronized boolean hasLocks() { 1091 return !mList.isEmpty(); 1092 } 1093 1094 private synchronized int getStrongestLockMode() { 1095 if (mList.isEmpty()) { 1096 return WifiManager.WIFI_MODE_FULL; 1097 } 1098 for (WifiLock l : mList) { 1099 if (l.mMode == WifiManager.WIFI_MODE_FULL) { 1100 return WifiManager.WIFI_MODE_FULL; 1101 } 1102 } 1103 return WifiManager.WIFI_MODE_SCAN_ONLY; 1104 } 1105 1106 private void addLock(WifiLock lock) { 1107 if (findLockByBinder(lock.mBinder) < 0) { 1108 mList.add(lock); 1109 } 1110 } 1111 1112 private WifiLock removeLock(IBinder binder) { 1113 int index = findLockByBinder(binder); 1114 if (index >= 0) { 1115 WifiLock ret = mList.remove(index); 1116 ret.unlinkDeathRecipient(); 1117 return ret; 1118 } else { 1119 return null; 1120 } 1121 } 1122 1123 private int findLockByBinder(IBinder binder) { 1124 int size = mList.size(); 1125 for (int i = size - 1; i >= 0; i--) 1126 if (mList.get(i).mBinder == binder) 1127 return i; 1128 return -1; 1129 } 1130 1131 private void dump(PrintWriter pw) { 1132 for (WifiLock l : mList) { 1133 pw.print(" "); 1134 pw.println(l); 1135 } 1136 } 1137 } 1138 1139 void enforceWakeSourcePermission(int uid, int pid) { 1140 if (uid == android.os.Process.myUid()) { 1141 return; 1142 } 1143 mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, 1144 pid, uid, null); 1145 } 1146 1147 public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) { 1148 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); 1149 if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) { 1150 return false; 1151 } 1152 if (ws != null) { 1153 enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid()); 1154 } 1155 if (ws != null && ws.size() == 0) { 1156 ws = null; 1157 } 1158 if (ws == null) { 1159 ws = new WorkSource(Binder.getCallingUid()); 1160 } 1161 WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws); 1162 synchronized (mLocks) { 1163 return acquireWifiLockLocked(wifiLock); 1164 } 1165 } 1166 1167 private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException { 1168 switch(wifiLock.mMode) { 1169 case WifiManager.WIFI_MODE_FULL: 1170 mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource); 1171 break; 1172 case WifiManager.WIFI_MODE_SCAN_ONLY: 1173 mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource); 1174 break; 1175 } 1176 } 1177 1178 private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException { 1179 switch(wifiLock.mMode) { 1180 case WifiManager.WIFI_MODE_FULL: 1181 mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource); 1182 break; 1183 case WifiManager.WIFI_MODE_SCAN_ONLY: 1184 mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource); 1185 break; 1186 } 1187 } 1188 1189 private boolean acquireWifiLockLocked(WifiLock wifiLock) { 1190 if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock); 1191 1192 mLocks.addLock(wifiLock); 1193 1194 long ident = Binder.clearCallingIdentity(); 1195 try { 1196 noteAcquireWifiLock(wifiLock); 1197 switch(wifiLock.mMode) { 1198 case WifiManager.WIFI_MODE_FULL: 1199 ++mFullLocksAcquired; 1200 break; 1201 case WifiManager.WIFI_MODE_SCAN_ONLY: 1202 ++mScanLocksAcquired; 1203 break; 1204 } 1205 } catch (RemoteException e) { 1206 } finally { 1207 Binder.restoreCallingIdentity(ident); 1208 } 1209 1210 // Be aggressive about adding new locks into the accounted state... 1211 // we want to over-report rather than under-report. 1212 reportStartWorkSource(); 1213 1214 updateWifiState(); 1215 return true; 1216 } 1217 1218 public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) { 1219 int uid = Binder.getCallingUid(); 1220 int pid = Binder.getCallingPid(); 1221 if (ws != null && ws.size() == 0) { 1222 ws = null; 1223 } 1224 if (ws != null) { 1225 enforceWakeSourcePermission(uid, pid); 1226 } 1227 long ident = Binder.clearCallingIdentity(); 1228 try { 1229 synchronized (mLocks) { 1230 int index = mLocks.findLockByBinder(lock); 1231 if (index < 0) { 1232 throw new IllegalArgumentException("Wifi lock not active"); 1233 } 1234 WifiLock wl = mLocks.mList.get(index); 1235 noteReleaseWifiLock(wl); 1236 wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid); 1237 noteAcquireWifiLock(wl); 1238 } 1239 } catch (RemoteException e) { 1240 } finally { 1241 Binder.restoreCallingIdentity(ident); 1242 } 1243 } 1244 1245 public boolean releaseWifiLock(IBinder lock) { 1246 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); 1247 synchronized (mLocks) { 1248 return releaseWifiLockLocked(lock); 1249 } 1250 } 1251 1252 private boolean releaseWifiLockLocked(IBinder lock) { 1253 boolean hadLock; 1254 1255 WifiLock wifiLock = mLocks.removeLock(lock); 1256 1257 if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock); 1258 1259 hadLock = (wifiLock != null); 1260 1261 if (hadLock) { 1262 long ident = Binder.clearCallingIdentity(); 1263 try { 1264 noteAcquireWifiLock(wifiLock); 1265 switch(wifiLock.mMode) { 1266 case WifiManager.WIFI_MODE_FULL: 1267 ++mFullLocksReleased; 1268 break; 1269 case WifiManager.WIFI_MODE_SCAN_ONLY: 1270 ++mScanLocksReleased; 1271 break; 1272 } 1273 } catch (RemoteException e) { 1274 } finally { 1275 Binder.restoreCallingIdentity(ident); 1276 } 1277 } 1278 // TODO - should this only happen if you hadLock? 1279 updateWifiState(); 1280 return hadLock; 1281 } 1282 1283 private abstract class DeathRecipient 1284 implements IBinder.DeathRecipient { 1285 String mTag; 1286 int mMode; 1287 IBinder mBinder; 1288 WorkSource mWorkSource; 1289 1290 DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) { 1291 super(); 1292 mTag = tag; 1293 mMode = mode; 1294 mBinder = binder; 1295 mWorkSource = ws; 1296 try { 1297 mBinder.linkToDeath(this, 0); 1298 } catch (RemoteException e) { 1299 binderDied(); 1300 } 1301 } 1302 1303 void unlinkDeathRecipient() { 1304 mBinder.unlinkToDeath(this, 0); 1305 } 1306 } 1307 1308 private class Multicaster extends DeathRecipient { 1309 Multicaster(String tag, IBinder binder) { 1310 super(Binder.getCallingUid(), tag, binder, null); 1311 } 1312 1313 public void binderDied() { 1314 Slog.e(TAG, "Multicaster binderDied"); 1315 synchronized (mMulticasters) { 1316 int i = mMulticasters.indexOf(this); 1317 if (i != -1) { 1318 removeMulticasterLocked(i, mMode); 1319 } 1320 } 1321 } 1322 1323 public String toString() { 1324 return "Multicaster{" + mTag + " binder=" + mBinder + "}"; 1325 } 1326 1327 public int getUid() { 1328 return mMode; 1329 } 1330 } 1331 1332 public void initializeMulticastFiltering() { 1333 enforceMulticastChangePermission(); 1334 1335 synchronized (mMulticasters) { 1336 // if anybody had requested filters be off, leave off 1337 if (mMulticasters.size() != 0) { 1338 return; 1339 } else { 1340 mWifiStateMachine.startPacketFiltering(); 1341 } 1342 } 1343 } 1344 1345 public void acquireMulticastLock(IBinder binder, String tag) { 1346 enforceMulticastChangePermission(); 1347 1348 synchronized (mMulticasters) { 1349 mMulticastEnabled++; 1350 mMulticasters.add(new Multicaster(tag, binder)); 1351 // Note that we could call stopPacketFiltering only when 1352 // our new size == 1 (first call), but this function won't 1353 // be called often and by making the stopPacket call each 1354 // time we're less fragile and self-healing. 1355 mWifiStateMachine.stopPacketFiltering(); 1356 } 1357 1358 int uid = Binder.getCallingUid(); 1359 Long ident = Binder.clearCallingIdentity(); 1360 try { 1361 mBatteryStats.noteWifiMulticastEnabled(uid); 1362 } catch (RemoteException e) { 1363 } finally { 1364 Binder.restoreCallingIdentity(ident); 1365 } 1366 } 1367 1368 public void releaseMulticastLock() { 1369 enforceMulticastChangePermission(); 1370 1371 int uid = Binder.getCallingUid(); 1372 synchronized (mMulticasters) { 1373 mMulticastDisabled++; 1374 int size = mMulticasters.size(); 1375 for (int i = size - 1; i >= 0; i--) { 1376 Multicaster m = mMulticasters.get(i); 1377 if ((m != null) && (m.getUid() == uid)) { 1378 removeMulticasterLocked(i, uid); 1379 } 1380 } 1381 } 1382 } 1383 1384 private void removeMulticasterLocked(int i, int uid) 1385 { 1386 Multicaster removed = mMulticasters.remove(i); 1387 1388 if (removed != null) { 1389 removed.unlinkDeathRecipient(); 1390 } 1391 if (mMulticasters.size() == 0) { 1392 mWifiStateMachine.startPacketFiltering(); 1393 } 1394 1395 Long ident = Binder.clearCallingIdentity(); 1396 try { 1397 mBatteryStats.noteWifiMulticastDisabled(uid); 1398 } catch (RemoteException e) { 1399 } finally { 1400 Binder.restoreCallingIdentity(ident); 1401 } 1402 } 1403 1404 public boolean isMulticastEnabled() { 1405 enforceAccessPermission(); 1406 1407 synchronized (mMulticasters) { 1408 return (mMulticasters.size() > 0); 1409 } 1410 } 1411 1412 private void checkAndSetNotification() { 1413 // If we shouldn't place a notification on available networks, then 1414 // don't bother doing any of the following 1415 if (!mNotificationEnabled) return; 1416 1417 State state = mNetworkInfo.getState(); 1418 if ((state == NetworkInfo.State.DISCONNECTED) 1419 || (state == NetworkInfo.State.UNKNOWN)) { 1420 // Look for an open network 1421 List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList(); 1422 if (scanResults != null) { 1423 int numOpenNetworks = 0; 1424 for (int i = scanResults.size() - 1; i >= 0; i--) { 1425 ScanResult scanResult = scanResults.get(i); 1426 1427 if (TextUtils.isEmpty(scanResult.capabilities)) { 1428 numOpenNetworks++; 1429 } 1430 } 1431 1432 if (numOpenNetworks > 0) { 1433 if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) { 1434 /* 1435 * We've scanned continuously at least 1436 * NUM_SCANS_BEFORE_NOTIFICATION times. The user 1437 * probably does not have a remembered network in range, 1438 * since otherwise supplicant would have tried to 1439 * associate and thus resetting this counter. 1440 */ 1441 setNotificationVisible(true, numOpenNetworks, false, 0); 1442 } 1443 return; 1444 } 1445 } 1446 } 1447 1448 // No open networks in range, remove the notification 1449 setNotificationVisible(false, 0, false, 0); 1450 } 1451 1452 /** 1453 * Clears variables related to tracking whether a notification has been 1454 * shown recently and clears the current notification. 1455 */ 1456 private void resetNotification() { 1457 mNotificationRepeatTime = 0; 1458 mNumScansSinceNetworkStateChange = 0; 1459 setNotificationVisible(false, 0, false, 0); 1460 } 1461 1462 /** 1463 * Display or don't display a notification that there are open Wi-Fi networks. 1464 * @param visible {@code true} if notification should be visible, {@code false} otherwise 1465 * @param numNetworks the number networks seen 1466 * @param force {@code true} to force notification to be shown/not-shown, 1467 * even if it is already shown/not-shown. 1468 * @param delay time in milliseconds after which the notification should be made 1469 * visible or invisible. 1470 */ 1471 private void setNotificationVisible(boolean visible, int numNetworks, boolean force, 1472 int delay) { 1473 1474 // Since we use auto cancel on the notification, when the 1475 // mNetworksAvailableNotificationShown is true, the notification may 1476 // have actually been canceled. However, when it is false we know 1477 // for sure that it is not being shown (it will not be shown any other 1478 // place than here) 1479 1480 // If it should be hidden and it is already hidden, then noop 1481 if (!visible && !mNotificationShown && !force) { 1482 return; 1483 } 1484 1485 NotificationManager notificationManager = (NotificationManager) mContext 1486 .getSystemService(Context.NOTIFICATION_SERVICE); 1487 1488 Message message; 1489 if (visible) { 1490 1491 // Not enough time has passed to show the notification again 1492 if (System.currentTimeMillis() < mNotificationRepeatTime) { 1493 return; 1494 } 1495 1496 if (mNotification == null) { 1497 // Cache the Notification object. 1498 mNotification = new Notification(); 1499 mNotification.when = 0; 1500 mNotification.icon = ICON_NETWORKS_AVAILABLE; 1501 mNotification.flags = Notification.FLAG_AUTO_CANCEL; 1502 mNotification.contentIntent = PendingIntent.getActivity(mContext, 0, 1503 new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0); 1504 } 1505 1506 CharSequence title = mContext.getResources().getQuantityText( 1507 com.android.internal.R.plurals.wifi_available, numNetworks); 1508 CharSequence details = mContext.getResources().getQuantityText( 1509 com.android.internal.R.plurals.wifi_available_detailed, numNetworks); 1510 mNotification.tickerText = title; 1511 mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent); 1512 1513 mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS; 1514 1515 notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification); 1516 } else { 1517 notificationManager.cancel(ICON_NETWORKS_AVAILABLE); 1518 } 1519 1520 mNotificationShown = visible; 1521 } 1522 1523 private class NotificationEnabledSettingObserver extends ContentObserver { 1524 1525 public NotificationEnabledSettingObserver(Handler handler) { 1526 super(handler); 1527 } 1528 1529 public void register() { 1530 ContentResolver cr = mContext.getContentResolver(); 1531 cr.registerContentObserver(Settings.Secure.getUriFor( 1532 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this); 1533 mNotificationEnabled = getValue(); 1534 } 1535 1536 @Override 1537 public void onChange(boolean selfChange) { 1538 super.onChange(selfChange); 1539 1540 mNotificationEnabled = getValue(); 1541 resetNotification(); 1542 } 1543 1544 private boolean getValue() { 1545 return Settings.Secure.getInt(mContext.getContentResolver(), 1546 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; 1547 } 1548 } 1549 1550 1551} 1552