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