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