WifiScanningServiceImpl.java revision 635e01902944c3367ccca28611a5b0b718f98bfd
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.scanner; 18 19import android.Manifest; 20import android.app.AlarmManager; 21import android.app.PendingIntent; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.pm.PackageManager; 27import android.net.wifi.IWifiScanner; 28import android.net.wifi.ScanResult; 29import android.net.wifi.WifiManager; 30import android.net.wifi.WifiScanner; 31import android.net.wifi.WifiScanner.BssidInfo; 32import android.net.wifi.WifiScanner.ChannelSpec; 33import android.net.wifi.WifiScanner.PnoSettings; 34import android.net.wifi.WifiScanner.ScanData; 35import android.net.wifi.WifiScanner.ScanSettings; 36import android.os.Binder; 37import android.os.Bundle; 38import android.os.Handler; 39import android.os.Looper; 40import android.os.Message; 41import android.os.Messenger; 42import android.os.RemoteException; 43import android.os.WorkSource; 44import android.util.ArrayMap; 45import android.util.LocalLog; 46import android.util.Log; 47import android.util.Pair; 48 49import com.android.internal.app.IBatteryStats; 50import com.android.internal.util.AsyncChannel; 51import com.android.internal.util.Protocol; 52import com.android.internal.util.State; 53import com.android.internal.util.StateMachine; 54import com.android.server.wifi.Clock; 55import com.android.server.wifi.WifiInjector; 56import com.android.server.wifi.WifiMetrics; 57import com.android.server.wifi.WifiMetricsProto; 58import com.android.server.wifi.WifiNative; 59import com.android.server.wifi.WifiStateMachine; 60import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; 61 62import java.io.FileDescriptor; 63import java.io.PrintWriter; 64import java.util.ArrayList; 65import java.util.Collection; 66import java.util.HashMap; 67import java.util.HashSet; 68import java.util.Iterator; 69import java.util.Set; 70 71public class WifiScanningServiceImpl extends IWifiScanner.Stub { 72 73 private static final String TAG = WifiScanningService.TAG; 74 private static final boolean DBG = false; 75 76 private static final int MIN_PERIOD_PER_CHANNEL_MS = 200; // DFS needs 120 ms 77 private static final int UNKNOWN_PID = -1; 78 79 private final LocalLog mLocalLog = new LocalLog(512); 80 81 private void localLog(String message) { 82 mLocalLog.log(message); 83 } 84 85 private void logw(String message) { 86 Log.w(TAG, message); 87 mLocalLog.log(message); 88 } 89 90 private void loge(String message) { 91 Log.e(TAG, message); 92 mLocalLog.log(message); 93 } 94 95 private WifiScannerImpl mScannerImpl; 96 97 @Override 98 public Messenger getMessenger() { 99 if (mClientHandler != null) { 100 return new Messenger(mClientHandler); 101 } else { 102 loge("WifiScanningServiceImpl trying to get messenger w/o initialization"); 103 return null; 104 } 105 } 106 107 @Override 108 public Bundle getAvailableChannels(int band) { 109 mChannelHelper.updateChannels(); 110 ChannelSpec[] channelSpecs = mChannelHelper.getAvailableScanChannels(band); 111 ArrayList<Integer> list = new ArrayList<Integer>(channelSpecs.length); 112 for (ChannelSpec channelSpec : channelSpecs) { 113 list.add(channelSpec.frequency); 114 } 115 Bundle b = new Bundle(); 116 b.putIntegerArrayList(WifiScanner.GET_AVAILABLE_CHANNELS_EXTRA, list); 117 return b; 118 } 119 120 private void enforceLocationHardwarePermission(int uid) { 121 mContext.enforcePermission( 122 Manifest.permission.LOCATION_HARDWARE, 123 UNKNOWN_PID, uid, 124 "LocationHardware"); 125 } 126 127 private class ClientHandler extends Handler { 128 129 ClientHandler(Looper looper) { 130 super(looper); 131 } 132 133 @Override 134 public void handleMessage(Message msg) { 135 switch (msg.what) { 136 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { 137 ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo); 138 if (client != null) { 139 logw("duplicate client connection: " + msg.sendingUid + ", messenger=" 140 + msg.replyTo); 141 client.mChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, 142 AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED); 143 return; 144 } 145 146 AsyncChannel ac = new AsyncChannel(); 147 ac.connected(mContext, this, msg.replyTo); 148 149 client = new ExternalClientInfo(msg.sendingUid, msg.replyTo, ac); 150 client.register(); 151 152 ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, 153 AsyncChannel.STATUS_SUCCESSFUL); 154 155 localLog("client connected: " + client); 156 return; 157 } 158 case AsyncChannel.CMD_CHANNEL_DISCONNECT: { 159 ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo); 160 if (client != null) { 161 client.mChannel.disconnect(); 162 } 163 return; 164 } 165 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { 166 ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo); 167 if (client != null) { 168 localLog("client disconnected: " + client + ", reason: " + msg.arg1); 169 client.cleanup(); 170 } 171 return; 172 } 173 } 174 175 try { 176 enforceLocationHardwarePermission(msg.sendingUid); 177 } catch (SecurityException e) { 178 localLog("failed to authorize app: " + e); 179 replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized"); 180 return; 181 } 182 183 // Since this message is sent from WifiScanner using |sendMessageSynchronously| which 184 // doesn't set the correct |msg.replyTo| field. 185 if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) { 186 mBackgroundScanStateMachine.sendMessage(Message.obtain(msg)); 187 return; 188 } 189 190 ClientInfo ci = mClients.get(msg.replyTo); 191 if (ci == null) { 192 loge("Could not find client info for message " + msg.replyTo); 193 replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, "Could not find listener"); 194 return; 195 } 196 197 switch (msg.what) { 198 case WifiScanner.CMD_START_BACKGROUND_SCAN: 199 case WifiScanner.CMD_STOP_BACKGROUND_SCAN: 200 case WifiScanner.CMD_SET_HOTLIST: 201 case WifiScanner.CMD_RESET_HOTLIST: 202 mBackgroundScanStateMachine.sendMessage(Message.obtain(msg)); 203 break; 204 case WifiScanner.CMD_START_PNO_SCAN: 205 case WifiScanner.CMD_STOP_PNO_SCAN: 206 mPnoScanStateMachine.sendMessage(Message.obtain(msg)); 207 break; 208 case WifiScanner.CMD_START_SINGLE_SCAN: 209 case WifiScanner.CMD_STOP_SINGLE_SCAN: 210 mSingleScanStateMachine.sendMessage(Message.obtain(msg)); 211 break; 212 case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: 213 case WifiScanner.CMD_START_TRACKING_CHANGE: 214 case WifiScanner.CMD_STOP_TRACKING_CHANGE: 215 mWifiChangeStateMachine.sendMessage(Message.obtain(msg)); 216 break; 217 case WifiScanner.CMD_REGISTER_SCAN_LISTENER: 218 logScanRequest("registerScanListener", ci, msg.arg2, null, null, null); 219 mSingleScanListeners.addRequest(ci, msg.arg2, null, null); 220 replySucceeded(msg); 221 break; 222 case WifiScanner.CMD_DEREGISTER_SCAN_LISTENER: 223 logScanRequest("deregisterScanListener", ci, msg.arg2, null, null, null); 224 mSingleScanListeners.removeRequest(ci, msg.arg2); 225 break; 226 default: 227 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "Invalid request"); 228 break; 229 } 230 } 231 } 232 233 private static final int BASE = Protocol.BASE_WIFI_SCANNER_SERVICE; 234 235 private static final int CMD_SCAN_RESULTS_AVAILABLE = BASE + 0; 236 private static final int CMD_FULL_SCAN_RESULTS = BASE + 1; 237 private static final int CMD_HOTLIST_AP_FOUND = BASE + 2; 238 private static final int CMD_HOTLIST_AP_LOST = BASE + 3; 239 private static final int CMD_WIFI_CHANGE_DETECTED = BASE + 4; 240 private static final int CMD_WIFI_CHANGE_TIMEOUT = BASE + 5; 241 private static final int CMD_DRIVER_LOADED = BASE + 6; 242 private static final int CMD_DRIVER_UNLOADED = BASE + 7; 243 private static final int CMD_SCAN_PAUSED = BASE + 8; 244 private static final int CMD_SCAN_RESTARTED = BASE + 9; 245 private static final int CMD_SCAN_FAILED = BASE + 10; 246 private static final int CMD_PNO_NETWORK_FOUND = BASE + 11; 247 private static final int CMD_PNO_SCAN_FAILED = BASE + 12; 248 249 private final Context mContext; 250 private final Looper mLooper; 251 private final WifiScannerImpl.WifiScannerImplFactory mScannerImplFactory; 252 private final ArrayMap<Messenger, ClientInfo> mClients; 253 254 private final RequestList<Void> mSingleScanListeners = new RequestList<>(); 255 256 private ChannelHelper mChannelHelper; 257 private BackgroundScanScheduler mBackgroundScheduler; 258 private WifiNative.ScanSettings mPreviousSchedule; 259 260 private WifiBackgroundScanStateMachine mBackgroundScanStateMachine; 261 private WifiSingleScanStateMachine mSingleScanStateMachine; 262 private WifiChangeStateMachine mWifiChangeStateMachine; 263 private WifiPnoScanStateMachine mPnoScanStateMachine; 264 private ClientHandler mClientHandler; 265 private final IBatteryStats mBatteryStats; 266 private final AlarmManager mAlarmManager; 267 private final WifiMetrics mWifiMetrics; 268 private final Clock mClock; 269 270 WifiScanningServiceImpl(Context context, Looper looper, 271 WifiScannerImpl.WifiScannerImplFactory scannerImplFactory, IBatteryStats batteryStats, 272 WifiInjector wifiInjector) { 273 mContext = context; 274 mLooper = looper; 275 mScannerImplFactory = scannerImplFactory; 276 mBatteryStats = batteryStats; 277 mClients = new ArrayMap<>(); 278 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 279 mWifiMetrics = wifiInjector.getWifiMetrics(); 280 mClock = wifiInjector.getClock(); 281 282 mPreviousSchedule = null; 283 } 284 285 public void startService() { 286 mClientHandler = new ClientHandler(mLooper); 287 mBackgroundScanStateMachine = new WifiBackgroundScanStateMachine(mLooper); 288 mWifiChangeStateMachine = new WifiChangeStateMachine(mLooper); 289 mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper); 290 mPnoScanStateMachine = new WifiPnoScanStateMachine(mLooper); 291 292 mContext.registerReceiver( 293 new BroadcastReceiver() { 294 @Override 295 public void onReceive(Context context, Intent intent) { 296 int state = intent.getIntExtra( 297 WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED); 298 if (DBG) localLog("SCAN_AVAILABLE : " + state); 299 if (state == WifiManager.WIFI_STATE_ENABLED) { 300 mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_LOADED); 301 mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED); 302 mPnoScanStateMachine.sendMessage(CMD_DRIVER_LOADED); 303 } else if (state == WifiManager.WIFI_STATE_DISABLED) { 304 mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED); 305 mSingleScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED); 306 mPnoScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED); 307 } 308 } 309 }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE)); 310 311 mBackgroundScanStateMachine.start(); 312 mWifiChangeStateMachine.start(); 313 mSingleScanStateMachine.start(); 314 mPnoScanStateMachine.start(); 315 } 316 317 private static boolean isWorkSourceValid(WorkSource workSource) { 318 return workSource != null && workSource.size() > 0 && workSource.get(0) >= 0; 319 } 320 321 private WorkSource computeWorkSource(ClientInfo ci, WorkSource requestedWorkSource) { 322 if (requestedWorkSource != null) { 323 if (isWorkSourceValid(requestedWorkSource)) { 324 // Wifi currently doesn't use names, so need to clear names out of the 325 // supplied WorkSource to allow future WorkSource combining. 326 requestedWorkSource.clearNames(); 327 return requestedWorkSource; 328 } else { 329 loge("Got invalid work source request: " + requestedWorkSource.toString() + 330 " from " + ci); 331 } 332 } 333 WorkSource callingWorkSource = new WorkSource(ci.getUid()); 334 if (isWorkSourceValid(callingWorkSource)) { 335 return callingWorkSource; 336 } else { 337 loge("Client has invalid work source: " + callingWorkSource); 338 return new WorkSource(); 339 } 340 } 341 342 private class RequestInfo<T> { 343 final ClientInfo clientInfo; 344 final int handlerId; 345 final WorkSource workSource; 346 final T settings; 347 348 RequestInfo(ClientInfo clientInfo, int handlerId, WorkSource requestedWorkSource, 349 T settings) { 350 this.clientInfo = clientInfo; 351 this.handlerId = handlerId; 352 this.settings = settings; 353 this.workSource = computeWorkSource(clientInfo, requestedWorkSource); 354 } 355 356 void reportEvent(int what, int arg1, Object obj) { 357 clientInfo.reportEvent(what, arg1, handlerId, obj); 358 } 359 } 360 361 private class RequestList<T> extends ArrayList<RequestInfo<T>> { 362 void addRequest(ClientInfo ci, int handler, WorkSource reqworkSource, T settings) { 363 add(new RequestInfo<T>(ci, handler, reqworkSource, settings)); 364 } 365 366 T removeRequest(ClientInfo ci, int handlerId) { 367 T removed = null; 368 Iterator<RequestInfo<T>> iter = iterator(); 369 while (iter.hasNext()) { 370 RequestInfo<T> entry = iter.next(); 371 if (entry.clientInfo == ci && entry.handlerId == handlerId) { 372 removed = entry.settings; 373 iter.remove(); 374 } 375 } 376 return removed; 377 } 378 379 Collection<T> getAllSettings() { 380 ArrayList<T> settingsList = new ArrayList<>(); 381 Iterator<RequestInfo<T>> iter = iterator(); 382 while (iter.hasNext()) { 383 RequestInfo<T> entry = iter.next(); 384 settingsList.add(entry.settings); 385 } 386 return settingsList; 387 } 388 389 Collection<T> getAllSettingsForClient(ClientInfo ci) { 390 ArrayList<T> settingsList = new ArrayList<>(); 391 Iterator<RequestInfo<T>> iter = iterator(); 392 while (iter.hasNext()) { 393 RequestInfo<T> entry = iter.next(); 394 if (entry.clientInfo == ci) { 395 settingsList.add(entry.settings); 396 } 397 } 398 return settingsList; 399 } 400 401 void removeAllForClient(ClientInfo ci) { 402 Iterator<RequestInfo<T>> iter = iterator(); 403 while (iter.hasNext()) { 404 RequestInfo<T> entry = iter.next(); 405 if (entry.clientInfo == ci) { 406 iter.remove(); 407 } 408 } 409 } 410 411 WorkSource createMergedWorkSource() { 412 WorkSource mergedSource = new WorkSource(); 413 for (RequestInfo<T> entry : this) { 414 mergedSource.add(entry.workSource); 415 } 416 return mergedSource; 417 } 418 } 419 420 /** 421 * State machine that holds the state of single scans. Scans should only be active in the 422 * ScanningState. The pending scans and active scans maps are swaped when entering 423 * ScanningState. Any requests queued while scanning will be placed in the pending queue and 424 * executed after transitioning back to IdleState. 425 */ 426 class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler { 427 private final DefaultState mDefaultState = new DefaultState(); 428 private final DriverStartedState mDriverStartedState = new DriverStartedState(); 429 private final IdleState mIdleState = new IdleState(); 430 private final ScanningState mScanningState = new ScanningState(); 431 432 private WifiNative.ScanSettings mActiveScanSettings = null; 433 private RequestList<ScanSettings> mActiveScans = new RequestList<>(); 434 private RequestList<ScanSettings> mPendingScans = new RequestList<>(); 435 436 WifiSingleScanStateMachine(Looper looper) { 437 super("WifiSingleScanStateMachine", looper); 438 439 setLogRecSize(128); 440 setLogOnlyTransitions(false); 441 442 // CHECKSTYLE:OFF IndentationCheck 443 addState(mDefaultState); 444 addState(mDriverStartedState, mDefaultState); 445 addState(mIdleState, mDriverStartedState); 446 addState(mScanningState, mDriverStartedState); 447 // CHECKSTYLE:ON IndentationCheck 448 449 setInitialState(mDefaultState); 450 } 451 452 /** 453 * Called to indicate a change in state for the current scan. 454 * Will dispatch a coresponding event to the state machine 455 */ 456 @Override 457 public void onScanStatus(int event) { 458 if (DBG) localLog("onScanStatus event received, event=" + event); 459 switch(event) { 460 case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE: 461 case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS: 462 case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT: 463 sendMessage(CMD_SCAN_RESULTS_AVAILABLE); 464 break; 465 case WifiNative.WIFI_SCAN_FAILED: 466 sendMessage(CMD_SCAN_FAILED); 467 break; 468 default: 469 Log.e(TAG, "Unknown scan status event: " + event); 470 break; 471 } 472 } 473 474 /** 475 * Called for each full scan result if requested 476 */ 477 @Override 478 public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) { 479 if (DBG) localLog("onFullScanResult received"); 480 sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult); 481 } 482 483 @Override 484 public void onScanPaused(ScanData[] scanData) { 485 // should not happen for single scan 486 Log.e(TAG, "Got scan paused for single scan"); 487 } 488 489 @Override 490 public void onScanRestarted() { 491 // should not happen for single scan 492 Log.e(TAG, "Got scan restarted for single scan"); 493 } 494 495 class DefaultState extends State { 496 @Override 497 public void enter() { 498 mActiveScans.clear(); 499 mPendingScans.clear(); 500 } 501 @Override 502 public boolean processMessage(Message msg) { 503 switch (msg.what) { 504 case CMD_DRIVER_LOADED: 505 transitionTo(mIdleState); 506 return HANDLED; 507 case CMD_DRIVER_UNLOADED: 508 transitionTo(mDefaultState); 509 return HANDLED; 510 case WifiScanner.CMD_START_SINGLE_SCAN: 511 case WifiScanner.CMD_STOP_SINGLE_SCAN: 512 replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available"); 513 return HANDLED; 514 case CMD_SCAN_RESULTS_AVAILABLE: 515 if (DBG) localLog("ignored scan results available event"); 516 return HANDLED; 517 case CMD_FULL_SCAN_RESULTS: 518 if (DBG) localLog("ignored full scan result event"); 519 return HANDLED; 520 default: 521 return NOT_HANDLED; 522 } 523 524 } 525 } 526 527 /** 528 * State representing when the driver is running. This state is not meant to be transitioned 529 * directly, but is instead indented as a parent state of ScanningState and IdleState 530 * to hold common functionality and handle cleaning up scans when the driver is shut down. 531 */ 532 class DriverStartedState extends State { 533 @Override 534 public void exit() { 535 mWifiMetrics.incrementScanReturnEntry( 536 WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED, 537 mPendingScans.size()); 538 sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED, 539 "Scan was interrupted"); 540 } 541 542 @Override 543 public boolean processMessage(Message msg) { 544 ClientInfo ci = mClients.get(msg.replyTo); 545 546 switch (msg.what) { 547 case WifiScanner.CMD_START_SINGLE_SCAN: 548 mWifiMetrics.incrementOneshotScanCount(); 549 int handler = msg.arg2; 550 Bundle scanParams = (Bundle) msg.obj; 551 if (scanParams == null) { 552 logCallback("singleScanInvalidRequest", ci, handler, "null params"); 553 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null"); 554 return HANDLED; 555 } 556 scanParams.setDefusable(true); 557 ScanSettings scanSettings = 558 scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY); 559 WorkSource workSource = 560 scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY); 561 if (validateScanRequest(ci, handler, scanSettings, workSource)) { 562 logScanRequest("addSingleScanRequest", ci, handler, workSource, 563 scanSettings, null); 564 replySucceeded(msg); 565 566 // If there is an active scan that will fulfill the scan request then 567 // mark this request as an active scan, otherwise mark it pending. 568 // If were not currently scanning then try to start a scan. Otherwise 569 // this scan will be scheduled when transitioning back to IdleState 570 // after finishing the current scan. 571 if (getCurrentState() == mScanningState) { 572 if (activeScanSatisfies(scanSettings)) { 573 mActiveScans.addRequest(ci, handler, workSource, scanSettings); 574 } else { 575 mPendingScans.addRequest(ci, handler, workSource, scanSettings); 576 } 577 } else { 578 mPendingScans.addRequest(ci, handler, workSource, scanSettings); 579 tryToStartNewScan(); 580 } 581 } else { 582 logCallback("singleScanInvalidRequest", ci, handler, "bad request"); 583 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 584 mWifiMetrics.incrementScanReturnEntry( 585 WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION, 1); 586 } 587 return HANDLED; 588 case WifiScanner.CMD_STOP_SINGLE_SCAN: 589 removeSingleScanRequest(ci, msg.arg2); 590 return HANDLED; 591 default: 592 return NOT_HANDLED; 593 } 594 } 595 } 596 597 class IdleState extends State { 598 @Override 599 public void enter() { 600 tryToStartNewScan(); 601 } 602 603 @Override 604 public boolean processMessage(Message msg) { 605 return NOT_HANDLED; 606 } 607 } 608 609 class ScanningState extends State { 610 private WorkSource mScanWorkSource; 611 612 @Override 613 public void enter() { 614 mScanWorkSource = mActiveScans.createMergedWorkSource(); 615 try { 616 mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource); 617 } catch (RemoteException e) { 618 loge(e.toString()); 619 } 620 } 621 622 @Override 623 public void exit() { 624 mActiveScanSettings = null; 625 try { 626 mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource); 627 } catch (RemoteException e) { 628 loge(e.toString()); 629 } 630 631 // if any scans are still active (never got results available then indicate failure) 632 mWifiMetrics.incrementScanReturnEntry( 633 WifiMetricsProto.WifiLog.SCAN_UNKNOWN, 634 mActiveScans.size()); 635 sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED, 636 "Scan was interrupted"); 637 } 638 639 @Override 640 public boolean processMessage(Message msg) { 641 switch (msg.what) { 642 case CMD_SCAN_RESULTS_AVAILABLE: 643 mWifiMetrics.incrementScanReturnEntry( 644 WifiMetricsProto.WifiLog.SCAN_SUCCESS, 645 mActiveScans.size()); 646 reportScanResults(mScannerImpl.getLatestSingleScanResults()); 647 mActiveScans.clear(); 648 transitionTo(mIdleState); 649 return HANDLED; 650 case CMD_FULL_SCAN_RESULTS: 651 reportFullScanResult((ScanResult) msg.obj, /* bucketsScanned */ msg.arg2); 652 return HANDLED; 653 case CMD_SCAN_FAILED: 654 mWifiMetrics.incrementScanReturnEntry( 655 WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mActiveScans.size()); 656 sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED, 657 "Scan failed"); 658 transitionTo(mIdleState); 659 return HANDLED; 660 default: 661 return NOT_HANDLED; 662 } 663 } 664 } 665 666 boolean validateScanRequest(ClientInfo ci, int handler, ScanSettings settings, 667 WorkSource workSource) { 668 if (ci == null) { 669 Log.d(TAG, "Failing single scan request ClientInfo not found " + handler); 670 return false; 671 } 672 if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { 673 if (settings.channels == null || settings.channels.length == 0) { 674 Log.d(TAG, "Failing single scan because channel list was empty"); 675 return false; 676 } 677 } 678 return true; 679 } 680 681 boolean activeScanSatisfies(ScanSettings settings) { 682 if (mActiveScanSettings == null) { 683 return false; 684 } 685 686 // there is always one bucket for a single scan 687 WifiNative.BucketSettings activeBucket = mActiveScanSettings.buckets[0]; 688 689 // validate that all requested channels are being scanned 690 ChannelCollection activeChannels = mChannelHelper.createChannelCollection(); 691 activeChannels.addChannels(activeBucket); 692 if (!activeChannels.containsSettings(settings)) { 693 return false; 694 } 695 696 // if the request is for a full scan, but there is no ongoing full scan 697 if ((settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0 698 && (activeBucket.report_events & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) 699 == 0) { 700 return false; 701 } 702 703 if (settings.hiddenNetworkIds != null) { 704 if (mActiveScanSettings.hiddenNetworkIds == null) { 705 return false; 706 } 707 Set<Integer> activeHiddenNetworkIds = new HashSet<>(); 708 for (int id : mActiveScanSettings.hiddenNetworkIds) { 709 activeHiddenNetworkIds.add(id); 710 } 711 for (int id : settings.hiddenNetworkIds) { 712 if (!activeHiddenNetworkIds.contains(id)) { 713 return false; 714 } 715 } 716 } 717 718 return true; 719 } 720 721 void removeSingleScanRequest(ClientInfo ci, int handler) { 722 if (ci != null) { 723 logScanRequest("removeSingleScanRequest", ci, handler, null, null, null); 724 mPendingScans.removeRequest(ci, handler); 725 mActiveScans.removeRequest(ci, handler); 726 } 727 } 728 729 void removeSingleScanRequests(ClientInfo ci) { 730 if (ci != null) { 731 logScanRequest("removeSingleScanRequests", ci, -1, null, null, null); 732 mPendingScans.removeAllForClient(ci); 733 mActiveScans.removeAllForClient(ci); 734 } 735 } 736 737 void tryToStartNewScan() { 738 if (mPendingScans.size() == 0) { // no pending requests 739 return; 740 } 741 mChannelHelper.updateChannels(); 742 // TODO move merging logic to a scheduler 743 WifiNative.ScanSettings settings = new WifiNative.ScanSettings(); 744 settings.num_buckets = 1; 745 WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); 746 bucketSettings.bucket = 0; 747 bucketSettings.period_ms = 0; 748 bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 749 750 ChannelCollection channels = mChannelHelper.createChannelCollection(); 751 HashSet<Integer> hiddenNetworkIdSet = new HashSet<>(); 752 for (RequestInfo<ScanSettings> entry : mPendingScans) { 753 channels.addChannels(entry.settings); 754 if (entry.settings.hiddenNetworkIds != null) { 755 for (int i = 0; i < entry.settings.hiddenNetworkIds.length; i++) { 756 hiddenNetworkIdSet.add(entry.settings.hiddenNetworkIds[i]); 757 } 758 } 759 if ((entry.settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) 760 != 0) { 761 bucketSettings.report_events |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; 762 } 763 } 764 if (hiddenNetworkIdSet.size() > 0) { 765 settings.hiddenNetworkIds = new int[hiddenNetworkIdSet.size()]; 766 int numHiddenNetworks = 0; 767 for (Integer hiddenNetworkId : hiddenNetworkIdSet) { 768 settings.hiddenNetworkIds[numHiddenNetworks++] = hiddenNetworkId; 769 } 770 } 771 772 channels.fillBucketSettings(bucketSettings, Integer.MAX_VALUE); 773 774 settings.buckets = new WifiNative.BucketSettings[] {bucketSettings}; 775 if (mScannerImpl.startSingleScan(settings, this)) { 776 // store the active scan settings 777 mActiveScanSettings = settings; 778 // swap pending and active scan requests 779 RequestList<ScanSettings> tmp = mActiveScans; 780 mActiveScans = mPendingScans; 781 mPendingScans = tmp; 782 // make sure that the pending list is clear 783 mPendingScans.clear(); 784 transitionTo(mScanningState); 785 } else { 786 mWifiMetrics.incrementScanReturnEntry( 787 WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mPendingScans.size()); 788 // notify and cancel failed scans 789 sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED, 790 "Failed to start single scan"); 791 } 792 } 793 794 void sendOpFailedToAllAndClear(RequestList<?> clientHandlers, int reason, 795 String description) { 796 for (RequestInfo<?> entry : clientHandlers) { 797 logCallback("singleScanFailed", entry.clientInfo, entry.handlerId, 798 "reason=" + reason + ", " + description); 799 entry.reportEvent(WifiScanner.CMD_OP_FAILED, 0, 800 new WifiScanner.OperationResult(reason, description)); 801 } 802 clientHandlers.clear(); 803 } 804 805 void reportFullScanResult(ScanResult result, int bucketsScanned) { 806 for (RequestInfo<ScanSettings> entry : mActiveScans) { 807 if (ScanScheduleUtil.shouldReportFullScanResultForSettings(mChannelHelper, 808 result, bucketsScanned, entry.settings, -1)) { 809 entry.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, result); 810 } 811 } 812 813 for (RequestInfo<Void> entry : mSingleScanListeners) { 814 entry.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, result); 815 } 816 } 817 818 void reportScanResults(ScanData results) { 819 if (results != null && results.getResults() != null) { 820 if (results.getResults().length > 0) { 821 mWifiMetrics.incrementNonEmptyScanResultCount(); 822 } else { 823 mWifiMetrics.incrementEmptyScanResultCount(); 824 } 825 } 826 ScanData[] allResults = new ScanData[] {results}; 827 for (RequestInfo<ScanSettings> entry : mActiveScans) { 828 ScanData[] resultsToDeliver = ScanScheduleUtil.filterResultsForSettings( 829 mChannelHelper, allResults, entry.settings, -1); 830 WifiScanner.ParcelableScanData parcelableResultsToDeliver = 831 new WifiScanner.ParcelableScanData(resultsToDeliver); 832 logCallback("singleScanResults", entry.clientInfo, entry.handlerId, 833 describeForLog(resultsToDeliver)); 834 entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableResultsToDeliver); 835 // make sure the handler is removed 836 entry.reportEvent(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, null); 837 } 838 839 WifiScanner.ParcelableScanData parcelableAllResults = 840 new WifiScanner.ParcelableScanData(allResults); 841 for (RequestInfo<Void> entry : mSingleScanListeners) { 842 logCallback("singleScanResults", entry.clientInfo, entry.handlerId, 843 describeForLog(allResults)); 844 entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults); 845 } 846 } 847 } 848 849 class WifiBackgroundScanStateMachine extends StateMachine 850 implements WifiNative.ScanEventHandler, WifiNative.HotlistEventHandler { 851 852 private final DefaultState mDefaultState = new DefaultState(); 853 private final StartedState mStartedState = new StartedState(); 854 private final PausedState mPausedState = new PausedState(); 855 856 private final RequestList<ScanSettings> mActiveBackgroundScans = new RequestList<>(); 857 private final RequestList<WifiScanner.HotlistSettings> mActiveHotlistSettings = 858 new RequestList<>(); 859 860 WifiBackgroundScanStateMachine(Looper looper) { 861 super("WifiBackgroundScanStateMachine", looper); 862 863 setLogRecSize(512); 864 setLogOnlyTransitions(false); 865 866 // CHECKSTYLE:OFF IndentationCheck 867 addState(mDefaultState); 868 addState(mStartedState, mDefaultState); 869 addState(mPausedState, mDefaultState); 870 // CHECKSTYLE:ON IndentationCheck 871 872 setInitialState(mDefaultState); 873 } 874 875 public Collection<ScanSettings> getBackgroundScanSettings(ClientInfo ci) { 876 return mActiveBackgroundScans.getAllSettingsForClient(ci); 877 } 878 879 public void removeBackgroundScanSettings(ClientInfo ci) { 880 mActiveBackgroundScans.removeAllForClient(ci); 881 updateSchedule(); 882 } 883 884 public void removeHotlistSettings(ClientInfo ci) { 885 mActiveHotlistSettings.removeAllForClient(ci); 886 resetHotlist(); 887 } 888 889 @Override 890 public void onScanStatus(int event) { 891 if (DBG) localLog("onScanStatus event received, event=" + event); 892 switch(event) { 893 case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE: 894 case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS: 895 case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT: 896 sendMessage(CMD_SCAN_RESULTS_AVAILABLE); 897 break; 898 case WifiNative.WIFI_SCAN_FAILED: 899 sendMessage(CMD_SCAN_FAILED); 900 break; 901 default: 902 Log.e(TAG, "Unknown scan status event: " + event); 903 break; 904 } 905 } 906 907 @Override 908 public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) { 909 if (DBG) localLog("onFullScanResult received"); 910 sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult); 911 } 912 913 @Override 914 public void onScanPaused(ScanData scanData[]) { 915 if (DBG) localLog("onScanPaused received"); 916 sendMessage(CMD_SCAN_PAUSED, scanData); 917 } 918 919 @Override 920 public void onScanRestarted() { 921 if (DBG) localLog("onScanRestarted received"); 922 sendMessage(CMD_SCAN_RESTARTED); 923 } 924 925 @Override 926 public void onHotlistApFound(ScanResult[] results) { 927 if (DBG) localLog("onHotlistApFound event received"); 928 sendMessage(CMD_HOTLIST_AP_FOUND, 0, 0, results); 929 } 930 931 @Override 932 public void onHotlistApLost(ScanResult[] results) { 933 if (DBG) localLog("onHotlistApLost event received"); 934 sendMessage(CMD_HOTLIST_AP_LOST, 0, 0, results); 935 } 936 937 class DefaultState extends State { 938 @Override 939 public void enter() { 940 if (DBG) localLog("DefaultState"); 941 mActiveBackgroundScans.clear(); 942 mActiveHotlistSettings.clear(); 943 } 944 945 @Override 946 public boolean processMessage(Message msg) { 947 switch (msg.what) { 948 case CMD_DRIVER_LOADED: 949 // TODO this should be moved to a common location since it is used outside 950 // of this state machine. It is ok right now because the driver loaded event 951 // is sent to this state machine first. 952 if (mScannerImpl == null) { 953 mScannerImpl = mScannerImplFactory.create(mContext, mLooper, mClock); 954 mChannelHelper = mScannerImpl.getChannelHelper(); 955 } 956 957 mBackgroundScheduler = new BackgroundScanScheduler(mChannelHelper); 958 959 WifiNative.ScanCapabilities capabilities = 960 new WifiNative.ScanCapabilities(); 961 if (!mScannerImpl.getScanCapabilities(capabilities)) { 962 loge("could not get scan capabilities"); 963 return HANDLED; 964 } 965 mBackgroundScheduler.setMaxBuckets(capabilities.max_scan_buckets); 966 mBackgroundScheduler.setMaxApPerScan(capabilities.max_ap_cache_per_scan); 967 968 Log.i(TAG, "wifi driver loaded with scan capabilities: " 969 + "max buckets=" + capabilities.max_scan_buckets); 970 971 transitionTo(mStartedState); 972 return HANDLED; 973 case CMD_DRIVER_UNLOADED: 974 Log.i(TAG, "wifi driver unloaded"); 975 transitionTo(mDefaultState); 976 break; 977 case WifiScanner.CMD_START_BACKGROUND_SCAN: 978 case WifiScanner.CMD_STOP_BACKGROUND_SCAN: 979 case WifiScanner.CMD_START_SINGLE_SCAN: 980 case WifiScanner.CMD_STOP_SINGLE_SCAN: 981 case WifiScanner.CMD_SET_HOTLIST: 982 case WifiScanner.CMD_RESET_HOTLIST: 983 case WifiScanner.CMD_GET_SCAN_RESULTS: 984 replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available"); 985 break; 986 987 case CMD_SCAN_RESULTS_AVAILABLE: 988 if (DBG) localLog("ignored scan results available event"); 989 break; 990 991 case CMD_FULL_SCAN_RESULTS: 992 if (DBG) localLog("ignored full scan result event"); 993 break; 994 995 default: 996 break; 997 } 998 999 return HANDLED; 1000 } 1001 } 1002 1003 class StartedState extends State { 1004 1005 @Override 1006 public void enter() { 1007 if (DBG) localLog("StartedState"); 1008 } 1009 1010 @Override 1011 public void exit() { 1012 sendBackgroundScanFailedToAllAndClear( 1013 WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted"); 1014 sendHotlistFailedToAllAndClear( 1015 WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted"); 1016 mScannerImpl.cleanup(); 1017 } 1018 1019 @Override 1020 public boolean processMessage(Message msg) { 1021 ClientInfo ci = mClients.get(msg.replyTo); 1022 1023 switch (msg.what) { 1024 case CMD_DRIVER_LOADED: 1025 return NOT_HANDLED; 1026 case CMD_DRIVER_UNLOADED: 1027 return NOT_HANDLED; 1028 case WifiScanner.CMD_START_BACKGROUND_SCAN: { 1029 mWifiMetrics.incrementBackgroundScanCount(); 1030 Bundle scanParams = (Bundle) msg.obj; 1031 if (scanParams == null) { 1032 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null"); 1033 return HANDLED; 1034 } 1035 scanParams.setDefusable(true); 1036 ScanSettings scanSettings = 1037 scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY); 1038 WorkSource workSource = 1039 scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY); 1040 if (addBackgroundScanRequest(ci, msg.arg2, scanSettings, workSource)) { 1041 replySucceeded(msg); 1042 } else { 1043 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 1044 } 1045 break; 1046 } 1047 case WifiScanner.CMD_STOP_BACKGROUND_SCAN: 1048 removeBackgroundScanRequest(ci, msg.arg2); 1049 break; 1050 case WifiScanner.CMD_GET_SCAN_RESULTS: 1051 reportScanResults(mScannerImpl.getLatestBatchedScanResults(true)); 1052 replySucceeded(msg); 1053 break; 1054 case WifiScanner.CMD_SET_HOTLIST: 1055 if (addHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj)) { 1056 replySucceeded(msg); 1057 } else { 1058 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 1059 } 1060 break; 1061 case WifiScanner.CMD_RESET_HOTLIST: 1062 removeHotlist(ci, msg.arg2); 1063 break; 1064 case CMD_SCAN_RESULTS_AVAILABLE: 1065 reportScanResults(mScannerImpl.getLatestBatchedScanResults(true)); 1066 break; 1067 case CMD_FULL_SCAN_RESULTS: 1068 reportFullScanResult((ScanResult) msg.obj, /* bucketsScanned */ msg.arg2); 1069 break; 1070 case CMD_HOTLIST_AP_FOUND: 1071 reportHotlistResults(WifiScanner.CMD_AP_FOUND, (ScanResult[]) msg.obj); 1072 break; 1073 case CMD_HOTLIST_AP_LOST: 1074 reportHotlistResults(WifiScanner.CMD_AP_LOST, (ScanResult[]) msg.obj); 1075 break; 1076 case CMD_SCAN_PAUSED: 1077 reportScanResults((ScanData[]) msg.obj); 1078 transitionTo(mPausedState); 1079 break; 1080 case CMD_SCAN_FAILED: 1081 Log.e(TAG, "WifiScanner background scan gave CMD_SCAN_FAILED"); 1082 sendBackgroundScanFailedToAllAndClear( 1083 WifiScanner.REASON_UNSPECIFIED, "Background Scan failed"); 1084 break; 1085 default: 1086 return NOT_HANDLED; 1087 } 1088 1089 return HANDLED; 1090 } 1091 } 1092 1093 class PausedState extends State { 1094 @Override 1095 public void enter() { 1096 if (DBG) localLog("PausedState"); 1097 } 1098 1099 @Override 1100 public boolean processMessage(Message msg) { 1101 switch (msg.what) { 1102 case CMD_SCAN_RESTARTED: 1103 transitionTo(mStartedState); 1104 break; 1105 default: 1106 deferMessage(msg); 1107 break; 1108 } 1109 return HANDLED; 1110 } 1111 } 1112 1113 private boolean addBackgroundScanRequest(ClientInfo ci, int handler, 1114 ScanSettings settings, WorkSource workSource) { 1115 // sanity check the input 1116 if (ci == null) { 1117 Log.d(TAG, "Failing scan request ClientInfo not found " + handler); 1118 return false; 1119 } 1120 if (settings.periodInMs < WifiScanner.MIN_SCAN_PERIOD_MS) { 1121 loge("Failing scan request because periodInMs is " + settings.periodInMs 1122 + ", min scan period is: " + WifiScanner.MIN_SCAN_PERIOD_MS); 1123 return false; 1124 } 1125 1126 if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED && settings.channels == null) { 1127 loge("Channels was null with unspecified band"); 1128 return false; 1129 } 1130 1131 if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED 1132 && settings.channels.length == 0) { 1133 loge("No channels specified"); 1134 return false; 1135 } 1136 1137 int minSupportedPeriodMs = mChannelHelper.estimateScanDuration(settings); 1138 if (settings.periodInMs < minSupportedPeriodMs) { 1139 loge("Failing scan request because minSupportedPeriodMs is " 1140 + minSupportedPeriodMs + " but the request wants " + settings.periodInMs); 1141 return false; 1142 } 1143 1144 // check truncated binary exponential back off scan settings 1145 if (settings.maxPeriodInMs != 0 && settings.maxPeriodInMs != settings.periodInMs) { 1146 if (settings.maxPeriodInMs < settings.periodInMs) { 1147 loge("Failing scan request because maxPeriodInMs is " + settings.maxPeriodInMs 1148 + " but less than periodInMs " + settings.periodInMs); 1149 return false; 1150 } 1151 if (settings.maxPeriodInMs > WifiScanner.MAX_SCAN_PERIOD_MS) { 1152 loge("Failing scan request because maxSupportedPeriodMs is " 1153 + WifiScanner.MAX_SCAN_PERIOD_MS + " but the request wants " 1154 + settings.maxPeriodInMs); 1155 return false; 1156 } 1157 if (settings.stepCount < 1) { 1158 loge("Failing scan request because stepCount is " + settings.stepCount 1159 + " which is less than 1"); 1160 return false; 1161 } 1162 } 1163 1164 logScanRequest("addBackgroundScanRequest", ci, handler, null, settings, null); 1165 mActiveBackgroundScans.addRequest(ci, handler, workSource, settings); 1166 1167 if (updateSchedule()) { 1168 return true; 1169 } else { 1170 mActiveBackgroundScans.removeRequest(ci, handler); 1171 localLog("Failing scan request because failed to reset scan"); 1172 return false; 1173 } 1174 } 1175 1176 private boolean updateSchedule() { 1177 if (mChannelHelper == null || mBackgroundScheduler == null || mScannerImpl == null) { 1178 loge("Failed to update schedule because WifiScanningService is not initialized"); 1179 return false; 1180 } 1181 mChannelHelper.updateChannels(); 1182 Collection<ScanSettings> settings = mActiveBackgroundScans.getAllSettings(); 1183 1184 mBackgroundScheduler.updateSchedule(settings); 1185 WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule(); 1186 1187 if (ScanScheduleUtil.scheduleEquals(mPreviousSchedule, schedule)) { 1188 if (DBG) Log.d(TAG, "schedule updated with no change"); 1189 return true; 1190 } 1191 1192 mPreviousSchedule = schedule; 1193 1194 if (schedule.num_buckets == 0) { 1195 mScannerImpl.stopBatchedScan(); 1196 if (DBG) Log.d(TAG, "scan stopped"); 1197 return true; 1198 } else { 1199 localLog("starting scan: " 1200 + "base period=" + schedule.base_period_ms 1201 + ", max ap per scan=" + schedule.max_ap_per_scan 1202 + ", batched scans=" + schedule.report_threshold_num_scans); 1203 for (int b = 0; b < schedule.num_buckets; b++) { 1204 WifiNative.BucketSettings bucket = schedule.buckets[b]; 1205 localLog("bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)" 1206 + "[" + bucket.report_events + "]: " 1207 + ChannelHelper.toString(bucket)); 1208 } 1209 1210 if (mScannerImpl.startBatchedScan(schedule, this)) { 1211 if (DBG) { 1212 Log.d(TAG, "scan restarted with " + schedule.num_buckets 1213 + " bucket(s) and base period: " + schedule.base_period_ms); 1214 } 1215 return true; 1216 } else { 1217 mPreviousSchedule = null; 1218 loge("error starting scan: " 1219 + "base period=" + schedule.base_period_ms 1220 + ", max ap per scan=" + schedule.max_ap_per_scan 1221 + ", batched scans=" + schedule.report_threshold_num_scans); 1222 for (int b = 0; b < schedule.num_buckets; b++) { 1223 WifiNative.BucketSettings bucket = schedule.buckets[b]; 1224 loge("bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)" 1225 + "[" + bucket.report_events + "]: " 1226 + ChannelHelper.toString(bucket)); 1227 } 1228 return false; 1229 } 1230 } 1231 } 1232 1233 private void removeBackgroundScanRequest(ClientInfo ci, int handler) { 1234 if (ci != null) { 1235 ScanSettings settings = mActiveBackgroundScans.removeRequest(ci, handler); 1236 logScanRequest("removeBackgroundScanRequest", ci, handler, null, settings, null); 1237 updateSchedule(); 1238 } 1239 } 1240 1241 private void reportFullScanResult(ScanResult result, int bucketsScanned) { 1242 for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) { 1243 ClientInfo ci = entry.clientInfo; 1244 int handler = entry.handlerId; 1245 ScanSettings settings = entry.settings; 1246 if (mBackgroundScheduler.shouldReportFullScanResultForSettings( 1247 result, bucketsScanned, settings)) { 1248 ScanResult newResult = new ScanResult(result); 1249 if (result.informationElements != null) { 1250 newResult.informationElements = result.informationElements.clone(); 1251 } 1252 else { 1253 newResult.informationElements = null; 1254 } 1255 ci.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, newResult); 1256 } 1257 } 1258 } 1259 1260 private void reportScanResults(ScanData[] results) { 1261 for (ScanData result : results) { 1262 if (result != null && result.getResults() != null) { 1263 if (result.getResults().length > 0) { 1264 mWifiMetrics.incrementNonEmptyScanResultCount(); 1265 } else { 1266 mWifiMetrics.incrementEmptyScanResultCount(); 1267 } 1268 } 1269 } 1270 for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) { 1271 ClientInfo ci = entry.clientInfo; 1272 int handler = entry.handlerId; 1273 ScanSettings settings = entry.settings; 1274 ScanData[] resultsToDeliver = 1275 mBackgroundScheduler.filterResultsForSettings(results, settings); 1276 if (resultsToDeliver != null) { 1277 logCallback("backgroundScanResults", ci, handler, 1278 describeForLog(resultsToDeliver)); 1279 WifiScanner.ParcelableScanData parcelableScanData = 1280 new WifiScanner.ParcelableScanData(resultsToDeliver); 1281 ci.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData); 1282 } 1283 } 1284 } 1285 1286 private void sendBackgroundScanFailedToAllAndClear(int reason, String description) { 1287 for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) { 1288 ClientInfo ci = entry.clientInfo; 1289 int handler = entry.handlerId; 1290 ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler, 1291 new WifiScanner.OperationResult(reason, description)); 1292 } 1293 mActiveBackgroundScans.clear(); 1294 } 1295 1296 private boolean addHotlist(ClientInfo ci, int handler, 1297 WifiScanner.HotlistSettings settings) { 1298 if (ci == null) { 1299 Log.d(TAG, "Failing hotlist request ClientInfo not found " + handler); 1300 return false; 1301 } 1302 mActiveHotlistSettings.addRequest(ci, handler, null, settings); 1303 resetHotlist(); 1304 return true; 1305 } 1306 1307 private void removeHotlist(ClientInfo ci, int handler) { 1308 if (ci != null) { 1309 mActiveHotlistSettings.removeRequest(ci, handler); 1310 resetHotlist(); 1311 } 1312 } 1313 1314 private void resetHotlist() { 1315 if (mScannerImpl == null) { 1316 loge("Failed to update hotlist because WifiScanningService is not initialized"); 1317 return; 1318 } 1319 1320 Collection<WifiScanner.HotlistSettings> settings = 1321 mActiveHotlistSettings.getAllSettings(); 1322 int num_hotlist_ap = 0; 1323 1324 for (WifiScanner.HotlistSettings s : settings) { 1325 num_hotlist_ap += s.bssidInfos.length; 1326 } 1327 1328 if (num_hotlist_ap == 0) { 1329 mScannerImpl.resetHotlist(); 1330 } else { 1331 BssidInfo[] bssidInfos = new BssidInfo[num_hotlist_ap]; 1332 int apLostThreshold = Integer.MAX_VALUE; 1333 int index = 0; 1334 for (WifiScanner.HotlistSettings s : settings) { 1335 for (int i = 0; i < s.bssidInfos.length; i++, index++) { 1336 bssidInfos[index] = s.bssidInfos[i]; 1337 } 1338 if (s.apLostThreshold < apLostThreshold) { 1339 apLostThreshold = s.apLostThreshold; 1340 } 1341 } 1342 1343 WifiScanner.HotlistSettings mergedSettings = new WifiScanner.HotlistSettings(); 1344 mergedSettings.bssidInfos = bssidInfos; 1345 mergedSettings.apLostThreshold = apLostThreshold; 1346 mScannerImpl.setHotlist(mergedSettings, this); 1347 } 1348 } 1349 1350 private void reportHotlistResults(int what, ScanResult[] results) { 1351 if (DBG) localLog("reportHotlistResults " + what + " results " + results.length); 1352 for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) { 1353 ClientInfo ci = entry.clientInfo; 1354 int handler = entry.handlerId; 1355 WifiScanner.HotlistSettings settings = entry.settings; 1356 int num_results = 0; 1357 for (ScanResult result : results) { 1358 for (BssidInfo BssidInfo : settings.bssidInfos) { 1359 if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) { 1360 num_results++; 1361 break; 1362 } 1363 } 1364 } 1365 if (num_results == 0) { 1366 // nothing to report 1367 return; 1368 } 1369 ScanResult[] results2 = new ScanResult[num_results]; 1370 int index = 0; 1371 for (ScanResult result : results) { 1372 for (BssidInfo BssidInfo : settings.bssidInfos) { 1373 if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) { 1374 results2[index] = result; 1375 index++; 1376 } 1377 } 1378 } 1379 WifiScanner.ParcelableScanResults parcelableScanResults = 1380 new WifiScanner.ParcelableScanResults(results2); 1381 1382 ci.reportEvent(what, 0, handler, parcelableScanResults); 1383 } 1384 } 1385 1386 private void sendHotlistFailedToAllAndClear(int reason, String description) { 1387 for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) { 1388 ClientInfo ci = entry.clientInfo; 1389 int handler = entry.handlerId; 1390 ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler, 1391 new WifiScanner.OperationResult(reason, description)); 1392 } 1393 mActiveHotlistSettings.clear(); 1394 } 1395 } 1396 1397 /** 1398 * PNO scan state machine has 5 states: 1399 * -Default State 1400 * -Started State 1401 * -Hw Pno Scan state 1402 * -Single Scan state 1403 * -Sw Pno Scan state 1404 * 1405 * These are the main state transitions: 1406 * 1. Start at |Default State| 1407 * 2. Move to |Started State| when we get the |WIFI_SCAN_AVAILABLE| broadcast from WifiManager. 1408 * 3. When a new PNO scan request comes in: 1409 * a.1. Switch to |Hw Pno Scan state| when the device supports HW PNO 1410 * (This could either be HAL based ePNO or supplicant based PNO). 1411 * a.2. In |Hw Pno Scan state| when PNO scan results are received, check if the result 1412 * contains IE (information elements). If yes, send the results to the client, else 1413 * switch to |Single Scan state| and send the result to the client when the scan result 1414 * is obtained. 1415 * b.1. Switch to |Sw Pno Scan state| when the device does not supports HW PNO 1416 * (This is for older devices which do not support HW PNO and for connected PNO on 1417 * devices which support supplicant based PNO) 1418 * b.2. In |Sw Pno Scan state| send the result to the client when the background scan result 1419 * is obtained 1420 * 1421 * Note: PNO scans only work for a single client today. We don't have support in HW to support 1422 * multiple requests at the same time, so will need non-trivial changes to support (if at all 1423 * possible) in WifiScanningService. 1424 */ 1425 class WifiPnoScanStateMachine extends StateMachine implements WifiNative.PnoEventHandler { 1426 1427 private final DefaultState mDefaultState = new DefaultState(); 1428 private final StartedState mStartedState = new StartedState(); 1429 private final HwPnoScanState mHwPnoScanState = new HwPnoScanState(); 1430 private final SwPnoScanState mSwPnoScanState = new SwPnoScanState(); 1431 private final SingleScanState mSingleScanState = new SingleScanState(); 1432 private InternalClientInfo mInternalClientInfo; 1433 1434 private final RequestList<Pair<PnoSettings, ScanSettings>> mActivePnoScans = 1435 new RequestList<>(); 1436 1437 WifiPnoScanStateMachine(Looper looper) { 1438 super("WifiPnoScanStateMachine", looper); 1439 1440 setLogRecSize(512); 1441 setLogOnlyTransitions(false); 1442 1443 // CHECKSTYLE:OFF IndentationCheck 1444 addState(mDefaultState); 1445 addState(mStartedState, mDefaultState); 1446 addState(mHwPnoScanState, mStartedState); 1447 addState(mSingleScanState, mHwPnoScanState); 1448 addState(mSwPnoScanState, mStartedState); 1449 // CHECKSTYLE:ON IndentationCheck 1450 1451 setInitialState(mDefaultState); 1452 } 1453 1454 public void removePnoSettings(ClientInfo ci) { 1455 mActivePnoScans.removeAllForClient(ci); 1456 transitionTo(mStartedState); 1457 } 1458 1459 @Override 1460 public void onPnoNetworkFound(ScanResult[] results) { 1461 if (DBG) localLog("onWifiPnoNetworkFound event received"); 1462 sendMessage(CMD_PNO_NETWORK_FOUND, 0, 0, results); 1463 } 1464 1465 @Override 1466 public void onPnoScanFailed() { 1467 if (DBG) localLog("onWifiPnoScanFailed event received"); 1468 sendMessage(CMD_PNO_SCAN_FAILED, 0, 0, null); 1469 } 1470 1471 class DefaultState extends State { 1472 @Override 1473 public void enter() { 1474 if (DBG) localLog("DefaultState"); 1475 } 1476 1477 @Override 1478 public boolean processMessage(Message msg) { 1479 switch (msg.what) { 1480 case CMD_DRIVER_LOADED: 1481 transitionTo(mStartedState); 1482 break; 1483 case CMD_DRIVER_UNLOADED: 1484 transitionTo(mDefaultState); 1485 break; 1486 case WifiScanner.CMD_START_PNO_SCAN: 1487 case WifiScanner.CMD_STOP_PNO_SCAN: 1488 replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available"); 1489 break; 1490 case CMD_PNO_NETWORK_FOUND: 1491 case CMD_PNO_SCAN_FAILED: 1492 case WifiScanner.CMD_SCAN_RESULT: 1493 case WifiScanner.CMD_OP_FAILED: 1494 loge("Unexpected message " + msg.what); 1495 break; 1496 default: 1497 return NOT_HANDLED; 1498 } 1499 return HANDLED; 1500 } 1501 } 1502 1503 class StartedState extends State { 1504 @Override 1505 public void enter() { 1506 if (DBG) localLog("StartedState"); 1507 } 1508 1509 @Override 1510 public void exit() { 1511 sendPnoScanFailedToAllAndClear( 1512 WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted"); 1513 } 1514 1515 @Override 1516 public boolean processMessage(Message msg) { 1517 ClientInfo ci = mClients.get(msg.replyTo); 1518 switch (msg.what) { 1519 case WifiScanner.CMD_START_PNO_SCAN: 1520 Bundle pnoParams = (Bundle) msg.obj; 1521 if (pnoParams == null) { 1522 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null"); 1523 return HANDLED; 1524 } 1525 pnoParams.setDefusable(true); 1526 PnoSettings pnoSettings = 1527 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY); 1528 // This message is handled after the transition to SwPnoScan/HwPnoScan state 1529 deferMessage(msg); 1530 if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) { 1531 transitionTo(mHwPnoScanState); 1532 } else { 1533 transitionTo(mSwPnoScanState); 1534 } 1535 break; 1536 case WifiScanner.CMD_STOP_PNO_SCAN: 1537 replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "no scan running"); 1538 break; 1539 default: 1540 return NOT_HANDLED; 1541 } 1542 return HANDLED; 1543 } 1544 } 1545 1546 class HwPnoScanState extends State { 1547 @Override 1548 public void enter() { 1549 if (DBG) localLog("HwPnoScanState"); 1550 } 1551 1552 @Override 1553 public void exit() { 1554 // Reset PNO scan in ScannerImpl before we exit. 1555 mScannerImpl.resetHwPnoList(); 1556 removeInternalClient(); 1557 } 1558 1559 @Override 1560 public boolean processMessage(Message msg) { 1561 ClientInfo ci = mClients.get(msg.replyTo); 1562 switch (msg.what) { 1563 case WifiScanner.CMD_START_PNO_SCAN: 1564 Bundle pnoParams = (Bundle) msg.obj; 1565 if (pnoParams == null) { 1566 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null"); 1567 return HANDLED; 1568 } 1569 pnoParams.setDefusable(true); 1570 PnoSettings pnoSettings = 1571 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY); 1572 ScanSettings scanSettings = 1573 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY); 1574 if (addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) { 1575 replySucceeded(msg); 1576 } else { 1577 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 1578 transitionTo(mStartedState); 1579 } 1580 break; 1581 case WifiScanner.CMD_STOP_PNO_SCAN: 1582 removeHwPnoScanRequest(ci, msg.arg2); 1583 transitionTo(mStartedState); 1584 break; 1585 case CMD_PNO_NETWORK_FOUND: 1586 ScanResult[] scanResults = ((ScanResult[]) msg.obj); 1587 if (isSingleScanNeeded(scanResults)) { 1588 ScanSettings activeScanSettings = getScanSettings(); 1589 if (activeScanSettings == null) { 1590 sendPnoScanFailedToAllAndClear( 1591 WifiScanner.REASON_UNSPECIFIED, 1592 "couldn't retrieve setting"); 1593 transitionTo(mStartedState); 1594 } else { 1595 addSingleScanRequest(activeScanSettings); 1596 transitionTo(mSingleScanState); 1597 } 1598 } else { 1599 reportPnoNetworkFound((ScanResult[]) msg.obj); 1600 } 1601 break; 1602 case CMD_PNO_SCAN_FAILED: 1603 sendPnoScanFailedToAllAndClear( 1604 WifiScanner.REASON_UNSPECIFIED, "pno scan failed"); 1605 transitionTo(mStartedState); 1606 break; 1607 default: 1608 return NOT_HANDLED; 1609 } 1610 return HANDLED; 1611 } 1612 } 1613 1614 class SingleScanState extends State { 1615 @Override 1616 public void enter() { 1617 if (DBG) localLog("SingleScanState"); 1618 } 1619 1620 @Override 1621 public boolean processMessage(Message msg) { 1622 ClientInfo ci = mClients.get(msg.replyTo); 1623 switch (msg.what) { 1624 case WifiScanner.CMD_SCAN_RESULT: 1625 WifiScanner.ParcelableScanData parcelableScanData = 1626 (WifiScanner.ParcelableScanData) msg.obj; 1627 ScanData[] scanDatas = parcelableScanData.getResults(); 1628 ScanData lastScanData = scanDatas[scanDatas.length - 1]; 1629 reportPnoNetworkFound(lastScanData.getResults()); 1630 transitionTo(mHwPnoScanState); 1631 break; 1632 case WifiScanner.CMD_OP_FAILED: 1633 sendPnoScanFailedToAllAndClear( 1634 WifiScanner.REASON_UNSPECIFIED, "single scan failed"); 1635 transitionTo(mStartedState); 1636 break; 1637 default: 1638 return NOT_HANDLED; 1639 } 1640 return HANDLED; 1641 } 1642 } 1643 1644 class SwPnoScanState extends State { 1645 private final ArrayList<ScanResult> mSwPnoFullScanResults = new ArrayList<>(); 1646 1647 @Override 1648 public void enter() { 1649 if (DBG) localLog("SwPnoScanState"); 1650 mSwPnoFullScanResults.clear(); 1651 } 1652 1653 @Override 1654 public void exit() { 1655 removeInternalClient(); 1656 } 1657 1658 @Override 1659 public boolean processMessage(Message msg) { 1660 ClientInfo ci = mClients.get(msg.replyTo); 1661 switch (msg.what) { 1662 case WifiScanner.CMD_START_PNO_SCAN: 1663 Bundle pnoParams = (Bundle) msg.obj; 1664 if (pnoParams == null) { 1665 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null"); 1666 return HANDLED; 1667 } 1668 pnoParams.setDefusable(true); 1669 PnoSettings pnoSettings = 1670 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY); 1671 ScanSettings scanSettings = 1672 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY); 1673 if (addSwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) { 1674 replySucceeded(msg); 1675 } else { 1676 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 1677 transitionTo(mStartedState); 1678 } 1679 break; 1680 case WifiScanner.CMD_STOP_PNO_SCAN: 1681 removeSwPnoScanRequest(ci, msg.arg2); 1682 transitionTo(mStartedState); 1683 break; 1684 case WifiScanner.CMD_FULL_SCAN_RESULT: 1685 // Aggregate full scan results until we get the |CMD_SCAN_RESULT| message 1686 mSwPnoFullScanResults.add((ScanResult) msg.obj); 1687 break; 1688 case WifiScanner.CMD_SCAN_RESULT: 1689 ScanResult[] scanResults = mSwPnoFullScanResults.toArray( 1690 new ScanResult[mSwPnoFullScanResults.size()]); 1691 reportPnoNetworkFound(scanResults); 1692 mSwPnoFullScanResults.clear(); 1693 break; 1694 case WifiScanner.CMD_OP_FAILED: 1695 sendPnoScanFailedToAllAndClear( 1696 WifiScanner.REASON_UNSPECIFIED, "background scan failed"); 1697 transitionTo(mStartedState); 1698 break; 1699 default: 1700 return NOT_HANDLED; 1701 } 1702 return HANDLED; 1703 } 1704 } 1705 1706 private WifiNative.PnoSettings convertPnoSettingsToNative(PnoSettings pnoSettings) { 1707 WifiNative.PnoSettings nativePnoSetting = new WifiNative.PnoSettings(); 1708 nativePnoSetting.min5GHzRssi = pnoSettings.min5GHzRssi; 1709 nativePnoSetting.min24GHzRssi = pnoSettings.min24GHzRssi; 1710 nativePnoSetting.initialScoreMax = pnoSettings.initialScoreMax; 1711 nativePnoSetting.currentConnectionBonus = pnoSettings.currentConnectionBonus; 1712 nativePnoSetting.sameNetworkBonus = pnoSettings.sameNetworkBonus; 1713 nativePnoSetting.secureBonus = pnoSettings.secureBonus; 1714 nativePnoSetting.band5GHzBonus = pnoSettings.band5GHzBonus; 1715 nativePnoSetting.isConnected = pnoSettings.isConnected; 1716 nativePnoSetting.networkList = 1717 new WifiNative.PnoNetwork[pnoSettings.networkList.length]; 1718 for (int i = 0; i < pnoSettings.networkList.length; i++) { 1719 nativePnoSetting.networkList[i] = new WifiNative.PnoNetwork(); 1720 nativePnoSetting.networkList[i].ssid = pnoSettings.networkList[i].ssid; 1721 nativePnoSetting.networkList[i].networkId = pnoSettings.networkList[i].networkId; 1722 nativePnoSetting.networkList[i].priority = pnoSettings.networkList[i].priority; 1723 nativePnoSetting.networkList[i].flags = pnoSettings.networkList[i].flags; 1724 nativePnoSetting.networkList[i].auth_bit_field = 1725 pnoSettings.networkList[i].authBitField; 1726 } 1727 return nativePnoSetting; 1728 } 1729 1730 // Retrieve the only active scan settings. 1731 private ScanSettings getScanSettings() { 1732 for (Pair<PnoSettings, ScanSettings> settingsPair : mActivePnoScans.getAllSettings()) { 1733 return settingsPair.second; 1734 } 1735 return null; 1736 } 1737 1738 private void removeInternalClient() { 1739 if (mInternalClientInfo != null) { 1740 mInternalClientInfo.cleanup(); 1741 mInternalClientInfo = null; 1742 } else { 1743 Log.w(TAG, "No Internal client for PNO"); 1744 } 1745 } 1746 1747 private void addInternalClient(ClientInfo ci) { 1748 if (mInternalClientInfo == null) { 1749 mInternalClientInfo = 1750 new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler())); 1751 mInternalClientInfo.register(); 1752 } else { 1753 Log.w(TAG, "Internal client for PNO already exists"); 1754 } 1755 } 1756 1757 private void addPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings, 1758 PnoSettings pnoSettings) { 1759 mActivePnoScans.addRequest(ci, handler, WifiStateMachine.WIFI_WORK_SOURCE, 1760 Pair.create(pnoSettings, scanSettings)); 1761 addInternalClient(ci); 1762 } 1763 1764 private Pair<PnoSettings, ScanSettings> removePnoScanRequest(ClientInfo ci, int handler) { 1765 Pair<PnoSettings, ScanSettings> settings = mActivePnoScans.removeRequest(ci, handler); 1766 return settings; 1767 } 1768 1769 private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings, 1770 PnoSettings pnoSettings) { 1771 if (ci == null) { 1772 Log.d(TAG, "Failing scan request ClientInfo not found " + handler); 1773 return false; 1774 } 1775 if (!mActivePnoScans.isEmpty()) { 1776 loge("Failing scan request because there is already an active scan"); 1777 return false; 1778 } 1779 WifiNative.PnoSettings nativePnoSettings = convertPnoSettingsToNative(pnoSettings); 1780 if (!mScannerImpl.setHwPnoList(nativePnoSettings, mPnoScanStateMachine)) { 1781 return false; 1782 } 1783 logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings); 1784 addPnoScanRequest(ci, handler, scanSettings, pnoSettings); 1785 // HW PNO is supported, check if we need a background scan running for this. 1786 if (mScannerImpl.shouldScheduleBackgroundScanForHwPno()) { 1787 addBackgroundScanRequest(scanSettings); 1788 } 1789 return true; 1790 } 1791 1792 private void removeHwPnoScanRequest(ClientInfo ci, int handler) { 1793 if (ci != null) { 1794 Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler); 1795 logScanRequest("removeHwPnoScanRequest", ci, handler, null, 1796 settings.second, settings.first); 1797 } 1798 } 1799 1800 private boolean addSwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings, 1801 PnoSettings pnoSettings) { 1802 if (ci == null) { 1803 Log.d(TAG, "Failing scan request ClientInfo not found " + handler); 1804 return false; 1805 } 1806 if (!mActivePnoScans.isEmpty()) { 1807 loge("Failing scan request because there is already an active scan"); 1808 return false; 1809 } 1810 logScanRequest("addSwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings); 1811 addPnoScanRequest(ci, handler, scanSettings, pnoSettings); 1812 // HW PNO is not supported, we need to revert to normal background scans and 1813 // report events after each scan and we need full scan results to get the IE information 1814 scanSettings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN 1815 | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; 1816 addBackgroundScanRequest(scanSettings); 1817 return true; 1818 } 1819 1820 private void removeSwPnoScanRequest(ClientInfo ci, int handler) { 1821 if (ci != null) { 1822 Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler); 1823 logScanRequest("removeSwPnoScanRequest", ci, handler, null, 1824 settings.second, settings.first); 1825 } 1826 } 1827 1828 private void reportPnoNetworkFound(ScanResult[] results) { 1829 WifiScanner.ParcelableScanResults parcelableScanResults = 1830 new WifiScanner.ParcelableScanResults(results); 1831 for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) { 1832 ClientInfo ci = entry.clientInfo; 1833 int handler = entry.handlerId; 1834 logCallback("pnoNetworkFound", ci, handler, describeForLog(results)); 1835 ci.reportEvent( 1836 WifiScanner.CMD_PNO_NETWORK_FOUND, 0, handler, parcelableScanResults); 1837 } 1838 } 1839 1840 private void sendPnoScanFailedToAllAndClear(int reason, String description) { 1841 for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) { 1842 ClientInfo ci = entry.clientInfo; 1843 int handler = entry.handlerId; 1844 ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler, 1845 new WifiScanner.OperationResult(reason, description)); 1846 } 1847 mActivePnoScans.clear(); 1848 } 1849 1850 private void addBackgroundScanRequest(ScanSettings settings) { 1851 if (DBG) localLog("Starting background scan"); 1852 if (mInternalClientInfo != null) { 1853 mInternalClientInfo.sendRequestToClientHandler( 1854 WifiScanner.CMD_START_BACKGROUND_SCAN, settings, 1855 WifiStateMachine.WIFI_WORK_SOURCE); 1856 } 1857 } 1858 1859 private void addSingleScanRequest(ScanSettings settings) { 1860 if (DBG) localLog("Starting single scan"); 1861 if (mInternalClientInfo != null) { 1862 mInternalClientInfo.sendRequestToClientHandler( 1863 WifiScanner.CMD_START_SINGLE_SCAN, settings, 1864 WifiStateMachine.WIFI_WORK_SOURCE); 1865 } 1866 } 1867 1868 /** 1869 * Checks if IE are present in scan data, if no single scan is needed to report event to 1870 * client 1871 */ 1872 private boolean isSingleScanNeeded(ScanResult[] scanResults) { 1873 for (ScanResult scanResult : scanResults) { 1874 if (scanResult.informationElements != null 1875 && scanResult.informationElements.length > 0) { 1876 return false; 1877 } 1878 } 1879 return true; 1880 } 1881 } 1882 1883 private abstract class ClientInfo { 1884 private final int mUid; 1885 private final WorkSource mWorkSource; 1886 private boolean mScanWorkReported = false; 1887 protected final Messenger mMessenger; 1888 1889 ClientInfo(int uid, Messenger messenger) { 1890 mUid = uid; 1891 mMessenger = messenger; 1892 mWorkSource = new WorkSource(uid); 1893 } 1894 1895 /** 1896 * Register this client to main client map. 1897 */ 1898 public void register() { 1899 mClients.put(mMessenger, this); 1900 } 1901 1902 /** 1903 * Unregister this client from main client map. 1904 */ 1905 private void unregister() { 1906 mClients.remove(mMessenger); 1907 } 1908 1909 public void cleanup() { 1910 mSingleScanListeners.removeAllForClient(this); 1911 mSingleScanStateMachine.removeSingleScanRequests(this); 1912 mBackgroundScanStateMachine.removeBackgroundScanSettings(this); 1913 mBackgroundScanStateMachine.removeHotlistSettings(this); 1914 unregister(); 1915 localLog("Successfully stopped all requests for client " + this); 1916 } 1917 1918 public int getUid() { 1919 return mUid; 1920 } 1921 1922 public void reportEvent(int what, int arg1, int arg2) { 1923 reportEvent(what, arg1, arg2, null); 1924 } 1925 1926 // This has to be implemented by subclasses to report events back to clients. 1927 public abstract void reportEvent(int what, int arg1, int arg2, Object obj); 1928 1929 // TODO(b/27903217): Blame scan on provided work source 1930 private void reportBatchedScanStart() { 1931 if (mUid == 0) 1932 return; 1933 1934 int csph = getCsph(); 1935 1936 try { 1937 mBatteryStats.noteWifiBatchedScanStartedFromSource(mWorkSource, csph); 1938 } catch (RemoteException e) { 1939 logw("failed to report scan work: " + e.toString()); 1940 } 1941 } 1942 1943 private void reportBatchedScanStop() { 1944 if (mUid == 0) 1945 return; 1946 1947 try { 1948 mBatteryStats.noteWifiBatchedScanStoppedFromSource(mWorkSource); 1949 } catch (RemoteException e) { 1950 logw("failed to cleanup scan work: " + e.toString()); 1951 } 1952 } 1953 1954 // TODO migrate batterystats to accept scan duration per hour instead of csph 1955 private int getCsph() { 1956 int totalScanDurationPerHour = 0; 1957 Collection<ScanSettings> settingsList = 1958 mBackgroundScanStateMachine.getBackgroundScanSettings(this); 1959 for (ScanSettings settings : settingsList) { 1960 int scanDurationMs = mChannelHelper.estimateScanDuration(settings); 1961 int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) / 1962 settings.periodInMs; 1963 totalScanDurationPerHour += scanDurationMs * scans_per_Hour; 1964 } 1965 1966 return totalScanDurationPerHour / ChannelHelper.SCAN_PERIOD_PER_CHANNEL_MS; 1967 } 1968 1969 public void reportScanWorkUpdate() { 1970 if (mScanWorkReported) { 1971 reportBatchedScanStop(); 1972 mScanWorkReported = false; 1973 } 1974 if (mBackgroundScanStateMachine.getBackgroundScanSettings(this).isEmpty()) { 1975 reportBatchedScanStart(); 1976 mScanWorkReported = true; 1977 } 1978 } 1979 1980 @Override 1981 public String toString() { 1982 return "ClientInfo[uid=" + mUid + "," + mMessenger + "]"; 1983 } 1984 } 1985 1986 /** 1987 * This class is used to represent external clients to the WifiScanning Service. 1988 */ 1989 private class ExternalClientInfo extends ClientInfo { 1990 private final AsyncChannel mChannel; 1991 /** 1992 * Indicates if the client is still connected 1993 * If the client is no longer connected then messages to it will be silently dropped 1994 */ 1995 private boolean mDisconnected = false; 1996 1997 ExternalClientInfo(int uid, Messenger messenger, AsyncChannel c) { 1998 super(uid, messenger); 1999 mChannel = c; 2000 if (DBG) localLog("New client, channel: " + c); 2001 } 2002 2003 @Override 2004 public void reportEvent(int what, int arg1, int arg2, Object obj) { 2005 if (!mDisconnected) { 2006 mChannel.sendMessage(what, arg1, arg2, obj); 2007 } 2008 } 2009 2010 @Override 2011 public void cleanup() { 2012 mDisconnected = true; 2013 // Internal clients should not have any wifi change requests. So, keeping this cleanup 2014 // only for external client because this will otherwise cause an infinite recursion 2015 // when the internal client in WifiChangeStateMachine is cleaned up. 2016 mWifiChangeStateMachine.removeWifiChangeHandler(this); 2017 mPnoScanStateMachine.removePnoSettings(this); 2018 super.cleanup(); 2019 } 2020 } 2021 2022 /** 2023 * This class is used to represent internal clients to the WifiScanning Service. This is needed 2024 * for communicating between State Machines. 2025 * This leaves the onReportEvent method unimplemented, so that the clients have the freedom 2026 * to handle the events as they need. 2027 */ 2028 private class InternalClientInfo extends ClientInfo { 2029 private static final int INTERNAL_CLIENT_HANDLER = 0; 2030 2031 /** 2032 * The UID here is used to proxy the original external requester UID. 2033 */ 2034 InternalClientInfo(int requesterUid, Messenger messenger) { 2035 super(requesterUid, messenger); 2036 } 2037 2038 @Override 2039 public void reportEvent(int what, int arg1, int arg2, Object obj) { 2040 Message message = Message.obtain(); 2041 message.what = what; 2042 message.arg1 = arg1; 2043 message.arg2 = arg2; 2044 message.obj = obj; 2045 try { 2046 mMessenger.send(message); 2047 } catch (RemoteException e) { 2048 loge("Failed to send message: " + what); 2049 } 2050 } 2051 2052 /** 2053 * Send a message to the client handler which should reroute the message to the appropriate 2054 * state machine. 2055 */ 2056 public void sendRequestToClientHandler(int what, ScanSettings settings, 2057 WorkSource workSource) { 2058 Message msg = Message.obtain(); 2059 msg.what = what; 2060 msg.arg2 = INTERNAL_CLIENT_HANDLER; 2061 if (settings != null) { 2062 Bundle bundle = new Bundle(); 2063 bundle.putParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); 2064 bundle.putParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY, workSource); 2065 msg.obj = bundle; 2066 } 2067 msg.replyTo = mMessenger; 2068 msg.sendingUid = getUid(); 2069 mClientHandler.sendMessage(msg); 2070 } 2071 2072 /** 2073 * Send a message to the client handler which should reroute the message to the appropriate 2074 * state machine. 2075 */ 2076 public void sendRequestToClientHandler(int what) { 2077 sendRequestToClientHandler(what, null, null); 2078 } 2079 2080 @Override 2081 public String toString() { 2082 return "InternalClientInfo[]"; 2083 } 2084 } 2085 2086 void replySucceeded(Message msg) { 2087 if (msg.replyTo != null) { 2088 Message reply = Message.obtain(); 2089 reply.what = WifiScanner.CMD_OP_SUCCEEDED; 2090 reply.arg2 = msg.arg2; 2091 try { 2092 msg.replyTo.send(reply); 2093 } catch (RemoteException e) { 2094 // There's not much we can do if reply can't be sent! 2095 } 2096 } else { 2097 // locally generated message; doesn't need a reply! 2098 } 2099 } 2100 2101 void replyFailed(Message msg, int reason, String description) { 2102 if (msg.replyTo != null) { 2103 Message reply = Message.obtain(); 2104 reply.what = WifiScanner.CMD_OP_FAILED; 2105 reply.arg2 = msg.arg2; 2106 reply.obj = new WifiScanner.OperationResult(reason, description); 2107 try { 2108 msg.replyTo.send(reply); 2109 } catch (RemoteException e) { 2110 // There's not much we can do if reply can't be sent! 2111 } 2112 } else { 2113 // locally generated message; doesn't need a reply! 2114 } 2115 } 2116 2117 /** 2118 * Wifi Change state machine is used to handle any wifi change tracking requests. 2119 * TODO: This state machine doesn't handle driver loading/unloading yet. 2120 */ 2121 class WifiChangeStateMachine extends StateMachine 2122 implements WifiNative.SignificantWifiChangeEventHandler { 2123 2124 private static final int MAX_APS_TO_TRACK = 3; 2125 private static final int MOVING_SCAN_PERIOD_MS = 10000; 2126 private static final int STATIONARY_SCAN_PERIOD_MS = 5000; 2127 private static final int MOVING_STATE_TIMEOUT_MS = 30000; 2128 2129 State mDefaultState = new DefaultState(); 2130 State mStationaryState = new StationaryState(); 2131 State mMovingState = new MovingState(); 2132 2133 private static final String ACTION_TIMEOUT = 2134 "com.android.server.WifiScanningServiceImpl.action.TIMEOUT"; 2135 private PendingIntent mTimeoutIntent; 2136 private ScanResult[] mCurrentBssids; 2137 private InternalClientInfo mInternalClientInfo; 2138 2139 private final Set<Pair<ClientInfo, Integer>> mActiveWifiChangeHandlers = new HashSet<>(); 2140 2141 WifiChangeStateMachine(Looper looper) { 2142 super("SignificantChangeStateMachine", looper); 2143 2144 // CHECKSTYLE:OFF IndentationCheck 2145 addState(mDefaultState); 2146 addState(mStationaryState, mDefaultState); 2147 addState(mMovingState, mDefaultState); 2148 // CHECKSTYLE:ON IndentationCheck 2149 2150 setInitialState(mDefaultState); 2151 } 2152 2153 public void removeWifiChangeHandler(ClientInfo ci) { 2154 Iterator<Pair<ClientInfo, Integer>> iter = mActiveWifiChangeHandlers.iterator(); 2155 while (iter.hasNext()) { 2156 Pair<ClientInfo, Integer> entry = iter.next(); 2157 if (entry.first == ci) { 2158 iter.remove(); 2159 } 2160 } 2161 untrackSignificantWifiChangeOnEmpty(); 2162 } 2163 2164 class DefaultState extends State { 2165 @Override 2166 public void enter() { 2167 if (DBG) localLog("Entering IdleState"); 2168 } 2169 2170 @Override 2171 public boolean processMessage(Message msg) { 2172 if (DBG) localLog("DefaultState state got " + msg); 2173 ClientInfo ci = mClients.get(msg.replyTo); 2174 switch (msg.what) { 2175 case WifiScanner.CMD_START_TRACKING_CHANGE: 2176 if (addWifiChangeHandler(ci, msg.arg2)) { 2177 replySucceeded(msg); 2178 transitionTo(mMovingState); 2179 } else { 2180 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 2181 } 2182 break; 2183 case WifiScanner.CMD_STOP_TRACKING_CHANGE: 2184 // nothing to do 2185 break; 2186 case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: 2187 /* save configuration till we transition to moving state */ 2188 deferMessage(msg); 2189 break; 2190 case WifiScanner.CMD_SCAN_RESULT: 2191 // nothing to do 2192 break; 2193 default: 2194 return NOT_HANDLED; 2195 } 2196 return HANDLED; 2197 } 2198 } 2199 2200 class StationaryState extends State { 2201 @Override 2202 public void enter() { 2203 if (DBG) localLog("Entering StationaryState"); 2204 reportWifiStabilized(mCurrentBssids); 2205 } 2206 2207 @Override 2208 public boolean processMessage(Message msg) { 2209 if (DBG) localLog("Stationary state got " + msg); 2210 ClientInfo ci = mClients.get(msg.replyTo); 2211 switch (msg.what) { 2212 case WifiScanner.CMD_START_TRACKING_CHANGE: 2213 if (addWifiChangeHandler(ci, msg.arg2)) { 2214 replySucceeded(msg); 2215 } else { 2216 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 2217 } 2218 break; 2219 case WifiScanner.CMD_STOP_TRACKING_CHANGE: 2220 removeWifiChangeHandler(ci, msg.arg2); 2221 break; 2222 case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: 2223 /* save configuration till we transition to moving state */ 2224 deferMessage(msg); 2225 break; 2226 case CMD_WIFI_CHANGE_DETECTED: 2227 if (DBG) localLog("Got wifi change detected"); 2228 reportWifiChanged((ScanResult[]) msg.obj); 2229 transitionTo(mMovingState); 2230 break; 2231 case WifiScanner.CMD_SCAN_RESULT: 2232 // nothing to do 2233 break; 2234 default: 2235 return NOT_HANDLED; 2236 } 2237 return HANDLED; 2238 } 2239 } 2240 2241 class MovingState extends State { 2242 boolean mWifiChangeDetected = false; 2243 boolean mScanResultsPending = false; 2244 2245 @Override 2246 public void enter() { 2247 if (DBG) localLog("Entering MovingState"); 2248 if (mTimeoutIntent == null) { 2249 Intent intent = new Intent(ACTION_TIMEOUT, null); 2250 mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 2251 2252 mContext.registerReceiver( 2253 new BroadcastReceiver() { 2254 @Override 2255 public void onReceive(Context context, Intent intent) { 2256 sendMessage(CMD_WIFI_CHANGE_TIMEOUT); 2257 } 2258 }, new IntentFilter(ACTION_TIMEOUT)); 2259 } 2260 issueFullScan(); 2261 } 2262 2263 @Override 2264 public boolean processMessage(Message msg) { 2265 if (DBG) localLog("MovingState state got " + msg); 2266 ClientInfo ci = mClients.get(msg.replyTo); 2267 switch (msg.what) { 2268 case WifiScanner.CMD_START_TRACKING_CHANGE: 2269 if (addWifiChangeHandler(ci, msg.arg2)) { 2270 replySucceeded(msg); 2271 } else { 2272 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 2273 } 2274 break; 2275 case WifiScanner.CMD_STOP_TRACKING_CHANGE: 2276 removeWifiChangeHandler(ci, msg.arg2); 2277 break; 2278 case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: 2279 if (DBG) localLog("Got configuration from app"); 2280 WifiScanner.WifiChangeSettings settings = 2281 (WifiScanner.WifiChangeSettings) msg.obj; 2282 reconfigureScan(settings); 2283 mWifiChangeDetected = false; 2284 long unchangedDelay = settings.unchangedSampleSize * settings.periodInMs; 2285 mAlarmManager.cancel(mTimeoutIntent); 2286 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2287 mClock.getElapsedSinceBootMillis() + unchangedDelay, 2288 mTimeoutIntent); 2289 break; 2290 case WifiScanner.CMD_SCAN_RESULT: 2291 if (DBG) localLog("Got scan results"); 2292 if (mScanResultsPending) { 2293 if (DBG) localLog("reconfiguring scan"); 2294 reconfigureScan((ScanData[])msg.obj, 2295 STATIONARY_SCAN_PERIOD_MS); 2296 mWifiChangeDetected = false; 2297 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2298 mClock.getElapsedSinceBootMillis() + MOVING_STATE_TIMEOUT_MS, 2299 mTimeoutIntent); 2300 mScanResultsPending = false; 2301 } 2302 break; 2303 case CMD_WIFI_CHANGE_DETECTED: 2304 if (DBG) localLog("Change detected"); 2305 mAlarmManager.cancel(mTimeoutIntent); 2306 reportWifiChanged((ScanResult[])msg.obj); 2307 mWifiChangeDetected = true; 2308 issueFullScan(); 2309 break; 2310 case CMD_WIFI_CHANGE_TIMEOUT: 2311 if (DBG) localLog("Got timeout event"); 2312 if (mWifiChangeDetected == false) { 2313 transitionTo(mStationaryState); 2314 } 2315 break; 2316 default: 2317 return NOT_HANDLED; 2318 } 2319 return HANDLED; 2320 } 2321 2322 @Override 2323 public void exit() { 2324 mAlarmManager.cancel(mTimeoutIntent); 2325 } 2326 2327 void issueFullScan() { 2328 if (DBG) localLog("Issuing full scan"); 2329 ScanSettings settings = new ScanSettings(); 2330 settings.band = WifiScanner.WIFI_BAND_BOTH; 2331 settings.periodInMs = MOVING_SCAN_PERIOD_MS; 2332 settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 2333 addScanRequest(settings); 2334 mScanResultsPending = true; 2335 } 2336 2337 } 2338 2339 private void reconfigureScan(ScanData[] results, int period) { 2340 // find brightest APs and set them as sentinels 2341 if (results.length < MAX_APS_TO_TRACK) { 2342 localLog("too few APs (" + results.length + ") available to track wifi change"); 2343 return; 2344 } 2345 2346 removeScanRequest(); 2347 2348 // remove duplicate BSSIDs 2349 HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>(); 2350 for (ScanResult result : results[0].getResults()) { 2351 ScanResult saved = bssidToScanResult.get(result.BSSID); 2352 if (saved == null) { 2353 bssidToScanResult.put(result.BSSID, result); 2354 } else if (saved.level > result.level) { 2355 bssidToScanResult.put(result.BSSID, result); 2356 } 2357 } 2358 2359 // find brightest BSSIDs 2360 ScanResult brightest[] = new ScanResult[MAX_APS_TO_TRACK]; 2361 Collection<ScanResult> results2 = bssidToScanResult.values(); 2362 for (ScanResult result : results2) { 2363 for (int j = 0; j < brightest.length; j++) { 2364 if (brightest[j] == null 2365 || (brightest[j].level < result.level)) { 2366 for (int k = brightest.length; k > (j + 1); k--) { 2367 brightest[k - 1] = brightest[k - 2]; 2368 } 2369 brightest[j] = result; 2370 break; 2371 } 2372 } 2373 } 2374 2375 // Get channels to scan for 2376 ArrayList<Integer> channels = new ArrayList<Integer>(); 2377 for (int i = 0; i < brightest.length; i++) { 2378 boolean found = false; 2379 for (int j = i + 1; j < brightest.length; j++) { 2380 if (brightest[j].frequency == brightest[i].frequency) { 2381 found = true; 2382 } 2383 } 2384 if (!found) { 2385 channels.add(brightest[i].frequency); 2386 } 2387 } 2388 2389 if (DBG) localLog("Found " + channels.size() + " channels"); 2390 2391 // set scanning schedule 2392 ScanSettings settings = new ScanSettings(); 2393 settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED; 2394 settings.channels = new ChannelSpec[channels.size()]; 2395 for (int i = 0; i < channels.size(); i++) { 2396 settings.channels[i] = new ChannelSpec(channels.get(i)); 2397 } 2398 2399 settings.periodInMs = period; 2400 addScanRequest(settings); 2401 2402 WifiScanner.WifiChangeSettings settings2 = new WifiScanner.WifiChangeSettings(); 2403 settings2.rssiSampleSize = 3; 2404 settings2.lostApSampleSize = 3; 2405 settings2.unchangedSampleSize = 3; 2406 settings2.minApsBreachingThreshold = 2; 2407 settings2.bssidInfos = new BssidInfo[brightest.length]; 2408 2409 for (int i = 0; i < brightest.length; i++) { 2410 BssidInfo BssidInfo = new BssidInfo(); 2411 BssidInfo.bssid = brightest[i].BSSID; 2412 int threshold = (100 + brightest[i].level) / 32 + 2; 2413 BssidInfo.low = brightest[i].level - threshold; 2414 BssidInfo.high = brightest[i].level + threshold; 2415 settings2.bssidInfos[i] = BssidInfo; 2416 2417 if (DBG) localLog("Setting bssid=" + BssidInfo.bssid + ", " + 2418 "low=" + BssidInfo.low + ", high=" + BssidInfo.high); 2419 } 2420 2421 trackSignificantWifiChange(settings2); 2422 mCurrentBssids = brightest; 2423 } 2424 2425 private void reconfigureScan(WifiScanner.WifiChangeSettings settings) { 2426 2427 if (settings.bssidInfos.length < MAX_APS_TO_TRACK) { 2428 localLog("too few APs (" + settings.bssidInfos.length 2429 + ") available to track wifi change"); 2430 return; 2431 } 2432 2433 if (DBG) localLog("Setting configuration specified by app"); 2434 2435 mCurrentBssids = new ScanResult[settings.bssidInfos.length]; 2436 HashSet<Integer> channels = new HashSet<Integer>(); 2437 2438 for (int i = 0; i < settings.bssidInfos.length; i++) { 2439 ScanResult result = new ScanResult(); 2440 result.BSSID = settings.bssidInfos[i].bssid; 2441 mCurrentBssids[i] = result; 2442 channels.add(settings.bssidInfos[i].frequencyHint); 2443 } 2444 2445 // cancel previous scan 2446 removeScanRequest(); 2447 2448 // set new scanning schedule 2449 ScanSettings settings2 = new ScanSettings(); 2450 settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED; 2451 settings2.channels = new ChannelSpec[channels.size()]; 2452 int i = 0; 2453 for (Integer channel : channels) { 2454 settings2.channels[i++] = new ChannelSpec(channel); 2455 } 2456 2457 settings2.periodInMs = settings.periodInMs; 2458 addScanRequest(settings2); 2459 2460 // start tracking new APs 2461 trackSignificantWifiChange(settings); 2462 } 2463 2464 2465 @Override 2466 public void onChangesFound(ScanResult results[]) { 2467 sendMessage(CMD_WIFI_CHANGE_DETECTED, 0, 0, results); 2468 } 2469 2470 private void addScanRequest(ScanSettings settings) { 2471 if (DBG) localLog("Starting scans"); 2472 if (mInternalClientInfo != null) { 2473 mInternalClientInfo.sendRequestToClientHandler( 2474 WifiScanner.CMD_START_BACKGROUND_SCAN, settings, null); 2475 } 2476 } 2477 2478 private void removeScanRequest() { 2479 if (DBG) localLog("Stopping scans"); 2480 if (mInternalClientInfo != null) { 2481 mInternalClientInfo.sendRequestToClientHandler( 2482 WifiScanner.CMD_STOP_BACKGROUND_SCAN); 2483 } 2484 } 2485 2486 private void trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings) { 2487 if (mScannerImpl != null) { 2488 mScannerImpl.untrackSignificantWifiChange(); 2489 mScannerImpl.trackSignificantWifiChange(settings, this); 2490 } 2491 } 2492 2493 private void untrackSignificantWifiChange() { 2494 if (mScannerImpl != null) { 2495 mScannerImpl.untrackSignificantWifiChange(); 2496 } 2497 } 2498 2499 private boolean addWifiChangeHandler(ClientInfo ci, int handler) { 2500 if (ci == null) { 2501 Log.d(TAG, "Failing wifi change request ClientInfo not found " + handler); 2502 return false; 2503 } 2504 mActiveWifiChangeHandlers.add(Pair.create(ci, handler)); 2505 // Add an internal client to make background scan requests. 2506 if (mInternalClientInfo == null) { 2507 mInternalClientInfo = 2508 new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler())); 2509 mInternalClientInfo.register(); 2510 } 2511 return true; 2512 } 2513 2514 private void removeWifiChangeHandler(ClientInfo ci, int handler) { 2515 if (ci != null) { 2516 mActiveWifiChangeHandlers.remove(Pair.create(ci, handler)); 2517 untrackSignificantWifiChangeOnEmpty(); 2518 } 2519 } 2520 2521 private void untrackSignificantWifiChangeOnEmpty() { 2522 if (mActiveWifiChangeHandlers.isEmpty()) { 2523 if (DBG) localLog("Got Disable Wifi Change"); 2524 mCurrentBssids = null; 2525 untrackSignificantWifiChange(); 2526 // Remove the internal client when there are no more external clients. 2527 if (mInternalClientInfo != null) { 2528 mInternalClientInfo.cleanup(); 2529 mInternalClientInfo = null; 2530 } 2531 transitionTo(mDefaultState); 2532 } 2533 } 2534 2535 private void reportWifiChanged(ScanResult[] results) { 2536 WifiScanner.ParcelableScanResults parcelableScanResults = 2537 new WifiScanner.ParcelableScanResults(results); 2538 Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator(); 2539 while (it.hasNext()) { 2540 Pair<ClientInfo, Integer> entry = it.next(); 2541 ClientInfo ci = entry.first; 2542 int handler = entry.second; 2543 ci.reportEvent(WifiScanner.CMD_WIFI_CHANGE_DETECTED, 0, handler, 2544 parcelableScanResults); 2545 } 2546 } 2547 2548 private void reportWifiStabilized(ScanResult[] results) { 2549 WifiScanner.ParcelableScanResults parcelableScanResults = 2550 new WifiScanner.ParcelableScanResults(results); 2551 Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator(); 2552 while (it.hasNext()) { 2553 Pair<ClientInfo, Integer> entry = it.next(); 2554 ClientInfo ci = entry.first; 2555 int handler = entry.second; 2556 ci.reportEvent(WifiScanner.CMD_WIFI_CHANGES_STABILIZED, 0, handler, 2557 parcelableScanResults); 2558 } 2559 } 2560 } 2561 2562 private static String toString(int uid, ScanSettings settings) { 2563 StringBuilder sb = new StringBuilder(); 2564 sb.append("ScanSettings[uid=").append(uid); 2565 sb.append(", period=").append(settings.periodInMs); 2566 sb.append(", report=").append(settings.reportEvents); 2567 if (settings.reportEvents == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL 2568 && settings.numBssidsPerScan > 0 2569 && settings.maxScansToCache > 1) { 2570 sb.append(", batch=").append(settings.maxScansToCache); 2571 sb.append(", numAP=").append(settings.numBssidsPerScan); 2572 } 2573 sb.append(", ").append(ChannelHelper.toString(settings)); 2574 sb.append("]"); 2575 2576 return sb.toString(); 2577 } 2578 2579 @Override 2580 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2581 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 2582 != PackageManager.PERMISSION_GRANTED) { 2583 pw.println("Permission Denial: can't dump WifiScanner from from pid=" 2584 + Binder.getCallingPid() 2585 + ", uid=" + Binder.getCallingUid() 2586 + " without permission " 2587 + android.Manifest.permission.DUMP); 2588 return; 2589 } 2590 pw.println("WifiScanningService - Log Begin ----"); 2591 mLocalLog.dump(fd, pw, args); 2592 pw.println("WifiScanningService - Log End ----"); 2593 pw.println(); 2594 pw.println("clients:"); 2595 for (ClientInfo client : mClients.values()) { 2596 pw.println(" " + client); 2597 } 2598 pw.println("listeners:"); 2599 for (ClientInfo client : mClients.values()) { 2600 Collection<ScanSettings> settingsList = 2601 mBackgroundScanStateMachine.getBackgroundScanSettings(client); 2602 for (ScanSettings settings : settingsList) { 2603 pw.println(" " + toString(client.mUid, settings)); 2604 } 2605 } 2606 if (mBackgroundScheduler != null) { 2607 WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule(); 2608 if (schedule != null) { 2609 pw.println("schedule:"); 2610 pw.println(" base period: " + schedule.base_period_ms); 2611 pw.println(" max ap per scan: " + schedule.max_ap_per_scan); 2612 pw.println(" batched scans: " + schedule.report_threshold_num_scans); 2613 pw.println(" buckets:"); 2614 for (int b = 0; b < schedule.num_buckets; b++) { 2615 WifiNative.BucketSettings bucket = schedule.buckets[b]; 2616 pw.println(" bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)[" 2617 + bucket.report_events + "]: " 2618 + ChannelHelper.toString(bucket)); 2619 } 2620 } 2621 } 2622 if (mPnoScanStateMachine != null) { 2623 mPnoScanStateMachine.dump(fd, pw, args); 2624 } 2625 } 2626 2627 void logScanRequest(String request, ClientInfo ci, int id, WorkSource workSource, 2628 ScanSettings settings, PnoSettings pnoSettings) { 2629 StringBuilder sb = new StringBuilder(); 2630 sb.append(request) 2631 .append(": ") 2632 .append((ci == null) ? "ClientInfo[unknown]" : ci.toString()) 2633 .append(",Id=") 2634 .append(id); 2635 if (workSource != null) { 2636 sb.append(",").append(workSource); 2637 } 2638 if (settings != null) { 2639 sb.append(", "); 2640 describeTo(sb, settings); 2641 } 2642 if (pnoSettings != null) { 2643 sb.append(", "); 2644 describeTo(sb, pnoSettings); 2645 } 2646 localLog(sb.toString()); 2647 } 2648 2649 void logCallback(String callback, ClientInfo ci, int id, String extra) { 2650 StringBuilder sb = new StringBuilder(); 2651 sb.append(callback) 2652 .append(": ") 2653 .append((ci == null) ? "ClientInfo[unknown]" : ci.toString()) 2654 .append(",Id=") 2655 .append(id); 2656 if (extra != null) { 2657 sb.append(",").append(extra); 2658 } 2659 localLog(sb.toString()); 2660 } 2661 2662 static String describeForLog(ScanData[] results) { 2663 StringBuilder sb = new StringBuilder(); 2664 sb.append("results="); 2665 for (int i = 0; i < results.length; ++i) { 2666 if (i > 0) sb.append(";"); 2667 sb.append(results[i].getResults().length); 2668 } 2669 return sb.toString(); 2670 } 2671 2672 static String describeForLog(ScanResult[] results) { 2673 return "results=" + results.length; 2674 } 2675 2676 static String describeTo(StringBuilder sb, ScanSettings scanSettings) { 2677 sb.append("ScanSettings { ") 2678 .append(" band:").append(scanSettings.band) 2679 .append(" period:").append(scanSettings.periodInMs) 2680 .append(" reportEvents:").append(scanSettings.reportEvents) 2681 .append(" numBssidsPerScan:").append(scanSettings.numBssidsPerScan) 2682 .append(" maxScansToCache:").append(scanSettings.maxScansToCache) 2683 .append(" channels:[ "); 2684 if (scanSettings.channels != null) { 2685 for (int i = 0; i < scanSettings.channels.length; i++) { 2686 sb.append(scanSettings.channels[i].frequency) 2687 .append(" "); 2688 } 2689 } 2690 sb.append(" ] ") 2691 .append(" } "); 2692 return sb.toString(); 2693 } 2694 2695 static String describeTo(StringBuilder sb, PnoSettings pnoSettings) { 2696 sb.append("PnoSettings { ") 2697 .append(" min5GhzRssi:").append(pnoSettings.min5GHzRssi) 2698 .append(" min24GhzRssi:").append(pnoSettings.min24GHzRssi) 2699 .append(" initialScoreMax:").append(pnoSettings.initialScoreMax) 2700 .append(" currentConnectionBonus:").append(pnoSettings.currentConnectionBonus) 2701 .append(" sameNetworkBonus:").append(pnoSettings.sameNetworkBonus) 2702 .append(" secureBonus:").append(pnoSettings.secureBonus) 2703 .append(" band5GhzBonus:").append(pnoSettings.band5GHzBonus) 2704 .append(" isConnected:").append(pnoSettings.isConnected) 2705 .append(" networks:[ "); 2706 if (pnoSettings.networkList != null) { 2707 for (int i = 0; i < pnoSettings.networkList.length; i++) { 2708 sb.append(pnoSettings.networkList[i].ssid) 2709 .append(",") 2710 .append(pnoSettings.networkList[i].networkId) 2711 .append(" "); 2712 } 2713 } 2714 sb.append(" ] ") 2715 .append(" } "); 2716 return sb.toString(); 2717 } 2718} 2719