WifiScanningServiceImpl.java revision 6471778905e0b92bd7a20d8a82e51c307dd0b96f
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, 240), 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 = 32; 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 mSettings.buckets[i] = bucketSettings; 975 } 976 } 977 978 HashMap<Integer, Integer> mChannelToBucketMap = new HashMap<Integer, Integer>(); 979 980 private int getBestBucket(ScanSettings settings) { 981 982 // check to see if any of the channels are being scanned already 983 // and find the smallest bucket index (it represents the quickest 984 // period of scan) 985 986 ChannelSpec channels[] = settings.channels; 987 if (channels == null) { 988 // set channels based on band 989 channels = getChannelsForBand(settings.band); 990 } 991 992 if (channels == null) { 993 // still no channels; then there's nothing to scan 994 loge("No channels to scan!!"); 995 return -1; 996 } 997 998 int mostFrequentBucketIndex = mTimeBuckets.length; 999 1000 for (ChannelSpec desiredChannelSpec : channels) { 1001 if (mChannelToBucketMap.containsKey(desiredChannelSpec.frequency)) { 1002 int bucket = mChannelToBucketMap.get(desiredChannelSpec.frequency); 1003 if (bucket < mostFrequentBucketIndex) { 1004 mostFrequentBucketIndex = bucket; 1005 } 1006 } 1007 } 1008 1009 int bestBucketIndex = -1; // best by period 1010 for (int i = 0; i < mTimeBuckets.length; i++) { 1011 TimeBucket bucket = mTimeBuckets[i]; 1012 if (bucket.periodMinInSecond * 1000 <= settings.periodInMs 1013 && settings.periodInMs < bucket.periodMaxInSecond * 1000) { 1014 // we set the time period to this 1015 bestBucketIndex = i; 1016 break; 1017 } 1018 } 1019 1020 if (mostFrequentBucketIndex < bestBucketIndex) { 1021 for (ChannelSpec desiredChannelSpec : channels) { 1022 mChannelToBucketMap.put(desiredChannelSpec.frequency, mostFrequentBucketIndex); 1023 } 1024 localLog("returning mf bucket number " + mostFrequentBucketIndex); 1025 return mostFrequentBucketIndex; 1026 } else if (bestBucketIndex != -1) { 1027 for (ChannelSpec desiredChannelSpec : channels) { 1028 mChannelToBucketMap.put(desiredChannelSpec.frequency, bestBucketIndex); 1029 } 1030 localLog("returning best bucket number " + bestBucketIndex); 1031 return bestBucketIndex; 1032 } 1033 1034 loge("Could not find suitable bucket for period " + settings.periodInMs); 1035 return -1; 1036 } 1037 1038 void prepChannelMap(ScanSettings settings) { 1039 getBestBucket(settings); 1040 } 1041 1042 int addScanRequestToBucket(ScanSettings settings) { 1043 1044 int bucketIndex = getBestBucket(settings); 1045 if (bucketIndex == -1) { 1046 loge("Ignoring invalid settings"); 1047 return -1; 1048 } 1049 1050 ChannelSpec desiredChannels[] = settings.channels; 1051 if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED 1052 || desiredChannels == null 1053 || desiredChannels.length == 0) { 1054 // set channels based on band 1055 desiredChannels = getChannelsForBand(settings.band); 1056 if (desiredChannels == null) { 1057 // still no channels; then there's nothing to scan 1058 loge("No channels to scan!!"); 1059 return -1; 1060 } 1061 } 1062 1063 // merge the channel lists for these buckets 1064 localLog("merging " + desiredChannels.length + " channels " 1065 + " for period " + settings.periodInMs 1066 + " maxScans " + settings.maxScansToCache); 1067 1068 WifiNative.BucketSettings bucket = mSettings.buckets[bucketIndex]; 1069 boolean added = (bucket.num_channels == 0) 1070 && (bucket.band == WifiScanner.WIFI_BAND_UNSPECIFIED); 1071 localLog("existing " + bucket.num_channels + " channels "); 1072 1073 HashSet<ChannelSpec> newChannels = new HashSet<ChannelSpec>(); 1074 for (ChannelSpec desiredChannelSpec : desiredChannels) { 1075 1076 localLog("desired channel " + desiredChannelSpec.frequency); 1077 1078 boolean found = false; 1079 for (int i = 0; i < bucket.num_channels; i++) { 1080 if (desiredChannelSpec.frequency == bucket.channels[i].frequency) { 1081 found = true; 1082 break; 1083 } 1084 } 1085 1086 if (!found) { 1087 newChannels.add(desiredChannelSpec); 1088 } else { 1089 if (DBG) localLog("Already scanning channel " + desiredChannelSpec.frequency); 1090 } 1091 } 1092 1093 if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED 1094 || (bucket.num_channels + newChannels.size()) > bucket.channels.length) { 1095 // can't accommodate all channels; switch to specifying band 1096 bucket.num_channels = 0; 1097 bucket.band = getBandFromChannels(bucket.channels) 1098 | getBandFromChannels(desiredChannels); 1099 bucket.channels = new WifiNative.ChannelSettings[0]; 1100 localLog("switching to using band " + bucket.band); 1101 } else { 1102 for (ChannelSpec desiredChannelSpec : newChannels) { 1103 1104 localLog("adding new channel spec " + desiredChannelSpec.frequency); 1105 1106 WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings(); 1107 channelSettings.frequency = desiredChannelSpec.frequency; 1108 bucket.channels[bucket.num_channels] = channelSettings; 1109 bucket.num_channels++; 1110 mChannelToBucketMap.put(bucketIndex, channelSettings.frequency); 1111 } 1112 } 1113 1114 if (bucket.report_events < settings.reportEvents) { 1115 if (DBG) localLog("setting report_events to " + settings.reportEvents); 1116 bucket.report_events = settings.reportEvents; 1117 } else { 1118 if (DBG) localLog("report_events is " + settings.reportEvents); 1119 } 1120 1121 if (added) { 1122 bucket.period_ms = mTimeBuckets[bucketIndex].periodInSecond * 1000; 1123 mSettings.num_buckets++; 1124 } 1125 1126 if (mSettings.max_ap_per_scan < settings.numBssidsPerScan) { 1127 mSettings.max_ap_per_scan = settings.numBssidsPerScan; 1128 } 1129 1130 if (settings.maxScansToCache != 0) { 1131 if (mSettings.report_threshold_num_scans > settings.maxScansToCache) { 1132 mSettings.report_threshold_num_scans = settings.maxScansToCache; 1133 } 1134 } 1135 1136 return bucket.period_ms; 1137 } 1138 1139 public WifiNative.ScanSettings getComputedSettings() { 1140 return mSettings; 1141 } 1142 1143 public void compressBuckets() { 1144 int num_buckets = 0; 1145 for (int i = 0; i < mSettings.buckets.length; i++) { 1146 if (mSettings.buckets[i].num_channels != 0 1147 || mSettings.buckets[i].band != WifiScanner.WIFI_BAND_UNSPECIFIED) { 1148 mSettings.buckets[num_buckets] = mSettings.buckets[i]; 1149 num_buckets++; 1150 } 1151 } 1152 // remove unused buckets 1153 for (int i = num_buckets; i < mSettings.buckets.length; i++) { 1154 mSettings.buckets[i] = null; 1155 } 1156 1157 mSettings.num_buckets = num_buckets; 1158 if (num_buckets != 0) { 1159 mSettings.base_period_ms = mSettings.buckets[0].period_ms; 1160 } 1161 } 1162 } 1163 1164 boolean resetBuckets() { 1165 SettingsComputer c = new SettingsComputer(); 1166 Collection<ClientInfo> clients = mClients.values(); 1167 for (ClientInfo ci : clients) { 1168 Collection<ScanSettings> settings = ci.getScanSettings(); 1169 for (ScanSettings s : settings) { 1170 c.prepChannelMap(s); 1171 } 1172 } 1173 1174 for (ClientInfo ci : clients) { 1175 Iterator it = ci.getScans(); 1176 while (it.hasNext()) { 1177 Map.Entry<Integer, ScanSettings> entry = 1178 (Map.Entry<Integer,ScanSettings>)it.next(); 1179 int id = entry.getKey(); 1180 ScanSettings s = entry.getValue(); 1181 int newPeriodInMs = c.addScanRequestToBucket(s); 1182 if (newPeriodInMs == -1) { 1183 if (DBG) localLog("could not find a good bucket"); 1184 return false; 1185 } 1186 if (newPeriodInMs != s.periodInMs) { 1187 ci.reportPeriodChanged(id, s, newPeriodInMs); 1188 } 1189 } 1190 } 1191 1192 c.compressBuckets(); 1193 1194 WifiNative.ScanSettings s = c.getComputedSettings(); 1195 if (s.num_buckets == 0) { 1196 if (DBG) localLog("Stopping scan because there are no buckets"); 1197 WifiNative.stopScan(); 1198 return true; 1199 } else { 1200 if (WifiNative.startScan(s, mStateMachine)) { 1201 localLog("Successfully started scan of " + s.num_buckets + " buckets at" 1202 + "time = " + SystemClock.elapsedRealtimeNanos() / 1000 + " period " 1203 + s.base_period_ms); 1204 return true; 1205 } else { 1206 loge("Failed to start scan of " + s.num_buckets + " buckets"); 1207 return false; 1208 } 1209 } 1210 } 1211 1212 boolean addScanRequest(ClientInfo ci, int handler, ScanSettings settings) { 1213 // sanity check the input 1214 if (ci == null) { 1215 Log.d(TAG, "Failing scan request ClientInfo not found " + handler); 1216 return false; 1217 } 1218 if (settings.periodInMs < WifiScanner.MIN_SCAN_PERIOD_MS) { 1219 localLog("Failing scan request because periodInMs is " + settings.periodInMs); 1220 return false; 1221 } 1222 1223 int minSupportedPeriodMs = 0; 1224 if (settings.channels != null) { 1225 minSupportedPeriodMs = settings.channels.length * MIN_PERIOD_PER_CHANNEL_MS; 1226 } else { 1227 if ((settings.band & WifiScanner.WIFI_BAND_24_GHZ) == 0) { 1228 /* 2.4 GHz band has 11 to 13 channels */ 1229 minSupportedPeriodMs += 1000; 1230 } 1231 if ((settings.band & WifiScanner.WIFI_BAND_5_GHZ) == 0) { 1232 /* 5 GHz band has another 10 channels */ 1233 minSupportedPeriodMs += 1000; 1234 } 1235 if ((settings.band & WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY) == 0) { 1236 /* DFS requires passive scan which takes longer time */ 1237 minSupportedPeriodMs += 2000; 1238 } 1239 } 1240 1241 if (settings.periodInMs < minSupportedPeriodMs) { 1242 localLog("Failing scan request because minSupportedPeriodMs is " 1243 + minSupportedPeriodMs + " but the request wants " + settings.periodInMs); 1244 return false; 1245 } 1246 1247 ci.addScanRequest(settings, handler); 1248 if (resetBuckets()) { 1249 return true; 1250 } else { 1251 ci.removeScanRequest(handler); 1252 localLog("Failing scan request because failed to reset scan"); 1253 return false; 1254 } 1255 } 1256 1257 boolean addSingleScanRequest(ClientInfo ci, int handler, ScanSettings settings) { 1258 if (ci == null) { 1259 Log.d(TAG, "Failing single scan request ClientInfo not found " + handler); 1260 return false; 1261 } 1262 if (settings.reportEvents == 0) { 1263 settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 1264 } 1265 if (settings.periodInMs == 0) { 1266 settings.periodInMs = 10000; // 10s - although second scan should never happen 1267 } 1268 ci.addScanRequest(settings, handler); 1269 if (resetBuckets()) { 1270 /* reset periodInMs to 0 to indicate single shot scan */ 1271 settings.periodInMs = 0; 1272 return true; 1273 } else { 1274 ci.removeScanRequest(handler); 1275 localLog("Failing scan request because failed to reset scan"); 1276 return false; 1277 } 1278 } 1279 1280 void removeScanRequest(ClientInfo ci, int handler) { 1281 if (ci != null) { 1282 ci.removeScanRequest(handler); 1283 resetBuckets(); 1284 } 1285 } 1286 1287 boolean reportScanResults() { 1288 ScanData results[] = WifiNative.getScanResults(/* flush = */ true); 1289 Collection<ClientInfo> clients = mClients.values(); 1290 for (ClientInfo ci2 : clients) { 1291 ci2.reportScanResults(results); 1292 } 1293 1294 return true; 1295 } 1296 1297 void resetHotlist() { 1298 Collection<ClientInfo> clients = mClients.values(); 1299 int num_hotlist_ap = 0; 1300 1301 for (ClientInfo ci : clients) { 1302 Collection<WifiScanner.HotlistSettings> c = ci.getHotlistSettings(); 1303 for (WifiScanner.HotlistSettings s : c) { 1304 num_hotlist_ap += s.bssidInfos.length; 1305 } 1306 } 1307 1308 if (num_hotlist_ap == 0) { 1309 WifiNative.resetHotlist(); 1310 } else { 1311 BssidInfo bssidInfos[] = new BssidInfo[num_hotlist_ap]; 1312 int index = 0; 1313 for (ClientInfo ci : clients) { 1314 Collection<WifiScanner.HotlistSettings> settings = ci.getHotlistSettings(); 1315 for (WifiScanner.HotlistSettings s : settings) { 1316 for (int i = 0; i < s.bssidInfos.length; i++, index++) { 1317 bssidInfos[index] = s.bssidInfos[i]; 1318 } 1319 } 1320 } 1321 1322 WifiScanner.HotlistSettings settings = new WifiScanner.HotlistSettings(); 1323 settings.bssidInfos = bssidInfos; 1324 settings.apLostThreshold = 3; 1325 WifiNative.setHotlist(settings, mStateMachine); 1326 } 1327 } 1328 1329 void setHotlist(ClientInfo ci, int handler, WifiScanner.HotlistSettings settings) { 1330 ci.addHostlistSettings(settings, handler); 1331 resetHotlist(); 1332 } 1333 1334 void resetHotlist(ClientInfo ci, int handler) { 1335 ci.removeHostlistSettings(handler); 1336 resetHotlist(); 1337 } 1338 1339 WifiChangeStateMachine mWifiChangeStateMachine; 1340 1341 void trackWifiChanges(ClientInfo ci, int handler) { 1342 mWifiChangeStateMachine.enable(); 1343 ci.addSignificantWifiChange(handler); 1344 } 1345 1346 void untrackWifiChanges(ClientInfo ci, int handler) { 1347 ci.removeSignificantWifiChange(handler); 1348 Collection<ClientInfo> clients = mClients.values(); 1349 for (ClientInfo ci2 : clients) { 1350 if (ci2.getWifiChangeHandlers().size() != 0) { 1351 // there is at least one client watching for 1352 // significant changes; so nothing more to do 1353 return; 1354 } 1355 } 1356 1357 // no more clients looking for significant wifi changes 1358 // no need to keep the state machine running; disable it 1359 mWifiChangeStateMachine.disable(); 1360 } 1361 1362 void configureWifiChange(WifiScanner.WifiChangeSettings settings) { 1363 mWifiChangeStateMachine.configure(settings); 1364 } 1365 1366 void reportWifiChanged(ScanResult results[]) { 1367 Collection<ClientInfo> clients = mClients.values(); 1368 for (ClientInfo ci : clients) { 1369 ci.reportWifiChanged(results); 1370 } 1371 } 1372 1373 void reportWifiStabilized(ScanResult results[]) { 1374 Collection<ClientInfo> clients = mClients.values(); 1375 for (ClientInfo ci : clients) { 1376 ci.reportWifiStabilized(results); 1377 } 1378 } 1379 1380 class WifiChangeStateMachine extends StateMachine 1381 implements WifiNative.SignificantWifiChangeEventHandler { 1382 1383 private static final String TAG = "WifiChangeStateMachine"; 1384 1385 private static final int WIFI_CHANGE_CMD_NEW_SCAN_RESULTS = 0; 1386 private static final int WIFI_CHANGE_CMD_CHANGE_DETECTED = 1; 1387 private static final int WIFI_CHANGE_CMD_CHANGE_TIMEOUT = 2; 1388 private static final int WIFI_CHANGE_CMD_ENABLE = 3; 1389 private static final int WIFI_CHANGE_CMD_DISABLE = 4; 1390 private static final int WIFI_CHANGE_CMD_CONFIGURE = 5; 1391 1392 private static final int MAX_APS_TO_TRACK = 3; 1393 private static final int MOVING_SCAN_PERIOD_MS = 10000; 1394 private static final int STATIONARY_SCAN_PERIOD_MS = 5000; 1395 private static final int MOVING_STATE_TIMEOUT_MS = 30000; 1396 1397 State mDefaultState = new DefaultState(); 1398 State mStationaryState = new StationaryState(); 1399 State mMovingState = new MovingState(); 1400 1401 private static final String ACTION_TIMEOUT = 1402 "com.android.server.WifiScanningServiceImpl.action.TIMEOUT"; 1403 AlarmManager mAlarmManager; 1404 PendingIntent mTimeoutIntent; 1405 ScanResult mCurrentBssids[]; 1406 1407 WifiChangeStateMachine(Looper looper) { 1408 super("SignificantChangeStateMachine", looper); 1409 1410 mClients.put(null, mClientInfo); 1411 1412 addState(mDefaultState); 1413 addState(mStationaryState, mDefaultState); 1414 addState(mMovingState, mDefaultState); 1415 1416 setInitialState(mDefaultState); 1417 } 1418 1419 public void enable() { 1420 if (mAlarmManager == null) { 1421 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 1422 } 1423 1424 if (mTimeoutIntent == null) { 1425 Intent intent = new Intent(ACTION_TIMEOUT, null); 1426 mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 1427 1428 mContext.registerReceiver( 1429 new BroadcastReceiver() { 1430 @Override 1431 public void onReceive(Context context, Intent intent) { 1432 sendMessage(WIFI_CHANGE_CMD_CHANGE_TIMEOUT); 1433 } 1434 }, new IntentFilter(ACTION_TIMEOUT)); 1435 } 1436 1437 sendMessage(WIFI_CHANGE_CMD_ENABLE); 1438 } 1439 1440 public void disable() { 1441 sendMessage(WIFI_CHANGE_CMD_DISABLE); 1442 } 1443 1444 public void configure(WifiScanner.WifiChangeSettings settings) { 1445 sendMessage(WIFI_CHANGE_CMD_CONFIGURE, settings); 1446 } 1447 1448 class DefaultState extends State { 1449 @Override 1450 public void enter() { 1451 if (DBG) localLog("Entering IdleState"); 1452 } 1453 1454 @Override 1455 public boolean processMessage(Message msg) { 1456 if (DBG) localLog("DefaultState state got " + msg); 1457 switch (msg.what) { 1458 case WIFI_CHANGE_CMD_ENABLE : 1459 transitionTo(mMovingState); 1460 break; 1461 case WIFI_CHANGE_CMD_DISABLE: 1462 // nothing to do 1463 break; 1464 case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS: 1465 // nothing to do 1466 break; 1467 case WIFI_CHANGE_CMD_CONFIGURE: 1468 /* save configuration till we transition to moving state */ 1469 deferMessage(msg); 1470 break; 1471 default: 1472 return NOT_HANDLED; 1473 } 1474 return HANDLED; 1475 } 1476 } 1477 1478 class StationaryState extends State { 1479 @Override 1480 public void enter() { 1481 if (DBG) localLog("Entering StationaryState"); 1482 reportWifiStabilized(mCurrentBssids); 1483 } 1484 1485 @Override 1486 public boolean processMessage(Message msg) { 1487 if (DBG) localLog("Stationary state got " + msg); 1488 switch (msg.what) { 1489 case WIFI_CHANGE_CMD_ENABLE : 1490 // do nothing 1491 break; 1492 case WIFI_CHANGE_CMD_CHANGE_DETECTED: 1493 if (DBG) localLog("Got wifi change detected"); 1494 reportWifiChanged((ScanResult[])msg.obj); 1495 transitionTo(mMovingState); 1496 break; 1497 case WIFI_CHANGE_CMD_DISABLE: 1498 if (DBG) localLog("Got Disable Wifi Change"); 1499 mCurrentBssids = null; 1500 removeScanRequest(); 1501 untrackSignificantWifiChange(); 1502 transitionTo(mDefaultState); 1503 break; 1504 case WIFI_CHANGE_CMD_CONFIGURE: 1505 /* save configuration till we transition to moving state */ 1506 deferMessage(msg); 1507 break; 1508 default: 1509 return NOT_HANDLED; 1510 } 1511 return HANDLED; 1512 } 1513 } 1514 1515 class MovingState extends State { 1516 boolean mWifiChangeDetected = false; 1517 boolean mScanResultsPending = false; 1518 1519 @Override 1520 public void enter() { 1521 if (DBG) localLog("Entering MovingState"); 1522 issueFullScan(); 1523 } 1524 1525 @Override 1526 public boolean processMessage(Message msg) { 1527 if (DBG) localLog("MovingState state got " + msg); 1528 switch (msg.what) { 1529 case WIFI_CHANGE_CMD_ENABLE : 1530 // do nothing 1531 break; 1532 case WIFI_CHANGE_CMD_DISABLE: 1533 if (DBG) localLog("Got Disable Wifi Change"); 1534 mCurrentBssids = null; 1535 removeScanRequest(); 1536 untrackSignificantWifiChange(); 1537 transitionTo(mDefaultState); 1538 break; 1539 case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS: 1540 if (DBG) localLog("Got scan results"); 1541 if (mScanResultsPending) { 1542 if (DBG) localLog("reconfiguring scan"); 1543 reconfigureScan((ScanData[])msg.obj, 1544 STATIONARY_SCAN_PERIOD_MS); 1545 mWifiChangeDetected = false; 1546 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, 1547 System.currentTimeMillis() + MOVING_STATE_TIMEOUT_MS, 1548 mTimeoutIntent); 1549 mScanResultsPending = false; 1550 } 1551 break; 1552 case WIFI_CHANGE_CMD_CONFIGURE: 1553 if (DBG) localLog("Got configuration from app"); 1554 WifiScanner.WifiChangeSettings settings = 1555 (WifiScanner.WifiChangeSettings) msg.obj; 1556 reconfigureScan(settings); 1557 mWifiChangeDetected = false; 1558 long unchangedDelay = settings.unchangedSampleSize * settings.periodInMs; 1559 mAlarmManager.cancel(mTimeoutIntent); 1560 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, 1561 System.currentTimeMillis() + unchangedDelay, 1562 mTimeoutIntent); 1563 break; 1564 case WIFI_CHANGE_CMD_CHANGE_DETECTED: 1565 if (DBG) localLog("Change detected"); 1566 mAlarmManager.cancel(mTimeoutIntent); 1567 reportWifiChanged((ScanResult[])msg.obj); 1568 mWifiChangeDetected = true; 1569 issueFullScan(); 1570 break; 1571 case WIFI_CHANGE_CMD_CHANGE_TIMEOUT: 1572 if (DBG) localLog("Got timeout event"); 1573 if (mWifiChangeDetected == false) { 1574 transitionTo(mStationaryState); 1575 } 1576 break; 1577 default: 1578 return NOT_HANDLED; 1579 } 1580 return HANDLED; 1581 } 1582 1583 @Override 1584 public void exit() { 1585 mAlarmManager.cancel(mTimeoutIntent); 1586 } 1587 1588 void issueFullScan() { 1589 if (DBG) localLog("Issuing full scan"); 1590 ScanSettings settings = new ScanSettings(); 1591 settings.band = WifiScanner.WIFI_BAND_BOTH; 1592 settings.periodInMs = MOVING_SCAN_PERIOD_MS; 1593 settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 1594 addScanRequest(settings); 1595 mScanResultsPending = true; 1596 } 1597 1598 } 1599 1600 void reconfigureScan(ScanData[] results, int period) { 1601 // find brightest APs and set them as sentinels 1602 if (results.length < MAX_APS_TO_TRACK) { 1603 localLog("too few APs (" + results.length + ") available to track wifi change"); 1604 return; 1605 } 1606 1607 removeScanRequest(); 1608 1609 // remove duplicate BSSIDs 1610 HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>(); 1611 for (ScanResult result : results[0].getResults()) { 1612 ScanResult saved = bssidToScanResult.get(result.BSSID); 1613 if (saved == null) { 1614 bssidToScanResult.put(result.BSSID, result); 1615 } else if (saved.level > result.level) { 1616 bssidToScanResult.put(result.BSSID, result); 1617 } 1618 } 1619 1620 // find brightest BSSIDs 1621 ScanResult brightest[] = new ScanResult[MAX_APS_TO_TRACK]; 1622 Collection<ScanResult> results2 = bssidToScanResult.values(); 1623 for (ScanResult result : results2) { 1624 for (int j = 0; j < brightest.length; j++) { 1625 if (brightest[j] == null 1626 || (brightest[j].level < result.level)) { 1627 for (int k = brightest.length; k > (j + 1); k--) { 1628 brightest[k - 1] = brightest[k - 2]; 1629 } 1630 brightest[j] = result; 1631 break; 1632 } 1633 } 1634 } 1635 1636 // Get channels to scan for 1637 ArrayList<Integer> channels = new ArrayList<Integer>(); 1638 for (int i = 0; i < brightest.length; i++) { 1639 boolean found = false; 1640 for (int j = i + 1; j < brightest.length; j++) { 1641 if (brightest[j].frequency == brightest[i].frequency) { 1642 found = true; 1643 } 1644 } 1645 if (!found) { 1646 channels.add(brightest[i].frequency); 1647 } 1648 } 1649 1650 if (DBG) localLog("Found " + channels.size() + " channels"); 1651 1652 // set scanning schedule 1653 ScanSettings settings = new ScanSettings(); 1654 settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED; 1655 settings.channels = new ChannelSpec[channels.size()]; 1656 for (int i = 0; i < channels.size(); i++) { 1657 settings.channels[i] = new ChannelSpec(channels.get(i)); 1658 } 1659 1660 settings.periodInMs = period; 1661 addScanRequest(settings); 1662 1663 WifiScanner.WifiChangeSettings settings2 = new WifiScanner.WifiChangeSettings(); 1664 settings2.rssiSampleSize = 3; 1665 settings2.lostApSampleSize = 3; 1666 settings2.unchangedSampleSize = 3; 1667 settings2.minApsBreachingThreshold = 2; 1668 settings2.bssidInfos = new BssidInfo[brightest.length]; 1669 1670 for (int i = 0; i < brightest.length; i++) { 1671 BssidInfo BssidInfo = new BssidInfo(); 1672 BssidInfo.bssid = brightest[i].BSSID; 1673 int threshold = (100 + brightest[i].level) / 32 + 2; 1674 BssidInfo.low = brightest[i].level - threshold; 1675 BssidInfo.high = brightest[i].level + threshold; 1676 settings2.bssidInfos[i] = BssidInfo; 1677 1678 if (DBG) localLog("Setting bssid=" + BssidInfo.bssid + ", " + 1679 "low=" + BssidInfo.low + ", high=" + BssidInfo.high); 1680 } 1681 1682 trackSignificantWifiChange(settings2); 1683 mCurrentBssids = brightest; 1684 } 1685 1686 void reconfigureScan(WifiScanner.WifiChangeSettings settings) { 1687 1688 if (settings.bssidInfos.length < MAX_APS_TO_TRACK) { 1689 localLog("too few APs (" + settings.bssidInfos.length 1690 + ") available to track wifi change"); 1691 return; 1692 } 1693 1694 if (DBG) localLog("Setting configuration specified by app"); 1695 1696 mCurrentBssids = new ScanResult[settings.bssidInfos.length]; 1697 HashSet<Integer> channels = new HashSet<Integer>(); 1698 1699 for (int i = 0; i < settings.bssidInfos.length; i++) { 1700 ScanResult result = new ScanResult(); 1701 result.BSSID = settings.bssidInfos[i].bssid; 1702 mCurrentBssids[i] = result; 1703 channels.add(settings.bssidInfos[i].frequencyHint); 1704 } 1705 1706 // cancel previous scan 1707 removeScanRequest(); 1708 1709 // set new scanning schedule 1710 ScanSettings settings2 = new ScanSettings(); 1711 settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED; 1712 settings2.channels = new ChannelSpec[channels.size()]; 1713 int i = 0; 1714 for (Integer channel : channels) { 1715 settings2.channels[i++] = new ChannelSpec(channel); 1716 } 1717 1718 settings2.periodInMs = settings.periodInMs; 1719 addScanRequest(settings2); 1720 1721 // start tracking new APs 1722 trackSignificantWifiChange(settings); 1723 } 1724 1725 class ClientInfoLocal extends ClientInfo { 1726 ClientInfoLocal() { 1727 super(0, null, null); 1728 } 1729 @Override 1730 void deliverScanResults(int handler, ScanData results[]) { 1731 if (DBG) localLog("Delivering messages directly"); 1732 sendMessage(WIFI_CHANGE_CMD_NEW_SCAN_RESULTS, 0, 0, results); 1733 } 1734 @Override 1735 void reportPeriodChanged(int handler, ScanSettings settings, int newPeriodInMs) { 1736 // nothing to do; no one is listening for this 1737 } 1738 } 1739 1740 @Override 1741 public void onChangesFound(ScanResult results[]) { 1742 sendMessage(WIFI_CHANGE_CMD_CHANGE_DETECTED, 0, 0, results); 1743 } 1744 1745 ClientInfo mClientInfo = new ClientInfoLocal(); 1746 private static final int SCAN_COMMAND_ID = 1; 1747 1748 void addScanRequest(ScanSettings settings) { 1749 if (DBG) localLog("Starting scans"); 1750 Message msg = Message.obtain(); 1751 msg.what = WifiScanner.CMD_START_BACKGROUND_SCAN; 1752 msg.arg2 = SCAN_COMMAND_ID; 1753 msg.obj = settings; 1754 mClientHandler.sendMessage(msg); 1755 } 1756 1757 void removeScanRequest() { 1758 if (DBG) localLog("Stopping scans"); 1759 Message msg = Message.obtain(); 1760 msg.what = WifiScanner.CMD_STOP_BACKGROUND_SCAN; 1761 msg.arg2 = SCAN_COMMAND_ID; 1762 mClientHandler.sendMessage(msg); 1763 } 1764 1765 void trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings) { 1766 WifiNative.untrackSignificantWifiChange(); 1767 WifiNative.trackSignificantWifiChange(settings, this); 1768 } 1769 1770 void untrackSignificantWifiChange() { 1771 WifiNative.untrackSignificantWifiChange(); 1772 } 1773 1774 } 1775 1776 private static ChannelSpec mChannels[][]; 1777 1778 private static void copyChannels( 1779 ChannelSpec channelSpec[], int offset, int channels[]) { 1780 for (int i = 0; i < channels.length; i++) { 1781 channelSpec[offset +i] = new ChannelSpec(channels[i]); 1782 } 1783 } 1784 1785 private static boolean initChannels() { 1786 if (mChannels != null) { 1787 /* already initialized */ 1788 return true; 1789 } 1790 1791 int channels24[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ); 1792 if (channels24 == null) { 1793 loge("Could not get channels for 2.4 GHz"); 1794 return false; 1795 } 1796 1797 int channels5[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ); 1798 if (channels5 == null) { 1799 loge("Could not get channels for 5 GHz"); 1800 return false; 1801 } 1802 1803 int channelsDfs[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY); 1804 if (channelsDfs == null) { 1805 loge("Could not get channels for DFS"); 1806 return false; 1807 } 1808 1809 mChannels = new ChannelSpec[8][]; 1810 1811 mChannels[0] = new ChannelSpec[0]; 1812 1813 mChannels[1] = new ChannelSpec[channels24.length]; 1814 copyChannels(mChannels[1], 0, channels24); 1815 1816 mChannels[2] = new ChannelSpec[channels5.length]; 1817 copyChannels(mChannels[2], 0, channels5); 1818 1819 mChannels[3] = new ChannelSpec[channels24.length + channels5.length]; 1820 copyChannels(mChannels[3], 0, channels24); 1821 copyChannels(mChannels[3], channels24.length, channels5); 1822 1823 mChannels[4] = new ChannelSpec[channelsDfs.length]; 1824 copyChannels(mChannels[4], 0, channelsDfs); 1825 1826 mChannels[5] = new ChannelSpec[channels24.length + channelsDfs.length]; 1827 copyChannels(mChannels[5], 0, channels24); 1828 copyChannels(mChannels[5], channels24.length, channelsDfs); 1829 1830 mChannels[6] = new ChannelSpec[channels5.length + channelsDfs.length]; 1831 copyChannels(mChannels[6], 0, channels5); 1832 copyChannels(mChannels[6], channels5.length, channelsDfs); 1833 1834 mChannels[7] = new ChannelSpec[ 1835 channels24.length + channels5.length + channelsDfs.length]; 1836 copyChannels(mChannels[7], 0, channels24); 1837 copyChannels(mChannels[7], channels24.length, channels5); 1838 copyChannels(mChannels[7], channels24.length + channels5.length, channelsDfs); 1839 1840 return true; 1841 } 1842 1843 private static ChannelSpec[] getChannelsForBand(int band) { 1844 initChannels(); 1845 1846 if (band < WifiScanner.WIFI_BAND_24_GHZ || band > WifiScanner.WIFI_BAND_BOTH_WITH_DFS) 1847 /* invalid value for band */ 1848 return mChannels[0]; 1849 else 1850 return mChannels[band]; 1851 } 1852 1853 private static boolean isDfs(int channel) { 1854 ChannelSpec[] dfsChannels = getChannelsForBand(WifiScanner 1855 .WIFI_BAND_5_GHZ_DFS_ONLY); 1856 for (int i = 0; i < dfsChannels.length; i++) { 1857 if (channel == dfsChannels[i].frequency) { 1858 return true; 1859 } 1860 } 1861 return false; 1862 } 1863 1864 private static int getBandFromChannels(ChannelSpec[] channels) { 1865 int band = WifiScanner.WIFI_BAND_UNSPECIFIED; 1866 for (ChannelSpec channel : channels) { 1867 if (2400 <= channel.frequency && channel.frequency < 2500) { 1868 band |= WifiScanner.WIFI_BAND_24_GHZ; 1869 } else if ( isDfs(channel.frequency)) { 1870 band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY; 1871 } else if (5100 <= channel.frequency && channel.frequency < 6000) { 1872 band |= WifiScanner.WIFI_BAND_5_GHZ; 1873 } 1874 } 1875 return band; 1876 } 1877 1878 private static int getBandFromChannels(WifiNative.ChannelSettings[] channels) { 1879 int band = WifiScanner.WIFI_BAND_UNSPECIFIED; 1880 for (WifiNative.ChannelSettings channel : channels) { 1881 if (channel != null) { 1882 if (2400 <= channel.frequency && channel.frequency < 2500) { 1883 band |= WifiScanner.WIFI_BAND_24_GHZ; 1884 } else if ( isDfs(channel.frequency)) { 1885 band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY; 1886 } else if (5100 <= channel.frequency && channel.frequency < 6000) { 1887 band |= WifiScanner.WIFI_BAND_5_GHZ; 1888 } 1889 } 1890 } 1891 return band; 1892 } 1893 1894 @Override 1895 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1896 mStateMachine.dump(fd, pw, args); 1897 } 1898} 1899