WifiScanningServiceImpl.java revision 5a091fb6a7f51f701e81a45174c9fd74f97dce22
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wifi; 18 19import android.Manifest; 20import android.app.AlarmManager; 21import android.app.PendingIntent; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.net.wifi.IWifiScanner; 27import android.net.wifi.ScanResult; 28import android.net.wifi.WifiManager; 29import android.net.wifi.WifiScanner; 30import android.net.wifi.WifiScanner.BssidInfo; 31import android.net.wifi.WifiScanner.ChannelSpec; 32import android.net.wifi.WifiScanner.ScanData; 33import android.net.wifi.WifiScanner.ScanSettings; 34import android.net.wifi.WifiSsid; 35import android.os.Bundle; 36import android.os.Handler; 37import android.os.HandlerThread; 38import android.os.Looper; 39import android.os.Message; 40import android.os.Messenger; 41import android.os.RemoteException; 42import android.os.SystemClock; 43import android.os.WorkSource; 44import android.util.LocalLog; 45import android.util.Log; 46 47import com.android.internal.app.IBatteryStats; 48import com.android.internal.util.AsyncChannel; 49import com.android.internal.util.Protocol; 50import com.android.internal.util.StateMachine; 51import com.android.internal.util.State; 52import com.android.server.am.BatteryStatsService; 53 54 55import java.io.FileDescriptor; 56import java.io.PrintWriter; 57import java.util.ArrayList; 58import java.util.Collection; 59import java.util.HashMap; 60import java.util.HashSet; 61import java.util.Iterator; 62import java.util.Map; 63 64public class WifiScanningServiceImpl extends IWifiScanner.Stub { 65 66 private static final String TAG = "WifiScanningService"; 67 private static final boolean DBG = true; 68 private static final boolean VDBG = false; 69 70 private static final int INVALID_KEY = 0; // same as WifiScanner 71 private static final int MIN_PERIOD_PER_CHANNEL_MS = 200; // DFS needs 120 ms 72 private static final int UNKNOWN_PID = -1; 73 74 private static final LocalLog mLocalLog = new LocalLog(500); 75 76 private static void localLog(String message) { 77 mLocalLog.log(message); 78 } 79 80 private static void logw(String message) { 81 Log.w(TAG, message); 82 mLocalLog.log(message); 83 } 84 85 private static void loge(String message) { 86 Log.e(TAG, message); 87 mLocalLog.log(message); 88 } 89 90 @Override 91 public Messenger getMessenger() { 92 if (mClientHandler != null) { 93 return new Messenger(mClientHandler); 94 } else { 95 loge("WifiScanningServiceImpl trying to get messenger w/o initialization"); 96 return null; 97 } 98 } 99 100 @Override 101 public Bundle getAvailableChannels(int band) { 102 ChannelSpec channelSpecs[] = getChannelsForBand(band); 103 ArrayList<Integer> list = new ArrayList<Integer>(channelSpecs.length); 104 for (ChannelSpec channelSpec : channelSpecs) { 105 list.add(channelSpec.frequency); 106 } 107 Bundle b = new Bundle(); 108 b.putIntegerArrayList(WifiScanner.GET_AVAILABLE_CHANNELS_EXTRA, list); 109 return b; 110 } 111 112 private void enforceLocationHardwarePermission(int uid) { 113 mContext.enforcePermission( 114 Manifest.permission.LOCATION_HARDWARE, 115 UNKNOWN_PID, uid, 116 "LocationHardware"); 117 } 118 119 private class ClientHandler extends Handler { 120 121 ClientHandler(android.os.Looper looper) { 122 super(looper); 123 } 124 125 @Override 126 public void handleMessage(Message msg) { 127 128 if (DBG) localLog("ClientHandler got" + msg); 129 130 switch (msg.what) { 131 132 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 133 if (msg.arg1 != AsyncChannel.STATUS_SUCCESSFUL) { 134 loge("Client connection failure, error=" + msg.arg1); 135 } 136 return; 137 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: 138 AsyncChannel ac = new AsyncChannel(); 139 ac.connect(mContext, this, msg.replyTo); 140 if (DBG) localLog("New client connected : " + msg.sendingUid + msg.replyTo); 141 ClientInfo cInfo = new ClientInfo(msg.sendingUid, ac, msg.replyTo); 142 mClients.put(msg.replyTo, cInfo); 143 return; 144 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 145 if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { 146 loge("Send failed, client connection lost"); 147 } else { 148 if (DBG) localLog("Client connection lost with reason: " + msg.arg1); 149 } 150 if (DBG) localLog("closing client " + msg.replyTo); 151 ClientInfo ci = mClients.remove(msg.replyTo); 152 if (ci != null) { /* can be null if send failed above */ 153 if (DBG) localLog("closing client " + ci.mUid); 154 ci.cleanup(); 155 } 156 return; 157 } 158 159 try { 160 enforceLocationHardwarePermission(msg.sendingUid); 161 } catch (SecurityException e) { 162 localLog("failed to authorize app: " + e); 163 replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized"); 164 return; 165 } 166 167 if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) { 168 mStateMachine.sendMessage(Message.obtain(msg)); 169 return; 170 } 171 ClientInfo ci = mClients.get(msg.replyTo); 172 if (ci == null) { 173 loge("Could not find client info for message " + msg.replyTo); 174 replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, "Could not find listener"); 175 return; 176 } 177 178 int validCommands[] = { 179 WifiScanner.CMD_SCAN, 180 WifiScanner.CMD_START_BACKGROUND_SCAN, 181 WifiScanner.CMD_STOP_BACKGROUND_SCAN, 182 WifiScanner.CMD_START_SINGLE_SCAN, 183 WifiScanner.CMD_STOP_SINGLE_SCAN, 184 WifiScanner.CMD_SET_HOTLIST, 185 WifiScanner.CMD_RESET_HOTLIST, 186 WifiScanner.CMD_CONFIGURE_WIFI_CHANGE, 187 WifiScanner.CMD_START_TRACKING_CHANGE, 188 WifiScanner.CMD_STOP_TRACKING_CHANGE }; 189 190 for (int cmd : validCommands) { 191 if (cmd == msg.what) { 192 mStateMachine.sendMessage(Message.obtain(msg)); 193 return; 194 } 195 } 196 197 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "Invalid request"); 198 } 199 } 200 201 private static final int BASE = Protocol.BASE_WIFI_SCANNER_SERVICE; 202 203 private static final int CMD_SCAN_RESULTS_AVAILABLE = BASE + 0; 204 private static final int CMD_FULL_SCAN_RESULTS = BASE + 1; 205 private static final int CMD_HOTLIST_AP_FOUND = BASE + 2; 206 private static final int CMD_HOTLIST_AP_LOST = BASE + 3; 207 private static final int CMD_WIFI_CHANGE_DETECTED = BASE + 4; 208 private static final int CMD_WIFI_CHANGES_STABILIZED = BASE + 5; 209 private static final int CMD_DRIVER_LOADED = BASE + 6; 210 private static final int CMD_DRIVER_UNLOADED = BASE + 7; 211 private static final int CMD_SCAN_PAUSED = BASE + 8; 212 private static final int CMD_SCAN_RESTARTED = BASE + 9; 213 private static final int CMD_STOP_SCAN_INTERNAL = BASE + 10; 214 215 private Context mContext; 216 private WifiScanningStateMachine mStateMachine; 217 private ClientHandler mClientHandler; 218 private IBatteryStats mBatteryStats; 219 220 WifiScanningServiceImpl() { } 221 222 WifiScanningServiceImpl(Context context) { 223 mContext = context; 224 } 225 226 public void startService(Context context) { 227 mContext = context; 228 229 HandlerThread thread = new HandlerThread("WifiScanningService"); 230 thread.start(); 231 232 mClientHandler = new ClientHandler(thread.getLooper()); 233 mStateMachine = new WifiScanningStateMachine(thread.getLooper()); 234 mWifiChangeStateMachine = new WifiChangeStateMachine(thread.getLooper()); 235 mBatteryStats = BatteryStatsService.getService(); 236 237 mContext.registerReceiver( 238 new BroadcastReceiver() { 239 @Override 240 public void onReceive(Context context, Intent intent) { 241 int state = intent.getIntExtra( 242 WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED); 243 if (DBG) localLog("SCAN_AVAILABLE : " + state); 244 if (state == WifiManager.WIFI_STATE_ENABLED) { 245 mStateMachine.sendMessage(CMD_DRIVER_LOADED); 246 } else if (state == WifiManager.WIFI_STATE_DISABLED) { 247 mStateMachine.sendMessage(CMD_DRIVER_UNLOADED); 248 } 249 } 250 }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE)); 251 252 mStateMachine.start(); 253 mWifiChangeStateMachine.start(); 254 } 255 256 class WifiScanningStateMachine extends StateMachine implements WifiNative.ScanEventHandler, 257 WifiNative.HotlistEventHandler, WifiNative.SignificantWifiChangeEventHandler { 258 259 private final DefaultState mDefaultState = new DefaultState(); 260 private final StartedState mStartedState = new StartedState(); 261 private final PausedState mPausedState = new PausedState(); 262 263 public WifiScanningStateMachine(Looper looper) { 264 super(TAG, looper); 265 266 setLogRecSize(512); 267 setLogOnlyTransitions(false); 268 // setDbg(DBG); 269 270 addState(mDefaultState); 271 addState(mStartedState, mDefaultState); 272 addState(mPausedState, mDefaultState); 273 274 setInitialState(mDefaultState); 275 } 276 277 @Override 278 public void onScanResultsAvailable() { 279 if (DBG) localLog("onScanResultAvailable event received"); 280 sendMessage(CMD_SCAN_RESULTS_AVAILABLE); 281 } 282 283 @Override 284 public void onScanStatus() { 285 if (DBG) localLog("onScanStatus event received"); 286 sendMessage(CMD_SCAN_RESULTS_AVAILABLE); 287 } 288 289 @Override 290 public void onFullScanResult(ScanResult fullScanResult) { 291 if (DBG) localLog("Full scanresult received"); 292 sendMessage(CMD_FULL_SCAN_RESULTS, 0, 0, fullScanResult); 293 } 294 295 @Override 296 public void onScanPaused(ScanData scanData[]) { 297 sendMessage(CMD_SCAN_PAUSED, scanData); 298 } 299 300 @Override 301 public void onScanRestarted() { 302 sendMessage(CMD_SCAN_RESTARTED); 303 } 304 305 @Override 306 public void onHotlistApFound(ScanResult[] results) { 307 if (DBG) localLog("HotlistApFound event received"); 308 sendMessage(CMD_HOTLIST_AP_FOUND, 0, 0, results); 309 } 310 311 @Override 312 public void onHotlistApLost(ScanResult[] results) { 313 if (DBG) localLog("HotlistApLost event received"); 314 sendMessage(CMD_HOTLIST_AP_LOST, 0, 0, results); 315 } 316 317 @Override 318 public void onChangesFound(ScanResult[] results) { 319 if (DBG) localLog("onWifiChangesFound event received"); 320 sendMessage(CMD_WIFI_CHANGE_DETECTED, 0, 0, results); 321 } 322 323 class DefaultState extends State { 324 @Override 325 public void enter() { 326 if (DBG) localLog("DefaultState"); 327 } 328 @Override 329 public boolean processMessage(Message msg) { 330 331 if (DBG) localLog("DefaultState got" + msg); 332 333 ClientInfo ci = mClients.get(msg.replyTo); 334 335 switch (msg.what) { 336 case CMD_DRIVER_LOADED: 337 if (WifiNative.startHal() && WifiNative.getInterfaces() != 0) { 338 WifiNative.ScanCapabilities capabilities = 339 new WifiNative.ScanCapabilities(); 340 if (WifiNative.getScanCapabilities(capabilities)) { 341 transitionTo(mStartedState); 342 } else { 343 loge("could not get scan capabilities"); 344 } 345 } else { 346 loge("could not start HAL"); 347 } 348 break; 349 case WifiScanner.CMD_SCAN: 350 case WifiScanner.CMD_START_BACKGROUND_SCAN: 351 case WifiScanner.CMD_STOP_BACKGROUND_SCAN: 352 case WifiScanner.CMD_START_SINGLE_SCAN: 353 case WifiScanner.CMD_STOP_SINGLE_SCAN: 354 case WifiScanner.CMD_SET_HOTLIST: 355 case WifiScanner.CMD_RESET_HOTLIST: 356 case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: 357 case WifiScanner.CMD_START_TRACKING_CHANGE: 358 case WifiScanner.CMD_STOP_TRACKING_CHANGE: 359 case WifiScanner.CMD_GET_SCAN_RESULTS: 360 replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available"); 361 break; 362 363 case CMD_SCAN_RESULTS_AVAILABLE: 364 if (DBG) localLog("ignored scan results available event"); 365 break; 366 367 case CMD_FULL_SCAN_RESULTS: 368 if (DBG) localLog("ignored full scan result event"); 369 break; 370 371 default: 372 break; 373 } 374 375 return HANDLED; 376 } 377 } 378 379 class StartedState extends State { 380 381 @Override 382 public void enter() { 383 if (DBG) localLog("StartedState"); 384 } 385 386 @Override 387 public boolean processMessage(Message msg) { 388 389 if (DBG) localLog("StartedState got" + msg); 390 391 ClientInfo ci = mClients.get(msg.replyTo); 392 393 switch (msg.what) { 394 case CMD_DRIVER_UNLOADED: 395 transitionTo(mDefaultState); 396 break; 397 case WifiScanner.CMD_SCAN: 398 replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not implemented"); 399 break; 400 case WifiScanner.CMD_START_BACKGROUND_SCAN: 401 if (addScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) { 402 replySucceeded(msg); 403 } else { 404 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 405 } 406 break; 407 case WifiScanner.CMD_STOP_BACKGROUND_SCAN: 408 removeScanRequest(ci, msg.arg2); 409 break; 410 case WifiScanner.CMD_GET_SCAN_RESULTS: 411 reportScanResults(); 412 replySucceeded(msg); 413 break; 414 case WifiScanner.CMD_START_SINGLE_SCAN: 415 if (addSingleScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) { 416 replySucceeded(msg); 417 } else { 418 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 419 } 420 break; 421 case WifiScanner.CMD_STOP_SINGLE_SCAN: 422 removeScanRequest(ci, msg.arg2); 423 break; 424 case CMD_STOP_SCAN_INTERNAL: 425 localLog("Removing single shot scan"); 426 removeScanRequest((ClientInfo) msg.obj, msg.arg2); 427 break; 428 case WifiScanner.CMD_SET_HOTLIST: 429 setHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj); 430 replySucceeded(msg); 431 break; 432 case WifiScanner.CMD_RESET_HOTLIST: 433 resetHotlist(ci, msg.arg2); 434 break; 435 case WifiScanner.CMD_START_TRACKING_CHANGE: 436 trackWifiChanges(ci, msg.arg2); 437 replySucceeded(msg); 438 break; 439 case WifiScanner.CMD_STOP_TRACKING_CHANGE: 440 untrackWifiChanges(ci, msg.arg2); 441 break; 442 case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: 443 configureWifiChange((WifiScanner.WifiChangeSettings) msg.obj); 444 break; 445 case CMD_SCAN_RESULTS_AVAILABLE: { 446 ScanData[] results = WifiNative.getScanResults(/* flush = */ true); 447 Collection<ClientInfo> clients = mClients.values(); 448 for (ClientInfo ci2 : clients) { 449 ci2.reportScanResults(results); 450 } 451 } 452 break; 453 case CMD_FULL_SCAN_RESULTS: { 454 ScanResult result = (ScanResult) msg.obj; 455 if (DBG) localLog("reporting fullscan result for " + result.SSID); 456 Collection<ClientInfo> clients = mClients.values(); 457 for (ClientInfo ci2 : clients) { 458 ci2.reportFullScanResult(result); 459 } 460 } 461 break; 462 463 case CMD_HOTLIST_AP_FOUND: { 464 ScanResult[] results = (ScanResult[])msg.obj; 465 if (DBG) localLog("Found " + results.length + " results"); 466 Collection<ClientInfo> clients = mClients.values(); 467 for (ClientInfo ci2 : clients) { 468 ci2.reportHotlistResults(WifiScanner.CMD_AP_FOUND, results); 469 } 470 } 471 break; 472 case CMD_HOTLIST_AP_LOST: { 473 ScanResult[] results = (ScanResult[])msg.obj; 474 if (DBG) localLog("Lost " + results.length + " results"); 475 Collection<ClientInfo> clients = mClients.values(); 476 for (ClientInfo ci2 : clients) { 477 ci2.reportHotlistResults(WifiScanner.CMD_AP_LOST, results); 478 } 479 } 480 break; 481 case CMD_WIFI_CHANGE_DETECTED: { 482 ScanResult[] results = (ScanResult[])msg.obj; 483 reportWifiChanged(results); 484 } 485 break; 486 case CMD_WIFI_CHANGES_STABILIZED: { 487 ScanResult[] results = (ScanResult[])msg.obj; 488 reportWifiStabilized(results); 489 } 490 break; 491 case CMD_SCAN_PAUSED: { 492 ScanData results[] = (ScanData[]) msg.obj; 493 Collection<ClientInfo> clients = mClients.values(); 494 for (ClientInfo ci2 : clients) { 495 ci2.reportScanResults(results); 496 } 497 transitionTo(mPausedState); 498 } 499 break; 500 default: 501 return NOT_HANDLED; 502 } 503 504 return HANDLED; 505 } 506 } 507 508 class PausedState extends State { 509 @Override 510 public void enter() { 511 if (DBG) localLog("PausedState"); 512 } 513 514 @Override 515 public boolean processMessage(Message msg) { 516 517 if (DBG) localLog("PausedState got" + msg); 518 519 switch (msg.what) { 520 case CMD_SCAN_RESTARTED: 521 transitionTo(mStartedState); 522 break; 523 default: 524 deferMessage(msg); 525 break; 526 } 527 return HANDLED; 528 } 529 530 } 531 532 @Override 533 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 534 super.dump(fd, pw, args); 535 pw.println("number of clients : " + mClients.size()); 536 for (ClientInfo client : mClients.values()) { 537 pw.append(client.toString()); 538 pw.append("------\n"); 539 } 540 pw.println(); 541 pw.println("localLog : "); 542 mLocalLog.dump(fd, pw, args); 543 } 544 } 545 546 /* client management */ 547 HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>(); 548 549 private class ClientInfo { 550 private static final int MAX_LIMIT = 16; 551 private final AsyncChannel mChannel; 552 private final Messenger mMessenger; 553 private final int mUid; 554 private final WorkSource mWorkSource; 555 private boolean mScanWorkReported = false; 556 557 ClientInfo(int uid, AsyncChannel c, Messenger m) { 558 mChannel = c; 559 mMessenger = m; 560 mUid = uid; 561 mWorkSource = new WorkSource(uid, TAG); 562 if (DBG) localLog("New client, channel: " + c + " messenger: " + m); 563 } 564 565 void reportBatchedScanStart() { 566 if (mUid == 0) 567 return; 568 569 int csph = getCsph(); 570 571 try { 572 mBatteryStats.noteWifiBatchedScanStartedFromSource(mWorkSource, csph); 573 localLog("started scanning for UID " + mUid + ", csph = " + csph); 574 } catch (RemoteException e) { 575 logw("failed to report scan work: " + e.toString()); 576 } 577 } 578 579 void reportBatchedScanStop() { 580 if (mUid == 0) 581 return; 582 583 try { 584 mBatteryStats.noteWifiBatchedScanStoppedFromSource(mWorkSource); 585 localLog("stopped scanning for UID " + mUid); 586 } catch (RemoteException e) { 587 logw("failed to cleanup scan work: " + e.toString()); 588 } 589 } 590 591 int getCsph() { 592 int csph = 0; 593 for (ScanSettings settings : getScanSettings()) { 594 int num_channels = settings.channels == null ? 0 : settings.channels.length; 595 if (num_channels == 0 && settings.band != 0) { 596 num_channels = getChannelsForBand(settings.band).length; 597 } 598 599 int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) / settings.periodInMs; 600 csph += num_channels * scans_per_Hour; 601 } 602 603 return csph; 604 } 605 606 void reportScanWorkUpdate() { 607 if (mScanWorkReported) { 608 reportBatchedScanStop(); 609 mScanWorkReported = false; 610 } 611 if (mScanSettings.isEmpty() == false) { 612 reportBatchedScanStart(); 613 mScanWorkReported = true; 614 } 615 } 616 617 @Override 618 public String toString() { 619 StringBuffer sb = new StringBuffer(); 620 sb.append("mChannel ").append(mChannel).append("\n"); 621 sb.append("mMessenger ").append(mMessenger).append("\n\n"); 622 623 Iterator<Map.Entry<Integer, ScanSettings>> it = mScanSettings.entrySet().iterator(); 624 for (; it.hasNext(); ) { 625 Map.Entry<Integer, ScanSettings> entry = it.next(); 626 sb.append("ScanId ").append(entry.getKey()).append("\n"); 627 628 ScanSettings scanSettings = entry.getValue(); 629 sb.append(" band:").append(scanSettings.band); 630 sb.append(" period:").append(scanSettings.periodInMs); 631 sb.append(" reportEvents:").append(scanSettings.reportEvents); 632 sb.append(" numBssidsPerScan:").append(scanSettings.numBssidsPerScan); 633 sb.append(" maxScansToCache:").append(scanSettings.maxScansToCache).append("\n"); 634 635 sb.append(" channels: "); 636 637 if (scanSettings.channels != null) { 638 for (int i = 0; i < scanSettings.channels.length; i++) { 639 sb.append(scanSettings.channels[i].frequency); 640 sb.append(" "); 641 } 642 } 643 644 sb.append("\n\n"); 645 } 646 647 return sb.toString(); 648 } 649 650 HashMap<Integer, ScanSettings> mScanSettings = new HashMap<Integer, ScanSettings>(4); 651 HashMap<Integer, Integer> mScanPeriods = new HashMap<Integer, Integer>(4); 652 653 void addScanRequest(ScanSettings settings, int id) { 654 mScanSettings.put(id, settings); 655 reportScanWorkUpdate(); 656 } 657 658 void removeScanRequest(int id) { 659 ScanSettings settings = mScanSettings.remove(id); 660 if (settings != null && settings.periodInMs == 0) { 661 /* this was a single shot scan */ 662 mChannel.sendMessage(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, id); 663 } 664 reportScanWorkUpdate(); 665 } 666 667 Iterator<Map.Entry<Integer, ScanSettings>> getScans() { 668 return mScanSettings.entrySet().iterator(); 669 } 670 671 Collection<ScanSettings> getScanSettings() { 672 return mScanSettings.values(); 673 } 674 675 void reportScanResults(ScanData[] results) { 676 Iterator<Integer> it = mScanSettings.keySet().iterator(); 677 while (it.hasNext()) { 678 int handler = it.next(); 679 reportScanResults(results, handler); 680 } 681 } 682 683 void reportScanResults(ScanData[] results, int handler) { 684 ScanSettings settings = mScanSettings.get(handler); 685 ChannelSpec desiredChannels[] = settings.channels; 686 if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED 687 || desiredChannels == null || desiredChannels.length == 0) { 688 desiredChannels = getChannelsForBand(settings.band); 689 } 690 691 // check the channels this client asked for .. 692 int num_results = 0; 693 for (ScanData result : results) { 694 boolean copyScanData = false; 695 for (ScanResult scanResult : result.getResults()) { 696 for (ChannelSpec channelSpec : desiredChannels) { 697 if (channelSpec.frequency == scanResult.frequency) { 698 copyScanData = true; 699 break; 700 } 701 } 702 if (copyScanData) { 703 num_results++; 704 break; 705 } 706 } 707 } 708 709 localLog("results = " + results.length + ", num_results = " + num_results); 710 711 ScanData results2[] = new ScanData[num_results]; 712 int index = 0; 713 for (ScanData result : results) { 714 boolean copyScanData = false; 715 for (ScanResult scanResult : result.getResults()) { 716 for (ChannelSpec channelSpec : desiredChannels) { 717 if (channelSpec.frequency == scanResult.frequency) { 718 copyScanData = true; 719 break; 720 } 721 } 722 if (copyScanData) { 723 break; 724 } 725 } 726 727 if (copyScanData) { 728 if (VDBG) { 729 localLog("adding at " + index); 730 } 731 results2[index] = new WifiScanner.ScanData(result); 732 index++; 733 } 734 } 735 736 localLog("delivering results, num = " + results2.length); 737 738 deliverScanResults(handler, results2); 739 if (settings.periodInMs == 0) { 740 /* this is a single shot scan; stop the scan now */ 741 mStateMachine.sendMessage(CMD_STOP_SCAN_INTERNAL, 0, handler, this); 742 } 743 } 744 745 void deliverScanResults(int handler, ScanData results[]) { 746 WifiScanner.ParcelableScanData parcelableScanData = 747 new WifiScanner.ParcelableScanData(results); 748 mChannel.sendMessage(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData); 749 } 750 751 void reportFullScanResult(ScanResult result) { 752 Iterator<Integer> it = mScanSettings.keySet().iterator(); 753 while (it.hasNext()) { 754 int handler = it.next(); 755 ScanSettings settings = mScanSettings.get(handler); 756 ChannelSpec desiredChannels[] = settings.channels; 757 if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED 758 || desiredChannels == null || desiredChannels.length == 0) { 759 desiredChannels = getChannelsForBand(settings.band); 760 } 761 for (ChannelSpec channelSpec : desiredChannels) { 762 if (channelSpec.frequency == result.frequency) { 763 WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(result.SSID); 764 ScanResult newResult = new ScanResult(wifiSsid, result.BSSID, "", 765 result.level, result.frequency, result.timestamp, 766 ScanResult.UNSPECIFIED, ScanResult.UNSPECIFIED,result.channelWidth, 767 result.centerFreq0, result.centerFreq1, 768 result.is80211McRTTResponder); 769 if (DBG) localLog("sending it to " + handler); 770 newResult.informationElements = result.informationElements.clone(); 771 mChannel.sendMessage( 772 WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, newResult); 773 } 774 } 775 } 776 } 777 778 void reportPeriodChanged(int handler, ScanSettings settings, int newPeriodInMs) { 779 Integer prevPeriodObject = mScanPeriods.get(handler); 780 int prevPeriodInMs = settings.periodInMs; 781 if (prevPeriodObject != null) { 782 prevPeriodInMs = prevPeriodObject; 783 } 784 785 if (prevPeriodInMs != newPeriodInMs) { 786 mChannel.sendMessage(WifiScanner.CMD_PERIOD_CHANGED, newPeriodInMs, handler); 787 } 788 } 789 790 HashMap<Integer, WifiScanner.HotlistSettings> mHotlistSettings = 791 new HashMap<Integer, WifiScanner.HotlistSettings>(); 792 793 void addHostlistSettings(WifiScanner.HotlistSettings settings, int handler) { 794 mHotlistSettings.put(handler, settings); 795 } 796 797 void removeHostlistSettings(int handler) { 798 mHotlistSettings.remove(handler); 799 } 800 801 Collection<WifiScanner.HotlistSettings> getHotlistSettings() { 802 return mHotlistSettings.values(); 803 } 804 805 void reportHotlistResults(int what, ScanResult[] results) { 806 Iterator<Map.Entry<Integer, WifiScanner.HotlistSettings>> it = 807 mHotlistSettings.entrySet().iterator(); 808 while (it.hasNext()) { 809 Map.Entry<Integer, WifiScanner.HotlistSettings> entry = it.next(); 810 int handler = entry.getKey(); 811 WifiScanner.HotlistSettings settings = entry.getValue(); 812 int num_results = 0; 813 for (ScanResult result : results) { 814 for (BssidInfo BssidInfo : settings.bssidInfos) { 815 if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) { 816 num_results++; 817 break; 818 } 819 } 820 } 821 822 if (num_results == 0) { 823 // nothing to report 824 return; 825 } 826 827 ScanResult results2[] = new ScanResult[num_results]; 828 int index = 0; 829 for (ScanResult result : results) { 830 for (BssidInfo BssidInfo : settings.bssidInfos) { 831 if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) { 832 results2[index] = result; 833 index++; 834 } 835 } 836 } 837 838 WifiScanner.ParcelableScanResults parcelableScanResults = 839 new WifiScanner.ParcelableScanResults(results2); 840 841 mChannel.sendMessage(what, 0, handler, parcelableScanResults); 842 } 843 } 844 845 HashSet<Integer> mSignificantWifiHandlers = new HashSet<Integer>(); 846 void addSignificantWifiChange(int handler) { 847 mSignificantWifiHandlers.add(handler); 848 } 849 850 void removeSignificantWifiChange(int handler) { 851 mSignificantWifiHandlers.remove(handler); 852 } 853 854 Collection<Integer> getWifiChangeHandlers() { 855 return mSignificantWifiHandlers; 856 } 857 858 void reportWifiChanged(ScanResult[] results) { 859 WifiScanner.ParcelableScanResults parcelableScanResults = 860 new WifiScanner.ParcelableScanResults(results); 861 Iterator<Integer> it = mSignificantWifiHandlers.iterator(); 862 while (it.hasNext()) { 863 int handler = it.next(); 864 mChannel.sendMessage(WifiScanner.CMD_WIFI_CHANGE_DETECTED, 865 0, handler, parcelableScanResults); 866 } 867 } 868 869 void reportWifiStabilized(ScanResult[] results) { 870 WifiScanner.ParcelableScanResults parcelableScanResults = 871 new WifiScanner.ParcelableScanResults(results); 872 Iterator<Integer> it = mSignificantWifiHandlers.iterator(); 873 while (it.hasNext()) { 874 int handler = it.next(); 875 mChannel.sendMessage(WifiScanner.CMD_WIFI_CHANGES_STABILIZED, 876 0, handler, parcelableScanResults); 877 } 878 } 879 880 void cleanup() { 881 mScanSettings.clear(); 882 resetBuckets(); 883 884 mHotlistSettings.clear(); 885 resetHotlist(); 886 887 for (Integer handler : mSignificantWifiHandlers) { 888 untrackWifiChanges(this, handler); 889 } 890 891 mSignificantWifiHandlers.clear(); 892 localLog("Successfully stopped all requests for client " + this); 893 } 894 } 895 896 void replySucceeded(Message msg) { 897 if (msg.replyTo != null) { 898 Message reply = Message.obtain(); 899 reply.what = WifiScanner.CMD_OP_SUCCEEDED; 900 reply.arg2 = msg.arg2; 901 try { 902 msg.replyTo.send(reply); 903 } catch (RemoteException e) { 904 // There's not much we can do if reply can't be sent! 905 } 906 } else { 907 // locally generated message; doesn't need a reply! 908 } 909 } 910 911 void replyFailed(Message msg, int reason, String description) { 912 if (msg.replyTo != null) { 913 Message reply = Message.obtain(); 914 reply.what = WifiScanner.CMD_OP_FAILED; 915 reply.arg2 = msg.arg2; 916 reply.obj = new WifiScanner.OperationResult(reason, description); 917 try { 918 msg.replyTo.send(reply); 919 } catch (RemoteException e) { 920 // There's not much we can do if reply can't be sent! 921 } 922 } else { 923 // locally generated message; doesn't need a reply! 924 } 925 } 926 927 private class SettingsComputer { 928 929 private class TimeBucket { 930 int periodInSecond; 931 int periodMinInSecond; 932 int periodMaxInSecond; 933 934 TimeBucket(int p, int min, int max) { 935 periodInSecond = p; 936 periodMinInSecond = min; 937 periodMaxInSecond = max; 938 } 939 } 940 941 private final TimeBucket[] mTimeBuckets = new TimeBucket[] { 942 new TimeBucket( 1, 0, 5 ), 943 new TimeBucket( 5, 5, 10 ), 944 new TimeBucket( 10, 10, 25 ), 945 new TimeBucket( 30, 25, 55 ), 946 new TimeBucket( 60, 55, 100), 947 new TimeBucket( 300, 240, 500), 948 new TimeBucket( 600, 500, 1500), 949 new TimeBucket( 1800, 1500, WifiScanner.MAX_SCAN_PERIOD_MS) }; 950 951 private static final int MAX_BUCKETS = 8; 952 private static final int MAX_CHANNELS = 8; 953 private static final int DEFAULT_MAX_AP_PER_SCAN = 10; 954 private static final int DEFAULT_REPORT_THRESHOLD_PERCENT = 100; 955 private static final int DEFAULT_BASE_PERIOD_MS = 5000; 956 private static final int DEFAULT_REPORT_THRESHOLD_NUM_SCANS = 10; 957 958 private WifiNative.ScanSettings mSettings; 959 { 960 mSettings = new WifiNative.ScanSettings(); 961 mSettings.max_ap_per_scan = DEFAULT_MAX_AP_PER_SCAN; 962 mSettings.base_period_ms = DEFAULT_BASE_PERIOD_MS; 963 mSettings.report_threshold_percent = DEFAULT_REPORT_THRESHOLD_PERCENT; 964 mSettings.report_threshold_num_scans = DEFAULT_REPORT_THRESHOLD_NUM_SCANS; 965 966 mSettings.buckets = new WifiNative.BucketSettings[MAX_BUCKETS]; 967 for (int i = 0; i < mSettings.buckets.length; i++) { 968 WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); 969 bucketSettings.bucket = i; 970 bucketSettings.report_events = 0; 971 bucketSettings.channels = new WifiNative.ChannelSettings[MAX_CHANNELS]; 972 bucketSettings.num_channels = 0; 973 for (int j = 0; j < bucketSettings.channels.length; j++) { 974 WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings(); 975 bucketSettings.channels[j] = channelSettings; 976 } 977 mSettings.buckets[i] = bucketSettings; 978 } 979 } 980 981 HashMap<Integer, Integer> mChannelToBucketMap = new HashMap<Integer, Integer>(); 982 983 private int getBestBucket(ScanSettings settings) { 984 985 // check to see if any of the channels are being scanned already 986 // and find the smallest bucket index (it represents the quickest 987 // period of scan) 988 989 ChannelSpec channels[] = settings.channels; 990 if (channels == null) { 991 // set channels based on band 992 channels = getChannelsForBand(settings.band); 993 } 994 995 if (channels == null) { 996 // still no channels; then there's nothing to scan 997 loge("No channels to scan!!"); 998 return -1; 999 } 1000 1001 int mostFrequentBucketIndex = mTimeBuckets.length; 1002 1003 for (ChannelSpec desiredChannelSpec : channels) { 1004 if (mChannelToBucketMap.containsKey(desiredChannelSpec.frequency)) { 1005 int bucket = mChannelToBucketMap.get(desiredChannelSpec.frequency); 1006 if (bucket < mostFrequentBucketIndex) { 1007 mostFrequentBucketIndex = bucket; 1008 } 1009 } 1010 } 1011 1012 int bestBucketIndex = -1; // best by period 1013 for (int i = 0; i < mTimeBuckets.length; i++) { 1014 TimeBucket bucket = mTimeBuckets[i]; 1015 if (bucket.periodMinInSecond * 1000 <= settings.periodInMs 1016 && settings.periodInMs < bucket.periodMaxInSecond * 1000) { 1017 // we set the time period to this 1018 bestBucketIndex = i; 1019 break; 1020 } 1021 } 1022 1023 if (mostFrequentBucketIndex < bestBucketIndex) { 1024 for (ChannelSpec desiredChannelSpec : channels) { 1025 mChannelToBucketMap.put(desiredChannelSpec.frequency, mostFrequentBucketIndex); 1026 } 1027 localLog("returning mf bucket number " + mostFrequentBucketIndex); 1028 return mostFrequentBucketIndex; 1029 } else if (bestBucketIndex != -1) { 1030 for (ChannelSpec desiredChannelSpec : channels) { 1031 mChannelToBucketMap.put(desiredChannelSpec.frequency, bestBucketIndex); 1032 } 1033 localLog("returning best bucket number " + bestBucketIndex); 1034 return bestBucketIndex; 1035 } 1036 1037 loge("Could not find suitable bucket for period " + settings.periodInMs); 1038 return -1; 1039 } 1040 1041 void prepChannelMap(ScanSettings settings) { 1042 getBestBucket(settings); 1043 } 1044 1045 int addScanRequestToBucket(ScanSettings settings) { 1046 1047 int bucketIndex = getBestBucket(settings); 1048 if (bucketIndex == -1) { 1049 loge("Ignoring invalid settings"); 1050 return -1; 1051 } 1052 1053 ChannelSpec desiredChannels[] = settings.channels; 1054 if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED 1055 || desiredChannels == null 1056 || desiredChannels.length == 0) { 1057 // set channels based on band 1058 desiredChannels = getChannelsForBand(settings.band); 1059 if (desiredChannels == null) { 1060 // still no channels; then there's nothing to scan 1061 loge("No channels to scan!!"); 1062 return -1; 1063 } 1064 } 1065 1066 // merge the channel lists for these buckets 1067 localLog("merging " + desiredChannels.length + " channels " 1068 + " for period " + settings.periodInMs 1069 + " maxScans " + settings.maxScansToCache); 1070 1071 WifiNative.BucketSettings bucket = mSettings.buckets[bucketIndex]; 1072 boolean added = (bucket.num_channels == 0) 1073 && (bucket.band == WifiScanner.WIFI_BAND_UNSPECIFIED); 1074 localLog("existing " + bucket.num_channels + " channels "); 1075 1076 HashSet<ChannelSpec> newChannels = new HashSet<ChannelSpec>(); 1077 for (ChannelSpec desiredChannelSpec : desiredChannels) { 1078 1079 localLog("desired channel " + desiredChannelSpec.frequency); 1080 1081 boolean found = false; 1082 for (WifiNative.ChannelSettings existingChannelSpec : bucket.channels) { 1083 if (desiredChannelSpec.frequency == existingChannelSpec.frequency) { 1084 found = true; 1085 break; 1086 } 1087 } 1088 1089 if (!found) { 1090 newChannels.add(desiredChannelSpec); 1091 } else { 1092 if (DBG) localLog("Already scanning channel " + desiredChannelSpec.frequency); 1093 } 1094 } 1095 1096 if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED 1097 || (bucket.num_channels + newChannels.size()) > bucket.channels.length) { 1098 // can't accommodate all channels; switch to specifying band 1099 bucket.num_channels = 0; 1100 bucket.band = getBandFromChannels(bucket.channels) 1101 | getBandFromChannels(desiredChannels); 1102 bucket.channels = new WifiNative.ChannelSettings[0]; 1103 localLog("switching to using band " + bucket.band); 1104 } else { 1105 for (ChannelSpec desiredChannelSpec : newChannels) { 1106 1107 localLog("adding new channel spec " + desiredChannelSpec.frequency); 1108 1109 WifiNative.ChannelSettings channelSettings = bucket.channels[bucket.num_channels]; 1110 channelSettings.frequency = desiredChannelSpec.frequency; 1111 bucket.num_channels++; 1112 mChannelToBucketMap.put(bucketIndex, channelSettings.frequency); 1113 } 1114 } 1115 1116 if (bucket.report_events < settings.reportEvents) { 1117 if (DBG) localLog("setting report_events to " + settings.reportEvents); 1118 bucket.report_events = settings.reportEvents; 1119 } else { 1120 if (DBG) localLog("report_events is " + settings.reportEvents); 1121 } 1122 1123 if (added) { 1124 bucket.period_ms = mTimeBuckets[bucketIndex].periodInSecond * 1000; 1125 mSettings.num_buckets++; 1126 } 1127 1128 if (mSettings.max_ap_per_scan < settings.numBssidsPerScan) { 1129 mSettings.max_ap_per_scan = settings.numBssidsPerScan; 1130 } 1131 1132 if (settings.maxScansToCache != 0) { 1133 if (mSettings.report_threshold_num_scans > settings.maxScansToCache) { 1134 mSettings.report_threshold_num_scans = settings.maxScansToCache; 1135 } 1136 } 1137 1138 return bucket.period_ms; 1139 } 1140 1141 public WifiNative.ScanSettings getComputedSettings() { 1142 return mSettings; 1143 } 1144 1145 public void compressBuckets() { 1146 int num_buckets = 0; 1147 for (int i = 0; i < mSettings.buckets.length; i++) { 1148 if (mSettings.buckets[i].num_channels != 0 1149 || mSettings.buckets[i].band != WifiScanner.WIFI_BAND_UNSPECIFIED) { 1150 mSettings.buckets[num_buckets] = mSettings.buckets[i]; 1151 num_buckets++; 1152 } 1153 } 1154 // remove unused buckets 1155 for (int i = num_buckets; i < mSettings.buckets.length; i++) { 1156 mSettings.buckets[i] = null; 1157 } 1158 1159 mSettings.num_buckets = num_buckets; 1160 if (num_buckets != 0) { 1161 mSettings.base_period_ms = mSettings.buckets[0].period_ms; 1162 } 1163 } 1164 } 1165 1166 boolean resetBuckets() { 1167 loge("resetBuckets num clients: " + mClients.size()); 1168 SettingsComputer c = new SettingsComputer(); 1169 Collection<ClientInfo> clients = mClients.values(); 1170 for (ClientInfo ci : clients) { 1171 Collection<ScanSettings> settings = ci.getScanSettings(); 1172 for (ScanSettings s : settings) { 1173 c.prepChannelMap(s); 1174 } 1175 } 1176 1177 for (ClientInfo ci : clients) { 1178 Iterator it = ci.getScans(); 1179 while (it.hasNext()) { 1180 Map.Entry<Integer, ScanSettings> entry = 1181 (Map.Entry<Integer,ScanSettings>)it.next(); 1182 int id = entry.getKey(); 1183 ScanSettings s = entry.getValue(); 1184 int newPeriodInMs = c.addScanRequestToBucket(s); 1185 if (newPeriodInMs == -1) { 1186 if (DBG) localLog("could not find a good bucket"); 1187 return false; 1188 } 1189 if (newPeriodInMs != s.periodInMs) { 1190 ci.reportPeriodChanged(id, s, newPeriodInMs); 1191 } 1192 } 1193 } 1194 1195 c.compressBuckets(); 1196 1197 WifiNative.ScanSettings s = c.getComputedSettings(); 1198 if (s.num_buckets == 0) { 1199 if (DBG) localLog("Stopping scan because there are no buckets"); 1200 WifiNative.stopScan(); 1201 return true; 1202 } else { 1203 if (WifiNative.startScan(s, mStateMachine)) { 1204 localLog("Successfully started scan of " + s.num_buckets + " buckets at" 1205 + "time = " + SystemClock.elapsedRealtimeNanos() / 1000 + " period " 1206 + s.base_period_ms); 1207 return true; 1208 } else { 1209 loge("Failed to start scan of " + s.num_buckets + " buckets"); 1210 return false; 1211 } 1212 } 1213 } 1214 1215 boolean addScanRequest(ClientInfo ci, int handler, ScanSettings settings) { 1216 // sanity check the input 1217 if (settings.periodInMs < WifiScanner.MIN_SCAN_PERIOD_MS) { 1218 localLog("Failing scan request because periodInMs is " + settings.periodInMs); 1219 return false; 1220 } 1221 1222 int minSupportedPeriodMs = 0; 1223 if (settings.channels != null) { 1224 minSupportedPeriodMs = settings.channels.length * MIN_PERIOD_PER_CHANNEL_MS; 1225 } else { 1226 if ((settings.band & WifiScanner.WIFI_BAND_24_GHZ) == 0) { 1227 /* 2.4 GHz band has 11 to 13 channels */ 1228 minSupportedPeriodMs += 1000; 1229 } 1230 if ((settings.band & WifiScanner.WIFI_BAND_5_GHZ) == 0) { 1231 /* 5 GHz band has another 10 channels */ 1232 minSupportedPeriodMs += 1000; 1233 } 1234 if ((settings.band & WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY) == 0) { 1235 /* DFS requires passive scan which takes longer time */ 1236 minSupportedPeriodMs += 2000; 1237 } 1238 } 1239 1240 if (settings.periodInMs < minSupportedPeriodMs) { 1241 localLog("Failing scan request because minSupportedPeriodMs is " 1242 + minSupportedPeriodMs + " but the request wants " + settings.periodInMs); 1243 return false; 1244 } 1245 1246 ci.addScanRequest(settings, handler); 1247 if (resetBuckets()) { 1248 return true; 1249 } else { 1250 ci.removeScanRequest(handler); 1251 localLog("Failing scan request because failed to reset scan"); 1252 return false; 1253 } 1254 } 1255 1256 boolean addSingleScanRequest(ClientInfo ci, int handler, ScanSettings settings) { 1257 if (settings.reportEvents == 0) { 1258 settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 1259 } 1260 if (settings.periodInMs == 0) { 1261 settings.periodInMs = 10000; // 10s - although second scan should never happen 1262 } 1263 ci.addScanRequest(settings, handler); 1264 if (resetBuckets()) { 1265 /* reset periodInMs to 0 to indicate single shot scan */ 1266 settings.periodInMs = 0; 1267 return true; 1268 } else { 1269 ci.removeScanRequest(handler); 1270 localLog("Failing scan request because failed to reset scan"); 1271 return false; 1272 } 1273 } 1274 1275 void removeScanRequest(ClientInfo ci, int handler) { 1276 ci.removeScanRequest(handler); 1277 resetBuckets(); 1278 } 1279 1280 boolean reportScanResults() { 1281 ScanData results[] = WifiNative.getScanResults(/* flush = */ true); 1282 Collection<ClientInfo> clients = mClients.values(); 1283 for (ClientInfo ci2 : clients) { 1284 ci2.reportScanResults(results); 1285 } 1286 1287 return true; 1288 } 1289 1290 void resetHotlist() { 1291 Collection<ClientInfo> clients = mClients.values(); 1292 int num_hotlist_ap = 0; 1293 1294 for (ClientInfo ci : clients) { 1295 Collection<WifiScanner.HotlistSettings> c = ci.getHotlistSettings(); 1296 for (WifiScanner.HotlistSettings s : c) { 1297 num_hotlist_ap += s.bssidInfos.length; 1298 } 1299 } 1300 1301 if (num_hotlist_ap == 0) { 1302 WifiNative.resetHotlist(); 1303 } else { 1304 BssidInfo bssidInfos[] = new BssidInfo[num_hotlist_ap]; 1305 int index = 0; 1306 for (ClientInfo ci : clients) { 1307 Collection<WifiScanner.HotlistSettings> settings = ci.getHotlistSettings(); 1308 for (WifiScanner.HotlistSettings s : settings) { 1309 for (int i = 0; i < s.bssidInfos.length; i++, index++) { 1310 bssidInfos[index] = s.bssidInfos[i]; 1311 } 1312 } 1313 } 1314 1315 WifiScanner.HotlistSettings settings = new WifiScanner.HotlistSettings(); 1316 settings.bssidInfos = bssidInfos; 1317 settings.apLostThreshold = 3; 1318 WifiNative.setHotlist(settings, mStateMachine); 1319 } 1320 } 1321 1322 void setHotlist(ClientInfo ci, int handler, WifiScanner.HotlistSettings settings) { 1323 ci.addHostlistSettings(settings, handler); 1324 resetHotlist(); 1325 } 1326 1327 void resetHotlist(ClientInfo ci, int handler) { 1328 ci.removeHostlistSettings(handler); 1329 resetHotlist(); 1330 } 1331 1332 WifiChangeStateMachine mWifiChangeStateMachine; 1333 1334 void trackWifiChanges(ClientInfo ci, int handler) { 1335 mWifiChangeStateMachine.enable(); 1336 ci.addSignificantWifiChange(handler); 1337 } 1338 1339 void untrackWifiChanges(ClientInfo ci, int handler) { 1340 ci.removeSignificantWifiChange(handler); 1341 Collection<ClientInfo> clients = mClients.values(); 1342 for (ClientInfo ci2 : clients) { 1343 if (ci2.getWifiChangeHandlers().size() != 0) { 1344 // there is at least one client watching for 1345 // significant changes; so nothing more to do 1346 return; 1347 } 1348 } 1349 1350 // no more clients looking for significant wifi changes 1351 // no need to keep the state machine running; disable it 1352 mWifiChangeStateMachine.disable(); 1353 } 1354 1355 void configureWifiChange(WifiScanner.WifiChangeSettings settings) { 1356 mWifiChangeStateMachine.configure(settings); 1357 } 1358 1359 void reportWifiChanged(ScanResult results[]) { 1360 Collection<ClientInfo> clients = mClients.values(); 1361 for (ClientInfo ci : clients) { 1362 ci.reportWifiChanged(results); 1363 } 1364 } 1365 1366 void reportWifiStabilized(ScanResult results[]) { 1367 Collection<ClientInfo> clients = mClients.values(); 1368 for (ClientInfo ci : clients) { 1369 ci.reportWifiStabilized(results); 1370 } 1371 } 1372 1373 class WifiChangeStateMachine extends StateMachine 1374 implements WifiNative.SignificantWifiChangeEventHandler { 1375 1376 private static final String TAG = "WifiChangeStateMachine"; 1377 1378 private static final int WIFI_CHANGE_CMD_NEW_SCAN_RESULTS = 0; 1379 private static final int WIFI_CHANGE_CMD_CHANGE_DETECTED = 1; 1380 private static final int WIFI_CHANGE_CMD_CHANGE_TIMEOUT = 2; 1381 private static final int WIFI_CHANGE_CMD_ENABLE = 3; 1382 private static final int WIFI_CHANGE_CMD_DISABLE = 4; 1383 private static final int WIFI_CHANGE_CMD_CONFIGURE = 5; 1384 1385 private static final int MAX_APS_TO_TRACK = 3; 1386 private static final int MOVING_SCAN_PERIOD_MS = 10000; 1387 private static final int STATIONARY_SCAN_PERIOD_MS = 5000; 1388 private static final int MOVING_STATE_TIMEOUT_MS = 30000; 1389 1390 State mDefaultState = new DefaultState(); 1391 State mStationaryState = new StationaryState(); 1392 State mMovingState = new MovingState(); 1393 1394 private static final String ACTION_TIMEOUT = 1395 "com.android.server.WifiScanningServiceImpl.action.TIMEOUT"; 1396 AlarmManager mAlarmManager; 1397 PendingIntent mTimeoutIntent; 1398 ScanResult mCurrentBssids[]; 1399 1400 WifiChangeStateMachine(Looper looper) { 1401 super("SignificantChangeStateMachine", looper); 1402 1403 mClients.put(null, mClientInfo); 1404 1405 addState(mDefaultState); 1406 addState(mStationaryState, mDefaultState); 1407 addState(mMovingState, mDefaultState); 1408 1409 setInitialState(mDefaultState); 1410 } 1411 1412 public void enable() { 1413 if (mAlarmManager == null) { 1414 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 1415 } 1416 1417 if (mTimeoutIntent == null) { 1418 Intent intent = new Intent(ACTION_TIMEOUT, null); 1419 mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 1420 1421 mContext.registerReceiver( 1422 new BroadcastReceiver() { 1423 @Override 1424 public void onReceive(Context context, Intent intent) { 1425 sendMessage(WIFI_CHANGE_CMD_CHANGE_TIMEOUT); 1426 } 1427 }, new IntentFilter(ACTION_TIMEOUT)); 1428 } 1429 1430 sendMessage(WIFI_CHANGE_CMD_ENABLE); 1431 } 1432 1433 public void disable() { 1434 sendMessage(WIFI_CHANGE_CMD_DISABLE); 1435 } 1436 1437 public void configure(WifiScanner.WifiChangeSettings settings) { 1438 sendMessage(WIFI_CHANGE_CMD_CONFIGURE, settings); 1439 } 1440 1441 class DefaultState extends State { 1442 @Override 1443 public void enter() { 1444 if (DBG) localLog("Entering IdleState"); 1445 } 1446 1447 @Override 1448 public boolean processMessage(Message msg) { 1449 if (DBG) localLog("DefaultState state got " + msg); 1450 switch (msg.what) { 1451 case WIFI_CHANGE_CMD_ENABLE : 1452 transitionTo(mMovingState); 1453 break; 1454 case WIFI_CHANGE_CMD_DISABLE: 1455 // nothing to do 1456 break; 1457 case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS: 1458 // nothing to do 1459 break; 1460 case WIFI_CHANGE_CMD_CONFIGURE: 1461 /* save configuration till we transition to moving state */ 1462 deferMessage(msg); 1463 break; 1464 default: 1465 return NOT_HANDLED; 1466 } 1467 return HANDLED; 1468 } 1469 } 1470 1471 class StationaryState extends State { 1472 @Override 1473 public void enter() { 1474 if (DBG) localLog("Entering StationaryState"); 1475 reportWifiStabilized(mCurrentBssids); 1476 } 1477 1478 @Override 1479 public boolean processMessage(Message msg) { 1480 if (DBG) localLog("Stationary state got " + msg); 1481 switch (msg.what) { 1482 case WIFI_CHANGE_CMD_ENABLE : 1483 // do nothing 1484 break; 1485 case WIFI_CHANGE_CMD_CHANGE_DETECTED: 1486 if (DBG) localLog("Got wifi change detected"); 1487 reportWifiChanged((ScanResult[])msg.obj); 1488 transitionTo(mMovingState); 1489 break; 1490 case WIFI_CHANGE_CMD_DISABLE: 1491 if (DBG) localLog("Got Disable Wifi Change"); 1492 mCurrentBssids = null; 1493 removeScanRequest(); 1494 untrackSignificantWifiChange(); 1495 transitionTo(mDefaultState); 1496 break; 1497 case WIFI_CHANGE_CMD_CONFIGURE: 1498 /* save configuration till we transition to moving state */ 1499 deferMessage(msg); 1500 break; 1501 default: 1502 return NOT_HANDLED; 1503 } 1504 return HANDLED; 1505 } 1506 } 1507 1508 class MovingState extends State { 1509 boolean mWifiChangeDetected = false; 1510 boolean mScanResultsPending = false; 1511 1512 @Override 1513 public void enter() { 1514 if (DBG) localLog("Entering MovingState"); 1515 issueFullScan(); 1516 } 1517 1518 @Override 1519 public boolean processMessage(Message msg) { 1520 if (DBG) localLog("MovingState state got " + msg); 1521 switch (msg.what) { 1522 case WIFI_CHANGE_CMD_ENABLE : 1523 // do nothing 1524 break; 1525 case WIFI_CHANGE_CMD_DISABLE: 1526 if (DBG) localLog("Got Disable Wifi Change"); 1527 mCurrentBssids = null; 1528 removeScanRequest(); 1529 untrackSignificantWifiChange(); 1530 transitionTo(mDefaultState); 1531 break; 1532 case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS: 1533 if (DBG) localLog("Got scan results"); 1534 if (mScanResultsPending) { 1535 if (DBG) localLog("reconfiguring scan"); 1536 reconfigureScan((ScanData[])msg.obj, 1537 STATIONARY_SCAN_PERIOD_MS); 1538 mWifiChangeDetected = false; 1539 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, 1540 System.currentTimeMillis() + MOVING_STATE_TIMEOUT_MS, 1541 mTimeoutIntent); 1542 mScanResultsPending = false; 1543 } 1544 break; 1545 case WIFI_CHANGE_CMD_CONFIGURE: 1546 if (DBG) localLog("Got configuration from app"); 1547 WifiScanner.WifiChangeSettings settings = 1548 (WifiScanner.WifiChangeSettings) msg.obj; 1549 reconfigureScan(settings); 1550 mWifiChangeDetected = false; 1551 long unchangedDelay = settings.unchangedSampleSize * settings.periodInMs; 1552 mAlarmManager.cancel(mTimeoutIntent); 1553 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, 1554 System.currentTimeMillis() + unchangedDelay, 1555 mTimeoutIntent); 1556 break; 1557 case WIFI_CHANGE_CMD_CHANGE_DETECTED: 1558 if (DBG) localLog("Change detected"); 1559 mAlarmManager.cancel(mTimeoutIntent); 1560 reportWifiChanged((ScanResult[])msg.obj); 1561 mWifiChangeDetected = true; 1562 issueFullScan(); 1563 break; 1564 case WIFI_CHANGE_CMD_CHANGE_TIMEOUT: 1565 if (DBG) localLog("Got timeout event"); 1566 if (mWifiChangeDetected == false) { 1567 transitionTo(mStationaryState); 1568 } 1569 break; 1570 default: 1571 return NOT_HANDLED; 1572 } 1573 return HANDLED; 1574 } 1575 1576 @Override 1577 public void exit() { 1578 mAlarmManager.cancel(mTimeoutIntent); 1579 } 1580 1581 void issueFullScan() { 1582 if (DBG) localLog("Issuing full scan"); 1583 ScanSettings settings = new ScanSettings(); 1584 settings.band = WifiScanner.WIFI_BAND_BOTH; 1585 settings.periodInMs = MOVING_SCAN_PERIOD_MS; 1586 settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 1587 addScanRequest(settings); 1588 mScanResultsPending = true; 1589 } 1590 1591 } 1592 1593 void reconfigureScan(ScanData[] results, int period) { 1594 // find brightest APs and set them as sentinels 1595 if (results.length < MAX_APS_TO_TRACK) { 1596 localLog("too few APs (" + results.length + ") available to track wifi change"); 1597 return; 1598 } 1599 1600 removeScanRequest(); 1601 1602 // remove duplicate BSSIDs 1603 HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>(); 1604 for (ScanResult result : results[0].getResults()) { 1605 ScanResult saved = bssidToScanResult.get(result.BSSID); 1606 if (saved == null) { 1607 bssidToScanResult.put(result.BSSID, result); 1608 } else if (saved.level > result.level) { 1609 bssidToScanResult.put(result.BSSID, result); 1610 } 1611 } 1612 1613 // find brightest BSSIDs 1614 ScanResult brightest[] = new ScanResult[MAX_APS_TO_TRACK]; 1615 Collection<ScanResult> results2 = bssidToScanResult.values(); 1616 for (ScanResult result : results2) { 1617 for (int j = 0; j < brightest.length; j++) { 1618 if (brightest[j] == null 1619 || (brightest[j].level < result.level)) { 1620 for (int k = brightest.length; k > (j + 1); k--) { 1621 brightest[k - 1] = brightest[k - 2]; 1622 } 1623 brightest[j] = result; 1624 break; 1625 } 1626 } 1627 } 1628 1629 // Get channels to scan for 1630 ArrayList<Integer> channels = new ArrayList<Integer>(); 1631 for (int i = 0; i < brightest.length; i++) { 1632 boolean found = false; 1633 for (int j = i + 1; j < brightest.length; j++) { 1634 if (brightest[j].frequency == brightest[i].frequency) { 1635 found = true; 1636 } 1637 } 1638 if (!found) { 1639 channels.add(brightest[i].frequency); 1640 } 1641 } 1642 1643 if (DBG) localLog("Found " + channels.size() + " channels"); 1644 1645 // set scanning schedule 1646 ScanSettings settings = new ScanSettings(); 1647 settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED; 1648 settings.channels = new ChannelSpec[channels.size()]; 1649 for (int i = 0; i < channels.size(); i++) { 1650 settings.channels[i] = new ChannelSpec(channels.get(i)); 1651 } 1652 1653 settings.periodInMs = period; 1654 addScanRequest(settings); 1655 1656 WifiScanner.WifiChangeSettings settings2 = new WifiScanner.WifiChangeSettings(); 1657 settings2.rssiSampleSize = 3; 1658 settings2.lostApSampleSize = 3; 1659 settings2.unchangedSampleSize = 3; 1660 settings2.minApsBreachingThreshold = 2; 1661 settings2.bssidInfos = new BssidInfo[brightest.length]; 1662 1663 for (int i = 0; i < brightest.length; i++) { 1664 BssidInfo BssidInfo = new BssidInfo(); 1665 BssidInfo.bssid = brightest[i].BSSID; 1666 int threshold = (100 + brightest[i].level) / 32 + 2; 1667 BssidInfo.low = brightest[i].level - threshold; 1668 BssidInfo.high = brightest[i].level + threshold; 1669 settings2.bssidInfos[i] = BssidInfo; 1670 1671 if (DBG) localLog("Setting bssid=" + BssidInfo.bssid + ", " + 1672 "low=" + BssidInfo.low + ", high=" + BssidInfo.high); 1673 } 1674 1675 trackSignificantWifiChange(settings2); 1676 mCurrentBssids = brightest; 1677 } 1678 1679 void reconfigureScan(WifiScanner.WifiChangeSettings settings) { 1680 1681 if (settings.bssidInfos.length < MAX_APS_TO_TRACK) { 1682 localLog("too few APs (" + settings.bssidInfos.length 1683 + ") available to track wifi change"); 1684 return; 1685 } 1686 1687 if (DBG) localLog("Setting configuration specified by app"); 1688 1689 mCurrentBssids = new ScanResult[settings.bssidInfos.length]; 1690 HashSet<Integer> channels = new HashSet<Integer>(); 1691 1692 for (int i = 0; i < settings.bssidInfos.length; i++) { 1693 ScanResult result = new ScanResult(); 1694 result.BSSID = settings.bssidInfos[i].bssid; 1695 mCurrentBssids[i] = result; 1696 channels.add(settings.bssidInfos[i].frequencyHint); 1697 } 1698 1699 // cancel previous scan 1700 removeScanRequest(); 1701 1702 // set new scanning schedule 1703 ScanSettings settings2 = new ScanSettings(); 1704 settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED; 1705 settings2.channels = new ChannelSpec[channels.size()]; 1706 int i = 0; 1707 for (Integer channel : channels) { 1708 settings2.channels[i++] = new ChannelSpec(channel); 1709 } 1710 1711 settings2.periodInMs = settings.periodInMs; 1712 addScanRequest(settings2); 1713 1714 // start tracking new APs 1715 trackSignificantWifiChange(settings); 1716 } 1717 1718 class ClientInfoLocal extends ClientInfo { 1719 ClientInfoLocal() { 1720 super(0, null, null); 1721 } 1722 @Override 1723 void deliverScanResults(int handler, ScanData results[]) { 1724 if (DBG) localLog("Delivering messages directly"); 1725 sendMessage(WIFI_CHANGE_CMD_NEW_SCAN_RESULTS, 0, 0, results); 1726 } 1727 @Override 1728 void reportPeriodChanged(int handler, ScanSettings settings, int newPeriodInMs) { 1729 // nothing to do; no one is listening for this 1730 } 1731 } 1732 1733 @Override 1734 public void onChangesFound(ScanResult results[]) { 1735 sendMessage(WIFI_CHANGE_CMD_CHANGE_DETECTED, 0, 0, results); 1736 } 1737 1738 ClientInfo mClientInfo = new ClientInfoLocal(); 1739 private static final int SCAN_COMMAND_ID = 1; 1740 1741 void addScanRequest(ScanSettings settings) { 1742 if (DBG) localLog("Starting scans"); 1743 Message msg = Message.obtain(); 1744 msg.what = WifiScanner.CMD_START_BACKGROUND_SCAN; 1745 msg.arg2 = SCAN_COMMAND_ID; 1746 msg.obj = settings; 1747 mClientHandler.sendMessage(msg); 1748 } 1749 1750 void removeScanRequest() { 1751 if (DBG) localLog("Stopping scans"); 1752 Message msg = Message.obtain(); 1753 msg.what = WifiScanner.CMD_STOP_BACKGROUND_SCAN; 1754 msg.arg2 = SCAN_COMMAND_ID; 1755 mClientHandler.sendMessage(msg); 1756 } 1757 1758 void trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings) { 1759 WifiNative.untrackSignificantWifiChange(); 1760 WifiNative.trackSignificantWifiChange(settings, this); 1761 } 1762 1763 void untrackSignificantWifiChange() { 1764 WifiNative.untrackSignificantWifiChange(); 1765 } 1766 1767 } 1768 1769 private static ChannelSpec mChannels[][]; 1770 1771 private static void copyChannels( 1772 ChannelSpec channelSpec[], int offset, int channels[]) { 1773 for (int i = 0; i < channels.length; i++) { 1774 channelSpec[offset +i] = new ChannelSpec(channels[i]); 1775 } 1776 } 1777 1778 private static boolean initChannels() { 1779 if (mChannels != null) { 1780 /* already initialized */ 1781 return true; 1782 } 1783 1784 int channels24[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ); 1785 if (channels24 == null) { 1786 loge("Could not get channels for 2.4 GHz"); 1787 return false; 1788 } 1789 1790 int channels5[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ); 1791 if (channels5 == null) { 1792 loge("Could not get channels for 5 GHz"); 1793 return false; 1794 } 1795 1796 int channelsDfs[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY); 1797 if (channelsDfs == null) { 1798 loge("Could not get channels for DFS"); 1799 return false; 1800 } 1801 1802 mChannels = new ChannelSpec[8][]; 1803 1804 mChannels[0] = new ChannelSpec[0]; 1805 1806 mChannels[1] = new ChannelSpec[channels24.length]; 1807 copyChannels(mChannels[1], 0, channels24); 1808 1809 mChannels[2] = new ChannelSpec[channels5.length]; 1810 copyChannels(mChannels[2], 0, channels5); 1811 1812 mChannels[3] = new ChannelSpec[channels24.length + channels5.length]; 1813 copyChannels(mChannels[3], 0, channels24); 1814 copyChannels(mChannels[3], channels24.length, channels5); 1815 1816 mChannels[4] = new ChannelSpec[channelsDfs.length]; 1817 copyChannels(mChannels[4], 0, channelsDfs); 1818 1819 mChannels[5] = new ChannelSpec[channels24.length + channelsDfs.length]; 1820 copyChannels(mChannels[5], 0, channels24); 1821 copyChannels(mChannels[5], channels24.length, channelsDfs); 1822 1823 mChannels[6] = new ChannelSpec[channels5.length + channelsDfs.length]; 1824 copyChannels(mChannels[6], 0, channels5); 1825 copyChannels(mChannels[6], channels5.length, channelsDfs); 1826 1827 mChannels[7] = new ChannelSpec[ 1828 channels24.length + channels5.length + channelsDfs.length]; 1829 copyChannels(mChannels[7], 0, channels24); 1830 copyChannels(mChannels[7], channels24.length, channels5); 1831 copyChannels(mChannels[7], channels24.length + channels5.length, channelsDfs); 1832 1833 return true; 1834 } 1835 1836 private static ChannelSpec[] getChannelsForBand(int band) { 1837 initChannels(); 1838 1839 if (band < WifiScanner.WIFI_BAND_24_GHZ || band > WifiScanner.WIFI_BAND_BOTH_WITH_DFS) 1840 /* invalid value for band */ 1841 return mChannels[0]; 1842 else 1843 return mChannels[band]; 1844 } 1845 1846 private static boolean isDfs(int channel) { 1847 ChannelSpec[] dfsChannels = getChannelsForBand(WifiScanner 1848 .WIFI_BAND_5_GHZ_DFS_ONLY); 1849 for (int i = 0; i < dfsChannels.length; i++) { 1850 if (channel == dfsChannels[i].frequency) { 1851 return true; 1852 } 1853 } 1854 return false; 1855 } 1856 1857 private static int getBandFromChannels(ChannelSpec[] channels) { 1858 int band = WifiScanner.WIFI_BAND_UNSPECIFIED; 1859 for (ChannelSpec channel : channels) { 1860 if (2400 <= channel.frequency && channel.frequency < 2500) { 1861 band |= WifiScanner.WIFI_BAND_24_GHZ; 1862 } else if ( isDfs(channel.frequency)) { 1863 band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY; 1864 } else if (5100 <= channel.frequency && channel.frequency < 6000) { 1865 band |= WifiScanner.WIFI_BAND_5_GHZ; 1866 } 1867 } 1868 return band; 1869 } 1870 1871 private static int getBandFromChannels(WifiNative.ChannelSettings[] channels) { 1872 int band = WifiScanner.WIFI_BAND_UNSPECIFIED; 1873 for (WifiNative.ChannelSettings channel : channels) { 1874 if (2400 <= channel.frequency && channel.frequency < 2500) { 1875 band |= WifiScanner.WIFI_BAND_24_GHZ; 1876 } else if ( isDfs(channel.frequency)) { 1877 band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY; 1878 } else if (5100 <= channel.frequency && channel.frequency < 6000) { 1879 band |= WifiScanner.WIFI_BAND_5_GHZ; 1880 } 1881 } 1882 return band; 1883 } 1884 1885 @Override 1886 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1887 mStateMachine.dump(fd, pw, args); 1888 } 1889} 1890