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