WifiService.java revision 07a2295a4dbce33f1913f942fa9733b016ab398a
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.wifi; 18 19import android.app.ActivityManager; 20import android.app.AppOpsManager; 21import android.bluetooth.BluetoothAdapter; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.pm.PackageManager; 27import android.database.ContentObserver; 28import android.net.wifi.IWifiManager; 29import android.net.wifi.ScanResult; 30import android.net.wifi.WifiInfo; 31import android.net.wifi.WifiManager; 32import android.net.wifi.WifiStateMachine; 33import android.net.wifi.WifiConfiguration; 34import android.net.wifi.WifiWatchdogStateMachine; 35import android.net.DhcpInfo; 36import android.net.DhcpResults; 37import android.net.LinkAddress; 38import android.net.NetworkUtils; 39import android.net.RouteInfo; 40import android.os.Binder; 41import android.os.Handler; 42import android.os.Messenger; 43import android.os.HandlerThread; 44import android.os.IBinder; 45import android.os.INetworkManagementService; 46import android.os.Message; 47import android.os.RemoteException; 48import android.os.SystemProperties; 49import android.os.UserHandle; 50import android.os.WorkSource; 51import android.provider.Settings; 52import android.util.Log; 53import android.util.Slog; 54 55import java.io.FileDescriptor; 56import java.io.PrintWriter; 57import java.net.InetAddress; 58import java.net.Inet4Address; 59import java.util.ArrayList; 60import java.util.List; 61import java.util.concurrent.atomic.AtomicBoolean; 62 63import com.android.internal.R; 64import com.android.internal.app.IBatteryStats; 65import com.android.internal.telephony.TelephonyIntents; 66import com.android.internal.util.AsyncChannel; 67import com.android.server.am.BatteryStatsService; 68import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED; 69import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED; 70import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED; 71import static com.android.server.wifi.WifiController.CMD_LOCKS_CHANGED; 72import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED; 73import static com.android.server.wifi.WifiController.CMD_SCREEN_OFF; 74import static com.android.server.wifi.WifiController.CMD_SCREEN_ON; 75import static com.android.server.wifi.WifiController.CMD_SET_AP; 76import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; 77/** 78 * WifiService handles remote WiFi operation requests by implementing 79 * the IWifiManager interface. 80 * 81 * @hide 82 */ 83public final class WifiService extends IWifiManager.Stub { 84 private static final String TAG = "WifiService"; 85 private static final boolean DBG = false; 86 87 final WifiStateMachine mWifiStateMachine; 88 89 private final Context mContext; 90 91 final LockList mLocks = new LockList(); 92 // some wifi lock statistics 93 private int mFullHighPerfLocksAcquired; 94 private int mFullHighPerfLocksReleased; 95 private int mFullLocksAcquired; 96 private int mFullLocksReleased; 97 private int mScanLocksAcquired; 98 private int mScanLocksReleased; 99 100 private final List<Multicaster> mMulticasters = 101 new ArrayList<Multicaster>(); 102 private int mMulticastEnabled; 103 private int mMulticastDisabled; 104 105 private AtomicBoolean mDeviceProvisioned = new AtomicBoolean(); 106 private AtomicBoolean mNotifyScanMode = new AtomicBoolean(); 107 108 private final IBatteryStats mBatteryStats; 109 private final AppOpsManager mAppOps; 110 111 private String mInterfaceName; 112 113 /* Tracks the open wi-fi network notification */ 114 private WifiNotificationController mNotificationController; 115 /* Polls traffic stats and notifies clients */ 116 private WifiTrafficPoller mTrafficPoller; 117 /* Tracks the persisted states for wi-fi & airplane mode */ 118 final WifiSettingsStore mSettingsStore; 119 120 /* The work source (UID) that triggered the current WIFI scan, synchronized 121 * on this */ 122 private WorkSource mScanWorkSource; 123 124 private boolean mIsReceiverRegistered = false; 125 126 /** 127 * Asynchronous channel to WifiStateMachine 128 */ 129 private AsyncChannel mWifiStateMachineChannel; 130 131 /** 132 * Handles client connections 133 */ 134 private class ClientHandler extends Handler { 135 136 ClientHandler(android.os.Looper looper) { 137 super(looper); 138 } 139 140 @Override 141 public void handleMessage(Message msg) { 142 switch (msg.what) { 143 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { 144 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 145 if (DBG) Slog.d(TAG, "New client listening to asynchronous messages"); 146 // We track the clients by the Messenger 147 // since it is expected to be always available 148 mTrafficPoller.addClient(msg.replyTo); 149 } else { 150 Slog.e(TAG, "Client connection failure, error=" + msg.arg1); 151 } 152 break; 153 } 154 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { 155 if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { 156 if (DBG) Slog.d(TAG, "Send failed, client connection lost"); 157 } else { 158 if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); 159 } 160 mTrafficPoller.removeClient(msg.replyTo); 161 break; 162 } 163 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { 164 AsyncChannel ac = new AsyncChannel(); 165 ac.connect(mContext, this, msg.replyTo); 166 break; 167 } 168 /* Client commands are forwarded to state machine */ 169 case WifiManager.CONNECT_NETWORK: 170 case WifiManager.SAVE_NETWORK: 171 case WifiManager.FORGET_NETWORK: 172 case WifiManager.START_WPS: 173 case WifiManager.CANCEL_WPS: 174 case WifiManager.DISABLE_NETWORK: 175 case WifiManager.RSSI_PKTCNT_FETCH: { 176 mWifiStateMachine.sendMessage(Message.obtain(msg)); 177 break; 178 } 179 default: { 180 Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg); 181 break; 182 } 183 } 184 } 185 } 186 private ClientHandler mClientHandler; 187 188 /** 189 * Handles interaction with WifiStateMachine 190 */ 191 private class WifiStateMachineHandler extends Handler { 192 private AsyncChannel mWsmChannel; 193 194 WifiStateMachineHandler(android.os.Looper looper) { 195 super(looper); 196 mWsmChannel = new AsyncChannel(); 197 mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler()); 198 } 199 200 @Override 201 public void handleMessage(Message msg) { 202 switch (msg.what) { 203 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { 204 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 205 mWifiStateMachineChannel = mWsmChannel; 206 } else { 207 Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1); 208 mWifiStateMachineChannel = null; 209 } 210 break; 211 } 212 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { 213 Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1); 214 mWifiStateMachineChannel = null; 215 //Re-establish connection to state machine 216 mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler()); 217 break; 218 } 219 default: { 220 Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg); 221 break; 222 } 223 } 224 } 225 } 226 WifiStateMachineHandler mWifiStateMachineHandler; 227 228 private WifiWatchdogStateMachine mWifiWatchdogStateMachine; 229 230 public WifiService(Context context) { 231 mContext = context; 232 233 mInterfaceName = SystemProperties.get("wifi.interface", "wlan0"); 234 235 mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName); 236 mWifiStateMachine.enableRssiPolling(true); 237 mBatteryStats = BatteryStatsService.getService(); 238 mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); 239 240 mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine); 241 mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName); 242 mSettingsStore = new WifiSettingsStore(mContext); 243 244 HandlerThread wifiThread = new HandlerThread("WifiService"); 245 wifiThread.start(); 246 mClientHandler = new ClientHandler(wifiThread.getLooper()); 247 mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper()); 248 mWifiController = new WifiController(mContext, this, wifiThread.getLooper()); 249 mWifiController.start(); 250 251 registerForScanModeChange(); 252 registerForDeviceProvisionedChange(); 253 registerForNotifyUserOnScanModeChange(); 254 mContext.registerReceiver( 255 new BroadcastReceiver() { 256 @Override 257 public void onReceive(Context context, Intent intent) { 258 if (mSettingsStore.handleAirplaneModeToggled()) { 259 mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED); 260 } 261 } 262 }, 263 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); 264 265 mContext.registerReceiver( 266 new BroadcastReceiver() { 267 @Override 268 public void onReceive(Context context, Intent intent) { 269 if (intent.getAction().equals( 270 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { 271 noteScanEnd(); 272 } 273 } 274 }, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); 275 } 276 277 private WifiController mWifiController; 278 279 /** Tell battery stats about a new WIFI scan */ 280 private void noteScanStart() { 281 WorkSource scanWorkSource = null; 282 synchronized (WifiService.this) { 283 if (mScanWorkSource != null) { 284 // Scan already in progress, don't add this one to battery stats 285 return; 286 } 287 scanWorkSource = new WorkSource(Binder.getCallingUid()); 288 mScanWorkSource = scanWorkSource; 289 } 290 291 long id = Binder.clearCallingIdentity(); 292 try { 293 mBatteryStats.noteWifiScanStartedFromSource(scanWorkSource); 294 } catch (RemoteException e) { 295 Log.w(TAG, e); 296 } finally { 297 Binder.restoreCallingIdentity(id); 298 } 299 } 300 301 /** Tell battery stats that the current WIFI scan has completed */ 302 private void noteScanEnd() { 303 WorkSource scanWorkSource = null; 304 synchronized (WifiService.this) { 305 scanWorkSource = mScanWorkSource; 306 mScanWorkSource = null; 307 } 308 if (scanWorkSource != null) { 309 try { 310 mBatteryStats.noteWifiScanStoppedFromSource(scanWorkSource); 311 } catch (RemoteException e) { 312 Log.w(TAG, e); 313 } 314 } 315 } 316 317 /** 318 * Check if Wi-Fi needs to be enabled and start 319 * if needed 320 * 321 * This function is used only at boot time 322 */ 323 public void checkAndStartWifi() { 324 /* Check if wi-fi needs to be enabled */ 325 boolean wifiEnabled = mSettingsStore.isWifiToggleEnabled(); 326 Slog.i(TAG, "WifiService starting up with Wi-Fi " + 327 (wifiEnabled ? "enabled" : "disabled")); 328 329 // If we are already disabled (could be due to airplane mode), avoid changing persist 330 // state here 331 if (wifiEnabled) setWifiEnabled(wifiEnabled); 332 333 mWifiWatchdogStateMachine = WifiWatchdogStateMachine. 334 makeWifiWatchdogStateMachine(mContext); 335 336 } 337 338 /** 339 * see {@link android.net.wifi.WifiManager#pingSupplicant()} 340 * @return {@code true} if the operation succeeds, {@code false} otherwise 341 */ 342 public boolean pingSupplicant() { 343 enforceAccessPermission(); 344 if (mWifiStateMachineChannel != null) { 345 return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel); 346 } else { 347 Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); 348 return false; 349 } 350 } 351 352 /** 353 * see {@link android.net.wifi.WifiManager#startScan()} 354 */ 355 public void startScan() { 356 enforceChangePermission(); 357 mWifiStateMachine.startScan(); 358 noteScanStart(); 359 } 360 361 private void enforceAccessPermission() { 362 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, 363 "WifiService"); 364 } 365 366 private void enforceChangePermission() { 367 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, 368 "WifiService"); 369 370 } 371 372 private void enforceMulticastChangePermission() { 373 mContext.enforceCallingOrSelfPermission( 374 android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, 375 "WifiService"); 376 } 377 378 private void enforceConnectivityInternalPermission() { 379 mContext.enforceCallingOrSelfPermission( 380 android.Manifest.permission.CONNECTIVITY_INTERNAL, 381 "ConnectivityService"); 382 } 383 384 /** 385 * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} 386 * @param enable {@code true} to enable, {@code false} to disable. 387 * @return {@code true} if the enable/disable operation was 388 * started or is already in the queue. 389 */ 390 public synchronized boolean setWifiEnabled(boolean enable) { 391 enforceChangePermission(); 392 Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid() 393 + ", uid=" + Binder.getCallingUid()); 394 if (DBG) { 395 Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n"); 396 } 397 398 /* 399 * Caller might not have WRITE_SECURE_SETTINGS, 400 * only CHANGE_WIFI_STATE is enforced 401 */ 402 403 long ident = Binder.clearCallingIdentity(); 404 try { 405 406 /* Turning off Wi-Fi when scans are still available */ 407 if (!enable && isScanningAlwaysAvailable()) { 408 /* Notify if device is provisioned and user has not opted out of the notification */ 409 if (mNotifyScanMode.get() && mDeviceProvisioned.get()) { 410 Intent intent = new Intent(WifiManager.ACTION_NOTIFY_SCAN_ALWAYS_AVAILABLE); 411 mContext.startActivityAsUser(intent, null, UserHandle.CURRENT); 412 } 413 } 414 415 if (! mSettingsStore.handleWifiToggled(enable)) { 416 // Nothing to do if wifi cannot be toggled 417 return true; 418 } 419 } finally { 420 Binder.restoreCallingIdentity(ident); 421 } 422 423 mWifiController.sendMessage(CMD_WIFI_TOGGLED); 424 425 if (enable) { 426 if (!mIsReceiverRegistered) { 427 registerForBroadcasts(); 428 mIsReceiverRegistered = true; 429 } 430 } else if (mIsReceiverRegistered) { 431 mContext.unregisterReceiver(mReceiver); 432 mIsReceiverRegistered = false; 433 } 434 435 return true; 436 } 437 438 /** 439 * see {@link WifiManager#getWifiState()} 440 * @return One of {@link WifiManager#WIFI_STATE_DISABLED}, 441 * {@link WifiManager#WIFI_STATE_DISABLING}, 442 * {@link WifiManager#WIFI_STATE_ENABLED}, 443 * {@link WifiManager#WIFI_STATE_ENABLING}, 444 * {@link WifiManager#WIFI_STATE_UNKNOWN} 445 */ 446 public int getWifiEnabledState() { 447 enforceAccessPermission(); 448 return mWifiStateMachine.syncGetWifiState(); 449 } 450 451 /** 452 * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)} 453 * @param wifiConfig SSID, security and channel details as 454 * part of WifiConfiguration 455 * @param enabled true to enable and false to disable 456 */ 457 public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { 458 enforceChangePermission(); 459 mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget(); 460 } 461 462 /** 463 * see {@link WifiManager#getWifiApState()} 464 * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, 465 * {@link WifiManager#WIFI_AP_STATE_DISABLING}, 466 * {@link WifiManager#WIFI_AP_STATE_ENABLED}, 467 * {@link WifiManager#WIFI_AP_STATE_ENABLING}, 468 * {@link WifiManager#WIFI_AP_STATE_FAILED} 469 */ 470 public int getWifiApEnabledState() { 471 enforceAccessPermission(); 472 return mWifiStateMachine.syncGetWifiApState(); 473 } 474 475 /** 476 * see {@link WifiManager#getWifiApConfiguration()} 477 * @return soft access point configuration 478 */ 479 public WifiConfiguration getWifiApConfiguration() { 480 enforceAccessPermission(); 481 return mWifiStateMachine.syncGetWifiApConfiguration(); 482 } 483 484 /** 485 * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)} 486 * @param wifiConfig WifiConfiguration details for soft access point 487 */ 488 public void setWifiApConfiguration(WifiConfiguration wifiConfig) { 489 enforceChangePermission(); 490 if (wifiConfig == null) 491 return; 492 mWifiStateMachine.setWifiApConfiguration(wifiConfig); 493 } 494 495 /** 496 * @param enable {@code true} to enable, {@code false} to disable. 497 * @return {@code true} if the enable/disable operation was 498 * started or is already in the queue. 499 */ 500 public boolean isScanningAlwaysAvailable() { 501 enforceAccessPermission(); 502 return mSettingsStore.isScanAlwaysAvailable(); 503 } 504 505 /** 506 * see {@link android.net.wifi.WifiManager#disconnect()} 507 */ 508 public void disconnect() { 509 enforceChangePermission(); 510 mWifiStateMachine.disconnectCommand(); 511 } 512 513 /** 514 * see {@link android.net.wifi.WifiManager#reconnect()} 515 */ 516 public void reconnect() { 517 enforceChangePermission(); 518 mWifiStateMachine.reconnectCommand(); 519 } 520 521 /** 522 * see {@link android.net.wifi.WifiManager#reassociate()} 523 */ 524 public void reassociate() { 525 enforceChangePermission(); 526 mWifiStateMachine.reassociateCommand(); 527 } 528 529 /** 530 * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()} 531 * @return the list of configured networks 532 */ 533 public List<WifiConfiguration> getConfiguredNetworks() { 534 enforceAccessPermission(); 535 if (mWifiStateMachineChannel != null) { 536 return mWifiStateMachine.syncGetConfiguredNetworks(mWifiStateMachineChannel); 537 } else { 538 Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); 539 return null; 540 } 541 } 542 543 /** 544 * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)} 545 * @return the supplicant-assigned identifier for the new or updated 546 * network if the operation succeeds, or {@code -1} if it fails 547 */ 548 public int addOrUpdateNetwork(WifiConfiguration config) { 549 enforceChangePermission(); 550 if (mWifiStateMachineChannel != null) { 551 return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config); 552 } else { 553 Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); 554 return -1; 555 } 556 } 557 558 /** 559 * See {@link android.net.wifi.WifiManager#removeNetwork(int)} 560 * @param netId the integer that identifies the network configuration 561 * to the supplicant 562 * @return {@code true} if the operation succeeded 563 */ 564 public boolean removeNetwork(int netId) { 565 enforceChangePermission(); 566 if (mWifiStateMachineChannel != null) { 567 return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId); 568 } else { 569 Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); 570 return false; 571 } 572 } 573 574 /** 575 * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)} 576 * @param netId the integer that identifies the network configuration 577 * to the supplicant 578 * @param disableOthers if true, disable all other networks. 579 * @return {@code true} if the operation succeeded 580 */ 581 public boolean enableNetwork(int netId, boolean disableOthers) { 582 enforceChangePermission(); 583 if (mWifiStateMachineChannel != null) { 584 return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId, 585 disableOthers); 586 } else { 587 Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); 588 return false; 589 } 590 } 591 592 /** 593 * See {@link android.net.wifi.WifiManager#disableNetwork(int)} 594 * @param netId the integer that identifies the network configuration 595 * to the supplicant 596 * @return {@code true} if the operation succeeded 597 */ 598 public boolean disableNetwork(int netId) { 599 enforceChangePermission(); 600 if (mWifiStateMachineChannel != null) { 601 return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId); 602 } else { 603 Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); 604 return false; 605 } 606 } 607 608 /** 609 * See {@link android.net.wifi.WifiManager#getConnectionInfo()} 610 * @return the Wi-Fi information, contained in {@link WifiInfo}. 611 */ 612 public WifiInfo getConnectionInfo() { 613 enforceAccessPermission(); 614 /* 615 * Make sure we have the latest information, by sending 616 * a status request to the supplicant. 617 */ 618 return mWifiStateMachine.syncRequestConnectionInfo(); 619 } 620 621 /** 622 * Return the results of the most recent access point scan, in the form of 623 * a list of {@link ScanResult} objects. 624 * @return the list of results 625 */ 626 public List<ScanResult> getScanResults(String callingPackage) { 627 enforceAccessPermission(); 628 int userId = UserHandle.getCallingUserId(); 629 int uid = Binder.getCallingUid(); 630 long ident = Binder.clearCallingIdentity(); 631 if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage) 632 != AppOpsManager.MODE_ALLOWED) { 633 return new ArrayList<ScanResult>(); 634 } 635 try { 636 int currentUser = ActivityManager.getCurrentUser(); 637 if (userId != currentUser) { 638 return new ArrayList<ScanResult>(); 639 } else { 640 return mWifiStateMachine.syncGetScanResultsList(); 641 } 642 } finally { 643 Binder.restoreCallingIdentity(ident); 644 } 645 } 646 647 /** 648 * Tell the supplicant to persist the current list of configured networks. 649 * @return {@code true} if the operation succeeded 650 * 651 * TODO: deprecate this 652 */ 653 public boolean saveConfiguration() { 654 boolean result = true; 655 enforceChangePermission(); 656 if (mWifiStateMachineChannel != null) { 657 return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel); 658 } else { 659 Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); 660 return false; 661 } 662 } 663 664 /** 665 * Set the country code 666 * @param countryCode ISO 3166 country code. 667 * @param persist {@code true} if the setting should be remembered. 668 * 669 * The persist behavior exists so that wifi can fall back to the last 670 * persisted country code on a restart, when the locale information is 671 * not available from telephony. 672 */ 673 public void setCountryCode(String countryCode, boolean persist) { 674 Slog.i(TAG, "WifiService trying to set country code to " + countryCode + 675 " with persist set to " + persist); 676 enforceChangePermission(); 677 mWifiStateMachine.setCountryCode(countryCode, persist); 678 } 679 680 /** 681 * Set the operational frequency band 682 * @param band One of 683 * {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO}, 684 * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ}, 685 * {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ}, 686 * @param persist {@code true} if the setting should be remembered. 687 * 688 */ 689 public void setFrequencyBand(int band, boolean persist) { 690 enforceChangePermission(); 691 if (!isDualBandSupported()) return; 692 Slog.i(TAG, "WifiService trying to set frequency band to " + band + 693 " with persist set to " + persist); 694 mWifiStateMachine.setFrequencyBand(band, persist); 695 } 696 697 698 /** 699 * Get the operational frequency band 700 */ 701 public int getFrequencyBand() { 702 enforceAccessPermission(); 703 return mWifiStateMachine.getFrequencyBand(); 704 } 705 706 public boolean isDualBandSupported() { 707 //TODO: Should move towards adding a driver API that checks at runtime 708 return mContext.getResources().getBoolean( 709 com.android.internal.R.bool.config_wifi_dual_band_support); 710 } 711 712 /** 713 * Return the DHCP-assigned addresses from the last successful DHCP request, 714 * if any. 715 * @return the DHCP information 716 * @deprecated 717 */ 718 public DhcpInfo getDhcpInfo() { 719 enforceAccessPermission(); 720 DhcpResults dhcpResults = mWifiStateMachine.syncGetDhcpResults(); 721 if (dhcpResults.linkProperties == null) return null; 722 723 DhcpInfo info = new DhcpInfo(); 724 for (LinkAddress la : dhcpResults.linkProperties.getLinkAddresses()) { 725 InetAddress addr = la.getAddress(); 726 if (addr instanceof Inet4Address) { 727 info.ipAddress = NetworkUtils.inetAddressToInt((Inet4Address)addr); 728 break; 729 } 730 } 731 for (RouteInfo r : dhcpResults.linkProperties.getRoutes()) { 732 if (r.isDefaultRoute()) { 733 InetAddress gateway = r.getGateway(); 734 if (gateway instanceof Inet4Address) { 735 info.gateway = NetworkUtils.inetAddressToInt((Inet4Address)gateway); 736 } 737 } else if (r.isHostRoute()) { 738 LinkAddress dest = r.getDestination(); 739 if (dest.getAddress() instanceof Inet4Address) { 740 info.netmask = NetworkUtils.prefixLengthToNetmaskInt( 741 dest.getNetworkPrefixLength()); 742 } 743 } 744 } 745 int dnsFound = 0; 746 for (InetAddress dns : dhcpResults.linkProperties.getDnses()) { 747 if (dns instanceof Inet4Address) { 748 if (dnsFound == 0) { 749 info.dns1 = NetworkUtils.inetAddressToInt((Inet4Address)dns); 750 } else { 751 info.dns2 = NetworkUtils.inetAddressToInt((Inet4Address)dns); 752 } 753 if (++dnsFound > 1) break; 754 } 755 } 756 InetAddress serverAddress = dhcpResults.serverAddress; 757 if (serverAddress instanceof Inet4Address) { 758 info.serverAddress = NetworkUtils.inetAddressToInt((Inet4Address)serverAddress); 759 } 760 info.leaseDuration = dhcpResults.leaseDuration; 761 762 return info; 763 } 764 765 /** 766 * see {@link android.net.wifi.WifiManager#startWifi} 767 * 768 */ 769 public void startWifi() { 770 enforceConnectivityInternalPermission(); 771 /* TODO: may be add permissions for access only to connectivity service 772 * TODO: if a start issued, keep wifi alive until a stop issued irrespective 773 * of WifiLock & device idle status unless wifi enabled status is toggled 774 */ 775 776 mWifiStateMachine.setDriverStart(true); 777 mWifiStateMachine.reconnectCommand(); 778 } 779 780 public void captivePortalCheckComplete() { 781 enforceConnectivityInternalPermission(); 782 mWifiStateMachine.captivePortalCheckComplete(); 783 } 784 785 /** 786 * see {@link android.net.wifi.WifiManager#stopWifi} 787 * 788 */ 789 public void stopWifi() { 790 enforceConnectivityInternalPermission(); 791 /* 792 * TODO: if a stop is issued, wifi is brought up only by startWifi 793 * unless wifi enabled status is toggled 794 */ 795 mWifiStateMachine.setDriverStart(false); 796 } 797 798 /** 799 * see {@link android.net.wifi.WifiManager#addToBlacklist} 800 * 801 */ 802 public void addToBlacklist(String bssid) { 803 enforceChangePermission(); 804 805 mWifiStateMachine.addToBlacklist(bssid); 806 } 807 808 /** 809 * see {@link android.net.wifi.WifiManager#clearBlacklist} 810 * 811 */ 812 public void clearBlacklist() { 813 enforceChangePermission(); 814 815 mWifiStateMachine.clearBlacklist(); 816 } 817 818 /** 819 * Get a reference to handler. This is used by a client to establish 820 * an AsyncChannel communication with WifiService 821 */ 822 public Messenger getWifiServiceMessenger() { 823 enforceAccessPermission(); 824 enforceChangePermission(); 825 return new Messenger(mClientHandler); 826 } 827 828 /** Get a reference to WifiStateMachine handler for AsyncChannel communication */ 829 public Messenger getWifiStateMachineMessenger() { 830 enforceAccessPermission(); 831 enforceChangePermission(); 832 return mWifiStateMachine.getMessenger(); 833 } 834 835 /** 836 * Get the IP and proxy configuration file 837 */ 838 public String getConfigFile() { 839 enforceAccessPermission(); 840 return mWifiStateMachine.getConfigFile(); 841 } 842 843 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 844 @Override 845 public void onReceive(Context context, Intent intent) { 846 String action = intent.getAction(); 847 if (action.equals(Intent.ACTION_SCREEN_ON)) { 848 mWifiController.sendMessage(CMD_SCREEN_ON); 849 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 850 mWifiController.sendMessage(CMD_SCREEN_OFF); 851 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 852 int pluggedType = intent.getIntExtra("plugged", 0); 853 mWifiController.sendMessage(CMD_BATTERY_CHANGED, pluggedType, 0, null); 854 } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { 855 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 856 BluetoothAdapter.STATE_DISCONNECTED); 857 mWifiStateMachine.sendBluetoothAdapterStateChange(state); 858 } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { 859 boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false); 860 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0); 861 } 862 } 863 }; 864 865 /** 866 * Observes settings changes to scan always mode. 867 */ 868 private void registerForScanModeChange() { 869 ContentObserver contentObserver = new ContentObserver(null) { 870 @Override 871 public void onChange(boolean selfChange) { 872 mSettingsStore.handleWifiScanAlwaysAvailableToggled(); 873 mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED); 874 } 875 }; 876 877 mContext.getContentResolver().registerContentObserver( 878 Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE), 879 false, contentObserver); 880 } 881 882 private void getPersistedDeviceProvisioned() { 883 mDeviceProvisioned.set(Settings.Global.getInt(mContext.getContentResolver(), 884 Settings.Global.DEVICE_PROVISIONED, 0) != 0); 885 } 886 887 private void getPersistedNotifyScanMode() { 888 mNotifyScanMode.set(Settings.Global.getInt(mContext.getContentResolver(), 889 Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE, 1) == 1); 890 } 891 892 /** 893 * Observes settings changes to notify the user when scan mode is active and 894 * Wi-Fi is turned off 895 */ 896 private void registerForNotifyUserOnScanModeChange() { 897 ContentObserver contentObserver = new ContentObserver(null) { 898 @Override 899 public void onChange(boolean selfChange) { 900 getPersistedNotifyScanMode(); 901 } 902 }; 903 904 getPersistedNotifyScanMode(); 905 mContext.getContentResolver().registerContentObserver( 906 Settings.Global.getUriFor(Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE), 907 false, contentObserver); 908 } 909 910 /* 911 * Observes settings changes device provisioned status 912 */ 913 private void registerForDeviceProvisionedChange() { 914 ContentObserver contentObserver = new ContentObserver(null) { 915 @Override 916 public void onChange(boolean selfChange) { 917 getPersistedDeviceProvisioned(); 918 } 919 }; 920 921 getPersistedDeviceProvisioned(); 922 mContext.getContentResolver().registerContentObserver( 923 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), 924 false, contentObserver); 925 } 926 927 private void registerForBroadcasts() { 928 IntentFilter intentFilter = new IntentFilter(); 929 intentFilter.addAction(Intent.ACTION_SCREEN_ON); 930 intentFilter.addAction(Intent.ACTION_SCREEN_OFF); 931 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); 932 intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 933 intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); 934 intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 935 mContext.registerReceiver(mReceiver, intentFilter); 936 } 937 938 @Override 939 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 940 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 941 != PackageManager.PERMISSION_GRANTED) { 942 pw.println("Permission Denial: can't dump WifiService from from pid=" 943 + Binder.getCallingPid() 944 + ", uid=" + Binder.getCallingUid()); 945 return; 946 } 947 pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName()); 948 pw.println("Stay-awake conditions: " + 949 Settings.Global.getInt(mContext.getContentResolver(), 950 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0)); 951 pw.println("mDeviceProvisioned " + mDeviceProvisioned.get()); 952 pw.println("mNotifyScanMode " + mNotifyScanMode.get()); 953 pw.println("mMulticastEnabled " + mMulticastEnabled); 954 pw.println("mMulticastDisabled " + mMulticastDisabled); 955 mWifiController.dump(fd, pw, args); 956 mSettingsStore.dump(fd, pw, args); 957 mNotificationController.dump(fd, pw, args); 958 mTrafficPoller.dump(fd, pw, args); 959 960 pw.println("Latest scan results:"); 961 List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList(); 962 if (scanResults != null && scanResults.size() != 0) { 963 pw.println(" BSSID Frequency RSSI Flags SSID"); 964 for (ScanResult r : scanResults) { 965 pw.printf(" %17s %9d %5d %-16s %s%n", 966 r.BSSID, 967 r.frequency, 968 r.level, 969 r.capabilities, 970 r.SSID == null ? "" : r.SSID); 971 } 972 } 973 pw.println(); 974 pw.println("Locks acquired: " + mFullLocksAcquired + " full, " + 975 mFullHighPerfLocksAcquired + " full high perf, " + 976 mScanLocksAcquired + " scan"); 977 pw.println("Locks released: " + mFullLocksReleased + " full, " + 978 mFullHighPerfLocksReleased + " full high perf, " + 979 mScanLocksReleased + " scan"); 980 pw.println(); 981 pw.println("Locks held:"); 982 mLocks.dump(pw); 983 984 mWifiWatchdogStateMachine.dump(fd, pw, args); 985 pw.println(); 986 mWifiStateMachine.dump(fd, pw, args); 987 pw.println(); 988 } 989 990 private class WifiLock extends DeathRecipient { 991 WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { 992 super(lockMode, tag, binder, ws); 993 } 994 995 public void binderDied() { 996 synchronized (mLocks) { 997 releaseWifiLockLocked(mBinder); 998 } 999 } 1000 1001 public String toString() { 1002 return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}"; 1003 } 1004 } 1005 1006 class LockList { 1007 private List<WifiLock> mList; 1008 1009 private LockList() { 1010 mList = new ArrayList<WifiLock>(); 1011 } 1012 1013 synchronized boolean hasLocks() { 1014 return !mList.isEmpty(); 1015 } 1016 1017 synchronized int getStrongestLockMode() { 1018 if (mList.isEmpty()) { 1019 return WifiManager.WIFI_MODE_FULL; 1020 } 1021 1022 if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) { 1023 return WifiManager.WIFI_MODE_FULL_HIGH_PERF; 1024 } 1025 1026 if (mFullLocksAcquired > mFullLocksReleased) { 1027 return WifiManager.WIFI_MODE_FULL; 1028 } 1029 1030 return WifiManager.WIFI_MODE_SCAN_ONLY; 1031 } 1032 1033 synchronized void updateWorkSource(WorkSource ws) { 1034 for (int i = 0; i < mLocks.mList.size(); i++) { 1035 ws.add(mLocks.mList.get(i).mWorkSource); 1036 } 1037 } 1038 1039 private void addLock(WifiLock lock) { 1040 if (findLockByBinder(lock.mBinder) < 0) { 1041 mList.add(lock); 1042 } 1043 } 1044 1045 private WifiLock removeLock(IBinder binder) { 1046 int index = findLockByBinder(binder); 1047 if (index >= 0) { 1048 WifiLock ret = mList.remove(index); 1049 ret.unlinkDeathRecipient(); 1050 return ret; 1051 } else { 1052 return null; 1053 } 1054 } 1055 1056 private int findLockByBinder(IBinder binder) { 1057 int size = mList.size(); 1058 for (int i = size - 1; i >= 0; i--) { 1059 if (mList.get(i).mBinder == binder) 1060 return i; 1061 } 1062 return -1; 1063 } 1064 1065 private void dump(PrintWriter pw) { 1066 for (WifiLock l : mList) { 1067 pw.print(" "); 1068 pw.println(l); 1069 } 1070 } 1071 } 1072 1073 void enforceWakeSourcePermission(int uid, int pid) { 1074 if (uid == android.os.Process.myUid()) { 1075 return; 1076 } 1077 mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, 1078 pid, uid, null); 1079 } 1080 1081 public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) { 1082 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); 1083 if (lockMode != WifiManager.WIFI_MODE_FULL && 1084 lockMode != WifiManager.WIFI_MODE_SCAN_ONLY && 1085 lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) { 1086 Slog.e(TAG, "Illegal argument, lockMode= " + lockMode); 1087 if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode); 1088 return false; 1089 } 1090 if (ws != null && ws.size() == 0) { 1091 ws = null; 1092 } 1093 if (ws != null) { 1094 enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid()); 1095 } 1096 if (ws == null) { 1097 ws = new WorkSource(Binder.getCallingUid()); 1098 } 1099 WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws); 1100 synchronized (mLocks) { 1101 return acquireWifiLockLocked(wifiLock); 1102 } 1103 } 1104 1105 private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException { 1106 switch(wifiLock.mMode) { 1107 case WifiManager.WIFI_MODE_FULL: 1108 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 1109 case WifiManager.WIFI_MODE_SCAN_ONLY: 1110 mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource); 1111 break; 1112 } 1113 } 1114 1115 private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException { 1116 switch(wifiLock.mMode) { 1117 case WifiManager.WIFI_MODE_FULL: 1118 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 1119 case WifiManager.WIFI_MODE_SCAN_ONLY: 1120 mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource); 1121 break; 1122 } 1123 } 1124 1125 private boolean acquireWifiLockLocked(WifiLock wifiLock) { 1126 if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock); 1127 1128 mLocks.addLock(wifiLock); 1129 1130 long ident = Binder.clearCallingIdentity(); 1131 try { 1132 noteAcquireWifiLock(wifiLock); 1133 switch(wifiLock.mMode) { 1134 case WifiManager.WIFI_MODE_FULL: 1135 ++mFullLocksAcquired; 1136 break; 1137 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 1138 ++mFullHighPerfLocksAcquired; 1139 break; 1140 1141 case WifiManager.WIFI_MODE_SCAN_ONLY: 1142 ++mScanLocksAcquired; 1143 break; 1144 } 1145 mWifiController.sendMessage(CMD_LOCKS_CHANGED); 1146 return true; 1147 } catch (RemoteException e) { 1148 return false; 1149 } finally { 1150 Binder.restoreCallingIdentity(ident); 1151 } 1152 } 1153 1154 public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) { 1155 int uid = Binder.getCallingUid(); 1156 int pid = Binder.getCallingPid(); 1157 if (ws != null && ws.size() == 0) { 1158 ws = null; 1159 } 1160 if (ws != null) { 1161 enforceWakeSourcePermission(uid, pid); 1162 } 1163 long ident = Binder.clearCallingIdentity(); 1164 try { 1165 synchronized (mLocks) { 1166 int index = mLocks.findLockByBinder(lock); 1167 if (index < 0) { 1168 throw new IllegalArgumentException("Wifi lock not active"); 1169 } 1170 WifiLock wl = mLocks.mList.get(index); 1171 noteReleaseWifiLock(wl); 1172 wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid); 1173 noteAcquireWifiLock(wl); 1174 } 1175 } catch (RemoteException e) { 1176 } finally { 1177 Binder.restoreCallingIdentity(ident); 1178 } 1179 } 1180 1181 public boolean releaseWifiLock(IBinder lock) { 1182 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); 1183 synchronized (mLocks) { 1184 return releaseWifiLockLocked(lock); 1185 } 1186 } 1187 1188 private boolean releaseWifiLockLocked(IBinder lock) { 1189 boolean hadLock; 1190 1191 WifiLock wifiLock = mLocks.removeLock(lock); 1192 1193 if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock); 1194 1195 hadLock = (wifiLock != null); 1196 1197 long ident = Binder.clearCallingIdentity(); 1198 try { 1199 if (hadLock) { 1200 noteReleaseWifiLock(wifiLock); 1201 switch(wifiLock.mMode) { 1202 case WifiManager.WIFI_MODE_FULL: 1203 ++mFullLocksReleased; 1204 break; 1205 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 1206 ++mFullHighPerfLocksReleased; 1207 break; 1208 case WifiManager.WIFI_MODE_SCAN_ONLY: 1209 ++mScanLocksReleased; 1210 break; 1211 } 1212 mWifiController.sendMessage(CMD_LOCKS_CHANGED); 1213 } 1214 } catch (RemoteException e) { 1215 } finally { 1216 Binder.restoreCallingIdentity(ident); 1217 } 1218 1219 return hadLock; 1220 } 1221 1222 private abstract class DeathRecipient 1223 implements IBinder.DeathRecipient { 1224 String mTag; 1225 int mMode; 1226 IBinder mBinder; 1227 WorkSource mWorkSource; 1228 1229 DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) { 1230 super(); 1231 mTag = tag; 1232 mMode = mode; 1233 mBinder = binder; 1234 mWorkSource = ws; 1235 try { 1236 mBinder.linkToDeath(this, 0); 1237 } catch (RemoteException e) { 1238 binderDied(); 1239 } 1240 } 1241 1242 void unlinkDeathRecipient() { 1243 mBinder.unlinkToDeath(this, 0); 1244 } 1245 } 1246 1247 private class Multicaster extends DeathRecipient { 1248 Multicaster(String tag, IBinder binder) { 1249 super(Binder.getCallingUid(), tag, binder, null); 1250 } 1251 1252 public void binderDied() { 1253 Slog.e(TAG, "Multicaster binderDied"); 1254 synchronized (mMulticasters) { 1255 int i = mMulticasters.indexOf(this); 1256 if (i != -1) { 1257 removeMulticasterLocked(i, mMode); 1258 } 1259 } 1260 } 1261 1262 public String toString() { 1263 return "Multicaster{" + mTag + " binder=" + mBinder + "}"; 1264 } 1265 1266 public int getUid() { 1267 return mMode; 1268 } 1269 } 1270 1271 public void initializeMulticastFiltering() { 1272 enforceMulticastChangePermission(); 1273 1274 synchronized (mMulticasters) { 1275 // if anybody had requested filters be off, leave off 1276 if (mMulticasters.size() != 0) { 1277 return; 1278 } else { 1279 mWifiStateMachine.startFilteringMulticastV4Packets(); 1280 } 1281 } 1282 } 1283 1284 public void acquireMulticastLock(IBinder binder, String tag) { 1285 enforceMulticastChangePermission(); 1286 1287 synchronized (mMulticasters) { 1288 mMulticastEnabled++; 1289 mMulticasters.add(new Multicaster(tag, binder)); 1290 // Note that we could call stopFilteringMulticastV4Packets only when 1291 // our new size == 1 (first call), but this function won't 1292 // be called often and by making the stopPacket call each 1293 // time we're less fragile and self-healing. 1294 mWifiStateMachine.stopFilteringMulticastV4Packets(); 1295 } 1296 1297 int uid = Binder.getCallingUid(); 1298 final long ident = Binder.clearCallingIdentity(); 1299 try { 1300 mBatteryStats.noteWifiMulticastEnabled(uid); 1301 } catch (RemoteException e) { 1302 } finally { 1303 Binder.restoreCallingIdentity(ident); 1304 } 1305 } 1306 1307 public void releaseMulticastLock() { 1308 enforceMulticastChangePermission(); 1309 1310 int uid = Binder.getCallingUid(); 1311 synchronized (mMulticasters) { 1312 mMulticastDisabled++; 1313 int size = mMulticasters.size(); 1314 for (int i = size - 1; i >= 0; i--) { 1315 Multicaster m = mMulticasters.get(i); 1316 if ((m != null) && (m.getUid() == uid)) { 1317 removeMulticasterLocked(i, uid); 1318 } 1319 } 1320 } 1321 } 1322 1323 private void removeMulticasterLocked(int i, int uid) 1324 { 1325 Multicaster removed = mMulticasters.remove(i); 1326 1327 if (removed != null) { 1328 removed.unlinkDeathRecipient(); 1329 } 1330 if (mMulticasters.size() == 0) { 1331 mWifiStateMachine.startFilteringMulticastV4Packets(); 1332 } 1333 1334 final long ident = Binder.clearCallingIdentity(); 1335 try { 1336 mBatteryStats.noteWifiMulticastDisabled(uid); 1337 } catch (RemoteException e) { 1338 } finally { 1339 Binder.restoreCallingIdentity(ident); 1340 } 1341 } 1342 1343 public boolean isMulticastEnabled() { 1344 enforceAccessPermission(); 1345 1346 synchronized (mMulticasters) { 1347 return (mMulticasters.size() > 0); 1348 } 1349 } 1350} 1351