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