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