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