WifiScanningServiceImpl.java revision 1ba04405f98489f0fbd66b6566c64324be11111a
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 Set<Integer> activeHiddenNetworkIds = new HashSet<>(); 705 for (int id : mActiveScanSettings.hiddenNetworkIds) { 706 activeHiddenNetworkIds.add(id); 707 } 708 for (int id : settings.hiddenNetworkIds) { 709 if (!activeHiddenNetworkIds.contains(id)) { 710 return false; 711 } 712 } 713 } 714 715 return true; 716 } 717 718 void removeSingleScanRequest(ClientInfo ci, int handler) { 719 if (ci != null) { 720 logScanRequest("removeSingleScanRequest", ci, handler, null, null, null); 721 mPendingScans.removeRequest(ci, handler); 722 mActiveScans.removeRequest(ci, handler); 723 } 724 } 725 726 void removeSingleScanRequests(ClientInfo ci) { 727 if (ci != null) { 728 logScanRequest("removeSingleScanRequests", ci, -1, null, null, null); 729 mPendingScans.removeAllForClient(ci); 730 mActiveScans.removeAllForClient(ci); 731 } 732 } 733 734 void tryToStartNewScan() { 735 if (mPendingScans.size() == 0) { // no pending requests 736 return; 737 } 738 mChannelHelper.updateChannels(); 739 // TODO move merging logic to a scheduler 740 WifiNative.ScanSettings settings = new WifiNative.ScanSettings(); 741 settings.num_buckets = 1; 742 WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); 743 bucketSettings.bucket = 0; 744 bucketSettings.period_ms = 0; 745 bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 746 747 ChannelCollection channels = mChannelHelper.createChannelCollection(); 748 HashSet<Integer> hiddenNetworkIdSet = new HashSet<>(); 749 for (RequestInfo<ScanSettings> entry : mPendingScans) { 750 channels.addChannels(entry.settings); 751 if (entry.settings.hiddenNetworkIds != null) { 752 for (int i = 0; i < entry.settings.hiddenNetworkIds.length; i++) { 753 hiddenNetworkIdSet.add(entry.settings.hiddenNetworkIds[i]); 754 } 755 } 756 if ((entry.settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) 757 != 0) { 758 bucketSettings.report_events |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; 759 } 760 } 761 if (hiddenNetworkIdSet.size() > 0) { 762 settings.hiddenNetworkIds = new int[hiddenNetworkIdSet.size()]; 763 int numHiddenNetworks = 0; 764 for (Integer hiddenNetworkId : hiddenNetworkIdSet) { 765 settings.hiddenNetworkIds[numHiddenNetworks++] = hiddenNetworkId; 766 } 767 } 768 769 channels.fillBucketSettings(bucketSettings, Integer.MAX_VALUE); 770 771 settings.buckets = new WifiNative.BucketSettings[] {bucketSettings}; 772 if (mScannerImpl.startSingleScan(settings, this)) { 773 // store the active scan settings 774 mActiveScanSettings = settings; 775 // swap pending and active scan requests 776 RequestList<ScanSettings> tmp = mActiveScans; 777 mActiveScans = mPendingScans; 778 mPendingScans = tmp; 779 // make sure that the pending list is clear 780 mPendingScans.clear(); 781 transitionTo(mScanningState); 782 } else { 783 mWifiMetrics.incrementScanReturnEntry( 784 WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mPendingScans.size()); 785 // notify and cancel failed scans 786 sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED, 787 "Failed to start single scan"); 788 } 789 } 790 791 void sendOpFailedToAllAndClear(RequestList<?> clientHandlers, int reason, 792 String description) { 793 for (RequestInfo<?> entry : clientHandlers) { 794 logCallback("singleScanFailed", entry.clientInfo, entry.handlerId, 795 "reason=" + reason + ", " + description); 796 entry.reportEvent(WifiScanner.CMD_OP_FAILED, 0, 797 new WifiScanner.OperationResult(reason, description)); 798 } 799 clientHandlers.clear(); 800 } 801 802 void reportFullScanResult(ScanResult result, int bucketsScanned) { 803 for (RequestInfo<ScanSettings> entry : mActiveScans) { 804 if (ScanScheduleUtil.shouldReportFullScanResultForSettings(mChannelHelper, 805 result, bucketsScanned, entry.settings, -1)) { 806 entry.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, result); 807 } 808 } 809 810 for (RequestInfo<Void> entry : mSingleScanListeners) { 811 entry.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, result); 812 } 813 } 814 815 void reportScanResults(ScanData results) { 816 if (results != null && results.getResults() != null) { 817 if (results.getResults().length > 0) { 818 mWifiMetrics.incrementNonEmptyScanResultCount(); 819 } else { 820 mWifiMetrics.incrementEmptyScanResultCount(); 821 } 822 } 823 ScanData[] allResults = new ScanData[] {results}; 824 for (RequestInfo<ScanSettings> entry : mActiveScans) { 825 ScanData[] resultsToDeliver = ScanScheduleUtil.filterResultsForSettings( 826 mChannelHelper, allResults, entry.settings, -1); 827 WifiScanner.ParcelableScanData parcelableResultsToDeliver = 828 new WifiScanner.ParcelableScanData(resultsToDeliver); 829 logCallback("singleScanResults", entry.clientInfo, entry.handlerId, 830 describeForLog(resultsToDeliver)); 831 entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableResultsToDeliver); 832 // make sure the handler is removed 833 entry.reportEvent(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, null); 834 } 835 836 WifiScanner.ParcelableScanData parcelableAllResults = 837 new WifiScanner.ParcelableScanData(allResults); 838 for (RequestInfo<Void> entry : mSingleScanListeners) { 839 logCallback("singleScanResults", entry.clientInfo, entry.handlerId, 840 describeForLog(allResults)); 841 entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults); 842 } 843 } 844 } 845 846 class WifiBackgroundScanStateMachine extends StateMachine 847 implements WifiNative.ScanEventHandler, WifiNative.HotlistEventHandler { 848 849 private final DefaultState mDefaultState = new DefaultState(); 850 private final StartedState mStartedState = new StartedState(); 851 private final PausedState mPausedState = new PausedState(); 852 853 private final RequestList<ScanSettings> mActiveBackgroundScans = new RequestList<>(); 854 private final RequestList<WifiScanner.HotlistSettings> mActiveHotlistSettings = 855 new RequestList<>(); 856 857 WifiBackgroundScanStateMachine(Looper looper) { 858 super("WifiBackgroundScanStateMachine", looper); 859 860 setLogRecSize(512); 861 setLogOnlyTransitions(false); 862 863 // CHECKSTYLE:OFF IndentationCheck 864 addState(mDefaultState); 865 addState(mStartedState, mDefaultState); 866 addState(mPausedState, mDefaultState); 867 // CHECKSTYLE:ON IndentationCheck 868 869 setInitialState(mDefaultState); 870 } 871 872 public Collection<ScanSettings> getBackgroundScanSettings(ClientInfo ci) { 873 return mActiveBackgroundScans.getAllSettingsForClient(ci); 874 } 875 876 public void removeBackgroundScanSettings(ClientInfo ci) { 877 mActiveBackgroundScans.removeAllForClient(ci); 878 updateSchedule(); 879 } 880 881 public void removeHotlistSettings(ClientInfo ci) { 882 mActiveHotlistSettings.removeAllForClient(ci); 883 resetHotlist(); 884 } 885 886 @Override 887 public void onScanStatus(int event) { 888 if (DBG) localLog("onScanStatus event received, event=" + event); 889 switch(event) { 890 case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE: 891 case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS: 892 case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT: 893 sendMessage(CMD_SCAN_RESULTS_AVAILABLE); 894 break; 895 case WifiNative.WIFI_SCAN_FAILED: 896 sendMessage(CMD_SCAN_FAILED); 897 break; 898 default: 899 Log.e(TAG, "Unknown scan status event: " + event); 900 break; 901 } 902 } 903 904 @Override 905 public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) { 906 if (DBG) localLog("onFullScanResult received"); 907 sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult); 908 } 909 910 @Override 911 public void onScanPaused(ScanData scanData[]) { 912 if (DBG) localLog("onScanPaused received"); 913 sendMessage(CMD_SCAN_PAUSED, scanData); 914 } 915 916 @Override 917 public void onScanRestarted() { 918 if (DBG) localLog("onScanRestarted received"); 919 sendMessage(CMD_SCAN_RESTARTED); 920 } 921 922 @Override 923 public void onHotlistApFound(ScanResult[] results) { 924 if (DBG) localLog("onHotlistApFound event received"); 925 sendMessage(CMD_HOTLIST_AP_FOUND, 0, 0, results); 926 } 927 928 @Override 929 public void onHotlistApLost(ScanResult[] results) { 930 if (DBG) localLog("onHotlistApLost event received"); 931 sendMessage(CMD_HOTLIST_AP_LOST, 0, 0, results); 932 } 933 934 class DefaultState extends State { 935 @Override 936 public void enter() { 937 if (DBG) localLog("DefaultState"); 938 mActiveBackgroundScans.clear(); 939 mActiveHotlistSettings.clear(); 940 } 941 942 @Override 943 public boolean processMessage(Message msg) { 944 switch (msg.what) { 945 case CMD_DRIVER_LOADED: 946 // TODO this should be moved to a common location since it is used outside 947 // of this state machine. It is ok right now because the driver loaded event 948 // is sent to this state machine first. 949 if (mScannerImpl == null) { 950 mScannerImpl = mScannerImplFactory.create(mContext, mLooper, mClock); 951 mChannelHelper = mScannerImpl.getChannelHelper(); 952 } 953 954 mBackgroundScheduler = new BackgroundScanScheduler(mChannelHelper); 955 956 WifiNative.ScanCapabilities capabilities = 957 new WifiNative.ScanCapabilities(); 958 if (!mScannerImpl.getScanCapabilities(capabilities)) { 959 loge("could not get scan capabilities"); 960 return HANDLED; 961 } 962 mBackgroundScheduler.setMaxBuckets(capabilities.max_scan_buckets); 963 mBackgroundScheduler.setMaxApPerScan(capabilities.max_ap_cache_per_scan); 964 965 Log.i(TAG, "wifi driver loaded with scan capabilities: " 966 + "max buckets=" + capabilities.max_scan_buckets); 967 968 transitionTo(mStartedState); 969 return HANDLED; 970 case CMD_DRIVER_UNLOADED: 971 Log.i(TAG, "wifi driver unloaded"); 972 transitionTo(mDefaultState); 973 break; 974 case WifiScanner.CMD_START_BACKGROUND_SCAN: 975 case WifiScanner.CMD_STOP_BACKGROUND_SCAN: 976 case WifiScanner.CMD_START_SINGLE_SCAN: 977 case WifiScanner.CMD_STOP_SINGLE_SCAN: 978 case WifiScanner.CMD_SET_HOTLIST: 979 case WifiScanner.CMD_RESET_HOTLIST: 980 case WifiScanner.CMD_GET_SCAN_RESULTS: 981 replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available"); 982 break; 983 984 case CMD_SCAN_RESULTS_AVAILABLE: 985 if (DBG) localLog("ignored scan results available event"); 986 break; 987 988 case CMD_FULL_SCAN_RESULTS: 989 if (DBG) localLog("ignored full scan result event"); 990 break; 991 992 default: 993 break; 994 } 995 996 return HANDLED; 997 } 998 } 999 1000 class StartedState extends State { 1001 1002 @Override 1003 public void enter() { 1004 if (DBG) localLog("StartedState"); 1005 } 1006 1007 @Override 1008 public void exit() { 1009 sendBackgroundScanFailedToAllAndClear( 1010 WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted"); 1011 sendHotlistFailedToAllAndClear( 1012 WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted"); 1013 mScannerImpl.cleanup(); 1014 } 1015 1016 @Override 1017 public boolean processMessage(Message msg) { 1018 ClientInfo ci = mClients.get(msg.replyTo); 1019 1020 switch (msg.what) { 1021 case CMD_DRIVER_LOADED: 1022 return NOT_HANDLED; 1023 case CMD_DRIVER_UNLOADED: 1024 return NOT_HANDLED; 1025 case WifiScanner.CMD_START_BACKGROUND_SCAN: { 1026 mWifiMetrics.incrementBackgroundScanCount(); 1027 Bundle scanParams = (Bundle) msg.obj; 1028 if (scanParams == null) { 1029 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null"); 1030 return HANDLED; 1031 } 1032 scanParams.setDefusable(true); 1033 ScanSettings scanSettings = 1034 scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY); 1035 WorkSource workSource = 1036 scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY); 1037 if (addBackgroundScanRequest(ci, msg.arg2, scanSettings, workSource)) { 1038 replySucceeded(msg); 1039 } else { 1040 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 1041 } 1042 break; 1043 } 1044 case WifiScanner.CMD_STOP_BACKGROUND_SCAN: 1045 removeBackgroundScanRequest(ci, msg.arg2); 1046 break; 1047 case WifiScanner.CMD_GET_SCAN_RESULTS: 1048 reportScanResults(mScannerImpl.getLatestBatchedScanResults(true)); 1049 replySucceeded(msg); 1050 break; 1051 case WifiScanner.CMD_SET_HOTLIST: 1052 addHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj); 1053 replySucceeded(msg); 1054 break; 1055 case WifiScanner.CMD_RESET_HOTLIST: 1056 removeHotlist(ci, msg.arg2); 1057 break; 1058 case CMD_SCAN_RESULTS_AVAILABLE: 1059 reportScanResults(mScannerImpl.getLatestBatchedScanResults(true)); 1060 break; 1061 case CMD_FULL_SCAN_RESULTS: 1062 reportFullScanResult((ScanResult) msg.obj, /* bucketsScanned */ msg.arg2); 1063 break; 1064 case CMD_HOTLIST_AP_FOUND: 1065 reportHotlistResults(WifiScanner.CMD_AP_FOUND, (ScanResult[]) msg.obj); 1066 break; 1067 case CMD_HOTLIST_AP_LOST: 1068 reportHotlistResults(WifiScanner.CMD_AP_LOST, (ScanResult[]) msg.obj); 1069 break; 1070 case CMD_SCAN_PAUSED: 1071 reportScanResults((ScanData[]) msg.obj); 1072 transitionTo(mPausedState); 1073 break; 1074 case CMD_SCAN_FAILED: 1075 Log.e(TAG, "WifiScanner background scan gave CMD_SCAN_FAILED"); 1076 sendBackgroundScanFailedToAllAndClear( 1077 WifiScanner.REASON_UNSPECIFIED, "Background Scan failed"); 1078 break; 1079 default: 1080 return NOT_HANDLED; 1081 } 1082 1083 return HANDLED; 1084 } 1085 } 1086 1087 class PausedState extends State { 1088 @Override 1089 public void enter() { 1090 if (DBG) localLog("PausedState"); 1091 } 1092 1093 @Override 1094 public boolean processMessage(Message msg) { 1095 switch (msg.what) { 1096 case CMD_SCAN_RESTARTED: 1097 transitionTo(mStartedState); 1098 break; 1099 default: 1100 deferMessage(msg); 1101 break; 1102 } 1103 return HANDLED; 1104 } 1105 } 1106 1107 private boolean addBackgroundScanRequest(ClientInfo ci, int handler, 1108 ScanSettings settings, WorkSource workSource) { 1109 // sanity check the input 1110 if (ci == null) { 1111 Log.d(TAG, "Failing scan request ClientInfo not found " + handler); 1112 return false; 1113 } 1114 if (settings.periodInMs < WifiScanner.MIN_SCAN_PERIOD_MS) { 1115 loge("Failing scan request because periodInMs is " + settings.periodInMs 1116 + ", min scan period is: " + WifiScanner.MIN_SCAN_PERIOD_MS); 1117 return false; 1118 } 1119 1120 if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED && settings.channels == null) { 1121 loge("Channels was null with unspecified band"); 1122 return false; 1123 } 1124 1125 if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED 1126 && settings.channels.length == 0) { 1127 loge("No channels specified"); 1128 return false; 1129 } 1130 1131 int minSupportedPeriodMs = mChannelHelper.estimateScanDuration(settings); 1132 if (settings.periodInMs < minSupportedPeriodMs) { 1133 loge("Failing scan request because minSupportedPeriodMs is " 1134 + minSupportedPeriodMs + " but the request wants " + settings.periodInMs); 1135 return false; 1136 } 1137 1138 // check truncated binary exponential back off scan settings 1139 if (settings.maxPeriodInMs != 0 && settings.maxPeriodInMs != settings.periodInMs) { 1140 if (settings.maxPeriodInMs < settings.periodInMs) { 1141 loge("Failing scan request because maxPeriodInMs is " + settings.maxPeriodInMs 1142 + " but less than periodInMs " + settings.periodInMs); 1143 return false; 1144 } 1145 if (settings.maxPeriodInMs > WifiScanner.MAX_SCAN_PERIOD_MS) { 1146 loge("Failing scan request because maxSupportedPeriodMs is " 1147 + WifiScanner.MAX_SCAN_PERIOD_MS + " but the request wants " 1148 + settings.maxPeriodInMs); 1149 return false; 1150 } 1151 if (settings.stepCount < 1) { 1152 loge("Failing scan request because stepCount is " + settings.stepCount 1153 + " which is less than 1"); 1154 return false; 1155 } 1156 } 1157 1158 logScanRequest("addBackgroundScanRequest", ci, handler, null, settings, null); 1159 mActiveBackgroundScans.addRequest(ci, handler, workSource, settings); 1160 1161 if (updateSchedule()) { 1162 return true; 1163 } else { 1164 mActiveBackgroundScans.removeRequest(ci, handler); 1165 localLog("Failing scan request because failed to reset scan"); 1166 return false; 1167 } 1168 } 1169 1170 private boolean updateSchedule() { 1171 if (mChannelHelper == null || mBackgroundScheduler == null || mScannerImpl == null) { 1172 loge("Failed to update schedule because WifiScanningService is not initialized"); 1173 return false; 1174 } 1175 mChannelHelper.updateChannels(); 1176 Collection<ScanSettings> settings = mActiveBackgroundScans.getAllSettings(); 1177 1178 mBackgroundScheduler.updateSchedule(settings); 1179 WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule(); 1180 1181 if (ScanScheduleUtil.scheduleEquals(mPreviousSchedule, schedule)) { 1182 if (DBG) Log.d(TAG, "schedule updated with no change"); 1183 return true; 1184 } 1185 1186 mPreviousSchedule = schedule; 1187 1188 if (schedule.num_buckets == 0) { 1189 mScannerImpl.stopBatchedScan(); 1190 if (DBG) Log.d(TAG, "scan stopped"); 1191 return true; 1192 } else { 1193 localLog("starting scan: " 1194 + "base period=" + schedule.base_period_ms 1195 + ", max ap per scan=" + schedule.max_ap_per_scan 1196 + ", batched scans=" + schedule.report_threshold_num_scans); 1197 for (int b = 0; b < schedule.num_buckets; b++) { 1198 WifiNative.BucketSettings bucket = schedule.buckets[b]; 1199 localLog("bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)" 1200 + "[" + bucket.report_events + "]: " 1201 + ChannelHelper.toString(bucket)); 1202 } 1203 1204 if (mScannerImpl.startBatchedScan(schedule, this)) { 1205 if (DBG) { 1206 Log.d(TAG, "scan restarted with " + schedule.num_buckets 1207 + " bucket(s) and base period: " + schedule.base_period_ms); 1208 } 1209 return true; 1210 } else { 1211 mPreviousSchedule = null; 1212 loge("error starting scan: " 1213 + "base period=" + schedule.base_period_ms 1214 + ", max ap per scan=" + schedule.max_ap_per_scan 1215 + ", batched scans=" + schedule.report_threshold_num_scans); 1216 for (int b = 0; b < schedule.num_buckets; b++) { 1217 WifiNative.BucketSettings bucket = schedule.buckets[b]; 1218 loge("bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)" 1219 + "[" + bucket.report_events + "]: " 1220 + ChannelHelper.toString(bucket)); 1221 } 1222 return false; 1223 } 1224 } 1225 } 1226 1227 private void removeBackgroundScanRequest(ClientInfo ci, int handler) { 1228 if (ci != null) { 1229 ScanSettings settings = mActiveBackgroundScans.removeRequest(ci, handler); 1230 logScanRequest("removeBackgroundScanRequest", ci, handler, null, settings, null); 1231 updateSchedule(); 1232 } 1233 } 1234 1235 private void reportFullScanResult(ScanResult result, int bucketsScanned) { 1236 for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) { 1237 ClientInfo ci = entry.clientInfo; 1238 int handler = entry.handlerId; 1239 ScanSettings settings = entry.settings; 1240 if (mBackgroundScheduler.shouldReportFullScanResultForSettings( 1241 result, bucketsScanned, settings)) { 1242 ScanResult newResult = new ScanResult(result); 1243 if (result.informationElements != null) { 1244 newResult.informationElements = result.informationElements.clone(); 1245 } 1246 else { 1247 newResult.informationElements = null; 1248 } 1249 ci.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, newResult); 1250 } 1251 } 1252 } 1253 1254 private void reportScanResults(ScanData[] results) { 1255 for (ScanData result : results) { 1256 if (result != null && result.getResults() != null) { 1257 if (result.getResults().length > 0) { 1258 mWifiMetrics.incrementNonEmptyScanResultCount(); 1259 } else { 1260 mWifiMetrics.incrementEmptyScanResultCount(); 1261 } 1262 } 1263 } 1264 for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) { 1265 ClientInfo ci = entry.clientInfo; 1266 int handler = entry.handlerId; 1267 ScanSettings settings = entry.settings; 1268 ScanData[] resultsToDeliver = 1269 mBackgroundScheduler.filterResultsForSettings(results, settings); 1270 if (resultsToDeliver != null) { 1271 logCallback("backgroundScanResults", ci, handler, 1272 describeForLog(resultsToDeliver)); 1273 WifiScanner.ParcelableScanData parcelableScanData = 1274 new WifiScanner.ParcelableScanData(resultsToDeliver); 1275 ci.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData); 1276 } 1277 } 1278 } 1279 1280 private void sendBackgroundScanFailedToAllAndClear(int reason, String description) { 1281 for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) { 1282 ClientInfo ci = entry.clientInfo; 1283 int handler = entry.handlerId; 1284 ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler, 1285 new WifiScanner.OperationResult(reason, description)); 1286 } 1287 mActiveBackgroundScans.clear(); 1288 } 1289 1290 private void addHotlist(ClientInfo ci, int handler, WifiScanner.HotlistSettings settings) { 1291 mActiveHotlistSettings.addRequest(ci, handler, null, settings); 1292 resetHotlist(); 1293 } 1294 1295 private void removeHotlist(ClientInfo ci, int handler) { 1296 mActiveHotlistSettings.removeRequest(ci, handler); 1297 resetHotlist(); 1298 } 1299 1300 private void resetHotlist() { 1301 if (mScannerImpl == null) { 1302 loge("Failed to update hotlist because WifiScanningService is not initialized"); 1303 return; 1304 } 1305 1306 Collection<WifiScanner.HotlistSettings> settings = 1307 mActiveHotlistSettings.getAllSettings(); 1308 int num_hotlist_ap = 0; 1309 1310 for (WifiScanner.HotlistSettings s : settings) { 1311 num_hotlist_ap += s.bssidInfos.length; 1312 } 1313 1314 if (num_hotlist_ap == 0) { 1315 mScannerImpl.resetHotlist(); 1316 } else { 1317 BssidInfo[] bssidInfos = new BssidInfo[num_hotlist_ap]; 1318 int apLostThreshold = Integer.MAX_VALUE; 1319 int index = 0; 1320 for (WifiScanner.HotlistSettings s : settings) { 1321 for (int i = 0; i < s.bssidInfos.length; i++, index++) { 1322 bssidInfos[index] = s.bssidInfos[i]; 1323 } 1324 if (s.apLostThreshold < apLostThreshold) { 1325 apLostThreshold = s.apLostThreshold; 1326 } 1327 } 1328 1329 WifiScanner.HotlistSettings mergedSettings = new WifiScanner.HotlistSettings(); 1330 mergedSettings.bssidInfos = bssidInfos; 1331 mergedSettings.apLostThreshold = apLostThreshold; 1332 mScannerImpl.setHotlist(mergedSettings, this); 1333 } 1334 } 1335 1336 private void reportHotlistResults(int what, ScanResult[] results) { 1337 if (DBG) localLog("reportHotlistResults " + what + " results " + results.length); 1338 for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) { 1339 ClientInfo ci = entry.clientInfo; 1340 int handler = entry.handlerId; 1341 WifiScanner.HotlistSettings settings = entry.settings; 1342 int num_results = 0; 1343 for (ScanResult result : results) { 1344 for (BssidInfo BssidInfo : settings.bssidInfos) { 1345 if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) { 1346 num_results++; 1347 break; 1348 } 1349 } 1350 } 1351 if (num_results == 0) { 1352 // nothing to report 1353 return; 1354 } 1355 ScanResult[] results2 = new ScanResult[num_results]; 1356 int index = 0; 1357 for (ScanResult result : results) { 1358 for (BssidInfo BssidInfo : settings.bssidInfos) { 1359 if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) { 1360 results2[index] = result; 1361 index++; 1362 } 1363 } 1364 } 1365 WifiScanner.ParcelableScanResults parcelableScanResults = 1366 new WifiScanner.ParcelableScanResults(results2); 1367 1368 ci.reportEvent(what, 0, handler, parcelableScanResults); 1369 } 1370 } 1371 1372 private void sendHotlistFailedToAllAndClear(int reason, String description) { 1373 for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) { 1374 ClientInfo ci = entry.clientInfo; 1375 int handler = entry.handlerId; 1376 ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler, 1377 new WifiScanner.OperationResult(reason, description)); 1378 } 1379 mActiveHotlistSettings.clear(); 1380 } 1381 } 1382 1383 /** 1384 * PNO scan state machine has 5 states: 1385 * -Default State 1386 * -Started State 1387 * -Hw Pno Scan state 1388 * -Single Scan state 1389 * -Sw Pno Scan state 1390 * 1391 * These are the main state transitions: 1392 * 1. Start at |Default State| 1393 * 2. Move to |Started State| when we get the |WIFI_SCAN_AVAILABLE| broadcast from WifiManager. 1394 * 3. When a new PNO scan request comes in: 1395 * a.1. Switch to |Hw Pno Scan state| when the device supports HW PNO 1396 * (This could either be HAL based ePNO or supplicant based PNO). 1397 * a.2. In |Hw Pno Scan state| when PNO scan results are received, check if the result 1398 * contains IE (information elements). If yes, send the results to the client, else 1399 * switch to |Single Scan state| and send the result to the client when the scan result 1400 * is obtained. 1401 * b.1. Switch to |Sw Pno Scan state| when the device does not supports HW PNO 1402 * (This is for older devices which do not support HW PNO and for connected PNO on 1403 * devices which support supplicant based PNO) 1404 * b.2. In |Sw Pno Scan state| send the result to the client when the background scan result 1405 * is obtained 1406 * 1407 * Note: PNO scans only work for a single client today. We don't have support in HW to support 1408 * multiple requests at the same time, so will need non-trivial changes to support (if at all 1409 * possible) in WifiScanningService. 1410 */ 1411 class WifiPnoScanStateMachine extends StateMachine implements WifiNative.PnoEventHandler { 1412 1413 private final DefaultState mDefaultState = new DefaultState(); 1414 private final StartedState mStartedState = new StartedState(); 1415 private final HwPnoScanState mHwPnoScanState = new HwPnoScanState(); 1416 private final SwPnoScanState mSwPnoScanState = new SwPnoScanState(); 1417 private final SingleScanState mSingleScanState = new SingleScanState(); 1418 private InternalClientInfo mInternalClientInfo; 1419 1420 private final RequestList<Pair<PnoSettings, ScanSettings>> mActivePnoScans = 1421 new RequestList<>(); 1422 1423 WifiPnoScanStateMachine(Looper looper) { 1424 super("WifiPnoScanStateMachine", looper); 1425 1426 setLogRecSize(512); 1427 setLogOnlyTransitions(false); 1428 1429 // CHECKSTYLE:OFF IndentationCheck 1430 addState(mDefaultState); 1431 addState(mStartedState, mDefaultState); 1432 addState(mHwPnoScanState, mStartedState); 1433 addState(mSingleScanState, mHwPnoScanState); 1434 addState(mSwPnoScanState, mStartedState); 1435 // CHECKSTYLE:ON IndentationCheck 1436 1437 setInitialState(mDefaultState); 1438 } 1439 1440 public void removePnoSettings(ClientInfo ci) { 1441 mActivePnoScans.removeAllForClient(ci); 1442 transitionTo(mStartedState); 1443 } 1444 1445 @Override 1446 public void onPnoNetworkFound(ScanResult[] results) { 1447 if (DBG) localLog("onWifiPnoNetworkFound event received"); 1448 sendMessage(CMD_PNO_NETWORK_FOUND, 0, 0, results); 1449 } 1450 1451 @Override 1452 public void onPnoScanFailed() { 1453 if (DBG) localLog("onWifiPnoScanFailed event received"); 1454 sendMessage(CMD_PNO_SCAN_FAILED, 0, 0, null); 1455 } 1456 1457 class DefaultState extends State { 1458 @Override 1459 public void enter() { 1460 if (DBG) localLog("DefaultState"); 1461 } 1462 1463 @Override 1464 public boolean processMessage(Message msg) { 1465 switch (msg.what) { 1466 case CMD_DRIVER_LOADED: 1467 transitionTo(mStartedState); 1468 break; 1469 case CMD_DRIVER_UNLOADED: 1470 transitionTo(mDefaultState); 1471 break; 1472 case WifiScanner.CMD_START_PNO_SCAN: 1473 case WifiScanner.CMD_STOP_PNO_SCAN: 1474 replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available"); 1475 break; 1476 case CMD_PNO_NETWORK_FOUND: 1477 case CMD_PNO_SCAN_FAILED: 1478 case WifiScanner.CMD_SCAN_RESULT: 1479 case WifiScanner.CMD_OP_FAILED: 1480 loge("Unexpected message " + msg.what); 1481 break; 1482 default: 1483 return NOT_HANDLED; 1484 } 1485 return HANDLED; 1486 } 1487 } 1488 1489 class StartedState extends State { 1490 @Override 1491 public void enter() { 1492 if (DBG) localLog("StartedState"); 1493 } 1494 1495 @Override 1496 public void exit() { 1497 sendPnoScanFailedToAllAndClear( 1498 WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted"); 1499 } 1500 1501 @Override 1502 public boolean processMessage(Message msg) { 1503 ClientInfo ci = mClients.get(msg.replyTo); 1504 switch (msg.what) { 1505 case WifiScanner.CMD_START_PNO_SCAN: 1506 Bundle pnoParams = (Bundle) msg.obj; 1507 if (pnoParams == null) { 1508 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null"); 1509 return HANDLED; 1510 } 1511 pnoParams.setDefusable(true); 1512 PnoSettings pnoSettings = 1513 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY); 1514 // This message is handled after the transition to SwPnoScan/HwPnoScan state 1515 deferMessage(msg); 1516 if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) { 1517 transitionTo(mHwPnoScanState); 1518 } else { 1519 transitionTo(mSwPnoScanState); 1520 } 1521 break; 1522 case WifiScanner.CMD_STOP_PNO_SCAN: 1523 replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "no scan running"); 1524 break; 1525 default: 1526 return NOT_HANDLED; 1527 } 1528 return HANDLED; 1529 } 1530 } 1531 1532 class HwPnoScanState extends State { 1533 @Override 1534 public void enter() { 1535 if (DBG) localLog("HwPnoScanState"); 1536 } 1537 1538 @Override 1539 public void exit() { 1540 // Reset PNO scan in ScannerImpl before we exit. 1541 mScannerImpl.resetHwPnoList(); 1542 removeInternalClient(); 1543 } 1544 1545 @Override 1546 public boolean processMessage(Message msg) { 1547 ClientInfo ci = mClients.get(msg.replyTo); 1548 switch (msg.what) { 1549 case WifiScanner.CMD_START_PNO_SCAN: 1550 Bundle pnoParams = (Bundle) msg.obj; 1551 if (pnoParams == null) { 1552 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null"); 1553 return HANDLED; 1554 } 1555 pnoParams.setDefusable(true); 1556 PnoSettings pnoSettings = 1557 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY); 1558 ScanSettings scanSettings = 1559 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY); 1560 if (addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) { 1561 replySucceeded(msg); 1562 } else { 1563 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 1564 transitionTo(mStartedState); 1565 } 1566 break; 1567 case WifiScanner.CMD_STOP_PNO_SCAN: 1568 removeHwPnoScanRequest(ci, msg.arg2); 1569 transitionTo(mStartedState); 1570 break; 1571 case CMD_PNO_NETWORK_FOUND: 1572 ScanResult[] scanResults = ((ScanResult[]) msg.obj); 1573 if (isSingleScanNeeded(scanResults)) { 1574 ScanSettings activeScanSettings = getScanSettings(); 1575 if (activeScanSettings == null) { 1576 sendPnoScanFailedToAllAndClear( 1577 WifiScanner.REASON_UNSPECIFIED, 1578 "couldn't retrieve setting"); 1579 transitionTo(mStartedState); 1580 } else { 1581 addSingleScanRequest(activeScanSettings); 1582 transitionTo(mSingleScanState); 1583 } 1584 } else { 1585 reportPnoNetworkFound((ScanResult[]) msg.obj); 1586 } 1587 break; 1588 case CMD_PNO_SCAN_FAILED: 1589 sendPnoScanFailedToAllAndClear( 1590 WifiScanner.REASON_UNSPECIFIED, "pno scan failed"); 1591 transitionTo(mStartedState); 1592 break; 1593 default: 1594 return NOT_HANDLED; 1595 } 1596 return HANDLED; 1597 } 1598 } 1599 1600 class SingleScanState extends State { 1601 @Override 1602 public void enter() { 1603 if (DBG) localLog("SingleScanState"); 1604 } 1605 1606 @Override 1607 public boolean processMessage(Message msg) { 1608 ClientInfo ci = mClients.get(msg.replyTo); 1609 switch (msg.what) { 1610 case WifiScanner.CMD_SCAN_RESULT: 1611 WifiScanner.ParcelableScanData parcelableScanData = 1612 (WifiScanner.ParcelableScanData) msg.obj; 1613 ScanData[] scanDatas = parcelableScanData.getResults(); 1614 ScanData lastScanData = scanDatas[scanDatas.length - 1]; 1615 reportPnoNetworkFound(lastScanData.getResults()); 1616 transitionTo(mHwPnoScanState); 1617 break; 1618 case WifiScanner.CMD_OP_FAILED: 1619 sendPnoScanFailedToAllAndClear( 1620 WifiScanner.REASON_UNSPECIFIED, "single scan failed"); 1621 transitionTo(mStartedState); 1622 break; 1623 default: 1624 return NOT_HANDLED; 1625 } 1626 return HANDLED; 1627 } 1628 } 1629 1630 class SwPnoScanState extends State { 1631 private final ArrayList<ScanResult> mSwPnoFullScanResults = new ArrayList<>(); 1632 1633 @Override 1634 public void enter() { 1635 if (DBG) localLog("SwPnoScanState"); 1636 mSwPnoFullScanResults.clear(); 1637 } 1638 1639 @Override 1640 public void exit() { 1641 removeInternalClient(); 1642 } 1643 1644 @Override 1645 public boolean processMessage(Message msg) { 1646 ClientInfo ci = mClients.get(msg.replyTo); 1647 switch (msg.what) { 1648 case WifiScanner.CMD_START_PNO_SCAN: 1649 Bundle pnoParams = (Bundle) msg.obj; 1650 if (pnoParams == null) { 1651 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null"); 1652 return HANDLED; 1653 } 1654 pnoParams.setDefusable(true); 1655 PnoSettings pnoSettings = 1656 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY); 1657 ScanSettings scanSettings = 1658 pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY); 1659 if (addSwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) { 1660 replySucceeded(msg); 1661 } else { 1662 replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); 1663 transitionTo(mStartedState); 1664 } 1665 break; 1666 case WifiScanner.CMD_STOP_PNO_SCAN: 1667 removeSwPnoScanRequest(ci, msg.arg2); 1668 transitionTo(mStartedState); 1669 break; 1670 case WifiScanner.CMD_FULL_SCAN_RESULT: 1671 // Aggregate full scan results until we get the |CMD_SCAN_RESULT| message 1672 mSwPnoFullScanResults.add((ScanResult) msg.obj); 1673 break; 1674 case WifiScanner.CMD_SCAN_RESULT: 1675 ScanResult[] scanResults = mSwPnoFullScanResults.toArray( 1676 new ScanResult[mSwPnoFullScanResults.size()]); 1677 reportPnoNetworkFound(scanResults); 1678 mSwPnoFullScanResults.clear(); 1679 break; 1680 case WifiScanner.CMD_OP_FAILED: 1681 sendPnoScanFailedToAllAndClear( 1682 WifiScanner.REASON_UNSPECIFIED, "background scan failed"); 1683 transitionTo(mStartedState); 1684 break; 1685 default: 1686 return NOT_HANDLED; 1687 } 1688 return HANDLED; 1689 } 1690 } 1691 1692 private WifiNative.PnoSettings convertPnoSettingsToNative(PnoSettings pnoSettings) { 1693 WifiNative.PnoSettings nativePnoSetting = new WifiNative.PnoSettings(); 1694 nativePnoSetting.min5GHzRssi = pnoSettings.min5GHzRssi; 1695 nativePnoSetting.min24GHzRssi = pnoSettings.min24GHzRssi; 1696 nativePnoSetting.initialScoreMax = pnoSettings.initialScoreMax; 1697 nativePnoSetting.currentConnectionBonus = pnoSettings.currentConnectionBonus; 1698 nativePnoSetting.sameNetworkBonus = pnoSettings.sameNetworkBonus; 1699 nativePnoSetting.secureBonus = pnoSettings.secureBonus; 1700 nativePnoSetting.band5GHzBonus = pnoSettings.band5GHzBonus; 1701 nativePnoSetting.isConnected = pnoSettings.isConnected; 1702 nativePnoSetting.networkList = 1703 new WifiNative.PnoNetwork[pnoSettings.networkList.length]; 1704 for (int i = 0; i < pnoSettings.networkList.length; i++) { 1705 nativePnoSetting.networkList[i] = new WifiNative.PnoNetwork(); 1706 nativePnoSetting.networkList[i].ssid = pnoSettings.networkList[i].ssid; 1707 nativePnoSetting.networkList[i].networkId = pnoSettings.networkList[i].networkId; 1708 nativePnoSetting.networkList[i].priority = pnoSettings.networkList[i].priority; 1709 nativePnoSetting.networkList[i].flags = pnoSettings.networkList[i].flags; 1710 nativePnoSetting.networkList[i].auth_bit_field = 1711 pnoSettings.networkList[i].authBitField; 1712 } 1713 return nativePnoSetting; 1714 } 1715 1716 // Retrieve the only active scan settings. 1717 private ScanSettings getScanSettings() { 1718 for (Pair<PnoSettings, ScanSettings> settingsPair : mActivePnoScans.getAllSettings()) { 1719 return settingsPair.second; 1720 } 1721 return null; 1722 } 1723 1724 private void removeInternalClient() { 1725 if (mInternalClientInfo != null) { 1726 mInternalClientInfo.cleanup(); 1727 mInternalClientInfo = null; 1728 } else { 1729 Log.w(TAG, "No Internal client for PNO"); 1730 } 1731 } 1732 1733 private void addInternalClient(ClientInfo ci) { 1734 if (mInternalClientInfo == null) { 1735 mInternalClientInfo = 1736 new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler())); 1737 mInternalClientInfo.register(); 1738 } else { 1739 Log.w(TAG, "Internal client for PNO already exists"); 1740 } 1741 } 1742 1743 private void addPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings, 1744 PnoSettings pnoSettings) { 1745 mActivePnoScans.addRequest(ci, handler, WifiStateMachine.WIFI_WORK_SOURCE, 1746 Pair.create(pnoSettings, scanSettings)); 1747 addInternalClient(ci); 1748 } 1749 1750 private Pair<PnoSettings, ScanSettings> removePnoScanRequest(ClientInfo ci, int handler) { 1751 Pair<PnoSettings, ScanSettings> settings = mActivePnoScans.removeRequest(ci, handler); 1752 return settings; 1753 } 1754 1755 private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings, 1756 PnoSettings pnoSettings) { 1757 if (ci == null) { 1758 Log.d(TAG, "Failing scan request ClientInfo not found " + handler); 1759 return false; 1760 } 1761 if (!mActivePnoScans.isEmpty()) { 1762 loge("Failing scan request because there is already an active scan"); 1763 return false; 1764 } 1765 WifiNative.PnoSettings nativePnoSettings = convertPnoSettingsToNative(pnoSettings); 1766 if (!mScannerImpl.setHwPnoList(nativePnoSettings, mPnoScanStateMachine)) { 1767 return false; 1768 } 1769 logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings); 1770 addPnoScanRequest(ci, handler, scanSettings, pnoSettings); 1771 // HW PNO is supported, check if we need a background scan running for this. 1772 if (mScannerImpl.shouldScheduleBackgroundScanForHwPno()) { 1773 addBackgroundScanRequest(scanSettings); 1774 } 1775 return true; 1776 } 1777 1778 private void removeHwPnoScanRequest(ClientInfo ci, int handler) { 1779 if (ci != null) { 1780 Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler); 1781 logScanRequest("removeHwPnoScanRequest", ci, handler, null, 1782 settings.second, settings.first); 1783 } 1784 } 1785 1786 private boolean addSwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings, 1787 PnoSettings pnoSettings) { 1788 if (ci == null) { 1789 Log.d(TAG, "Failing scan request ClientInfo not found " + handler); 1790 return false; 1791 } 1792 if (!mActivePnoScans.isEmpty()) { 1793 loge("Failing scan request because there is already an active scan"); 1794 return false; 1795 } 1796 logScanRequest("addSwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings); 1797 addPnoScanRequest(ci, handler, scanSettings, pnoSettings); 1798 // HW PNO is not supported, we need to revert to normal background scans and 1799 // report events after each scan and we need full scan results to get the IE information 1800 scanSettings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN 1801 | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; 1802 addBackgroundScanRequest(scanSettings); 1803 return true; 1804 } 1805 1806 private void removeSwPnoScanRequest(ClientInfo ci, int handler) { 1807 if (ci != null) { 1808 Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler); 1809 logScanRequest("removeSwPnoScanRequest", ci, handler, null, 1810 settings.second, settings.first); 1811 } 1812 } 1813 1814 private void reportPnoNetworkFound(ScanResult[] results) { 1815 WifiScanner.ParcelableScanResults parcelableScanResults = 1816 new WifiScanner.ParcelableScanResults(results); 1817 for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) { 1818 ClientInfo ci = entry.clientInfo; 1819 int handler = entry.handlerId; 1820 logCallback("pnoNetworkFound", ci, handler, describeForLog(results)); 1821 ci.reportEvent( 1822 WifiScanner.CMD_PNO_NETWORK_FOUND, 0, handler, parcelableScanResults); 1823 } 1824 } 1825 1826 private void sendPnoScanFailedToAllAndClear(int reason, String description) { 1827 for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) { 1828 ClientInfo ci = entry.clientInfo; 1829 int handler = entry.handlerId; 1830 ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler, 1831 new WifiScanner.OperationResult(reason, description)); 1832 } 1833 mActivePnoScans.clear(); 1834 } 1835 1836 private void addBackgroundScanRequest(ScanSettings settings) { 1837 if (DBG) localLog("Starting background scan"); 1838 if (mInternalClientInfo != null) { 1839 mInternalClientInfo.sendRequestToClientHandler( 1840 WifiScanner.CMD_START_BACKGROUND_SCAN, settings, 1841 WifiStateMachine.WIFI_WORK_SOURCE); 1842 } 1843 } 1844 1845 private void addSingleScanRequest(ScanSettings settings) { 1846 if (DBG) localLog("Starting single scan"); 1847 if (mInternalClientInfo != null) { 1848 mInternalClientInfo.sendRequestToClientHandler( 1849 WifiScanner.CMD_START_SINGLE_SCAN, settings, 1850 WifiStateMachine.WIFI_WORK_SOURCE); 1851 } 1852 } 1853 1854 /** 1855 * Checks if IE are present in scan data, if no single scan is needed to report event to 1856 * client 1857 */ 1858 private boolean isSingleScanNeeded(ScanResult[] scanResults) { 1859 for (ScanResult scanResult : scanResults) { 1860 if (scanResult.informationElements != null 1861 && scanResult.informationElements.length > 0) { 1862 return false; 1863 } 1864 } 1865 return true; 1866 } 1867 } 1868 1869 private abstract class ClientInfo { 1870 private final int mUid; 1871 private final WorkSource mWorkSource; 1872 private boolean mScanWorkReported = false; 1873 protected final Messenger mMessenger; 1874 1875 ClientInfo(int uid, Messenger messenger) { 1876 mUid = uid; 1877 mMessenger = messenger; 1878 mWorkSource = new WorkSource(uid); 1879 } 1880 1881 /** 1882 * Register this client to main client map. 1883 */ 1884 public void register() { 1885 mClients.put(mMessenger, this); 1886 } 1887 1888 /** 1889 * Unregister this client from main client map. 1890 */ 1891 private void unregister() { 1892 mClients.remove(mMessenger); 1893 } 1894 1895 public void cleanup() { 1896 mSingleScanListeners.removeAllForClient(this); 1897 mSingleScanStateMachine.removeSingleScanRequests(this); 1898 mBackgroundScanStateMachine.removeBackgroundScanSettings(this); 1899 mBackgroundScanStateMachine.removeHotlistSettings(this); 1900 unregister(); 1901 localLog("Successfully stopped all requests for client " + this); 1902 } 1903 1904 public int getUid() { 1905 return mUid; 1906 } 1907 1908 public void reportEvent(int what, int arg1, int arg2) { 1909 reportEvent(what, arg1, arg2, null); 1910 } 1911 1912 // This has to be implemented by subclasses to report events back to clients. 1913 public abstract void reportEvent(int what, int arg1, int arg2, Object obj); 1914 1915 // TODO(b/27903217): Blame scan on provided work source 1916 private void reportBatchedScanStart() { 1917 if (mUid == 0) 1918 return; 1919 1920 int csph = getCsph(); 1921 1922 try { 1923 mBatteryStats.noteWifiBatchedScanStartedFromSource(mWorkSource, csph); 1924 } catch (RemoteException e) { 1925 logw("failed to report scan work: " + e.toString()); 1926 } 1927 } 1928 1929 private void reportBatchedScanStop() { 1930 if (mUid == 0) 1931 return; 1932 1933 try { 1934 mBatteryStats.noteWifiBatchedScanStoppedFromSource(mWorkSource); 1935 } catch (RemoteException e) { 1936 logw("failed to cleanup scan work: " + e.toString()); 1937 } 1938 } 1939 1940 // TODO migrate batterystats to accept scan duration per hour instead of csph 1941 private int getCsph() { 1942 int totalScanDurationPerHour = 0; 1943 Collection<ScanSettings> settingsList = 1944 mBackgroundScanStateMachine.getBackgroundScanSettings(this); 1945 for (ScanSettings settings : settingsList) { 1946 int scanDurationMs = mChannelHelper.estimateScanDuration(settings); 1947 int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) / 1948 settings.periodInMs; 1949 totalScanDurationPerHour += scanDurationMs * scans_per_Hour; 1950 } 1951 1952 return totalScanDurationPerHour / ChannelHelper.SCAN_PERIOD_PER_CHANNEL_MS; 1953 } 1954 1955 public void reportScanWorkUpdate() { 1956 if (mScanWorkReported) { 1957 reportBatchedScanStop(); 1958 mScanWorkReported = false; 1959 } 1960 if (mBackgroundScanStateMachine.getBackgroundScanSettings(this).isEmpty()) { 1961 reportBatchedScanStart(); 1962 mScanWorkReported = true; 1963 } 1964 } 1965 1966 @Override 1967 public String toString() { 1968 return "ClientInfo[uid=" + mUid + "," + mMessenger + "]"; 1969 } 1970 } 1971 1972 /** 1973 * This class is used to represent external clients to the WifiScanning Service. 1974 */ 1975 private class ExternalClientInfo extends ClientInfo { 1976 private final AsyncChannel mChannel; 1977 /** 1978 * Indicates if the client is still connected 1979 * If the client is no longer connected then messages to it will be silently dropped 1980 */ 1981 private boolean mDisconnected = false; 1982 1983 ExternalClientInfo(int uid, Messenger messenger, AsyncChannel c) { 1984 super(uid, messenger); 1985 mChannel = c; 1986 if (DBG) localLog("New client, channel: " + c); 1987 } 1988 1989 @Override 1990 public void reportEvent(int what, int arg1, int arg2, Object obj) { 1991 if (!mDisconnected) { 1992 mChannel.sendMessage(what, arg1, arg2, obj); 1993 } 1994 } 1995 1996 @Override 1997 public void cleanup() { 1998 mDisconnected = true; 1999 // Internal clients should not have any wifi change requests. So, keeping this cleanup 2000 // only for external client because this will otherwise cause an infinite recursion 2001 // when the internal client in WifiChangeStateMachine is cleaned up. 2002 mWifiChangeStateMachine.removeWifiChangeHandler(this); 2003 mPnoScanStateMachine.removePnoSettings(this); 2004 super.cleanup(); 2005 } 2006 } 2007 2008 /** 2009 * This class is used to represent internal clients to the WifiScanning Service. This is needed 2010 * for communicating between State Machines. 2011 * This leaves the onReportEvent method unimplemented, so that the clients have the freedom 2012 * to handle the events as they need. 2013 */ 2014 private class InternalClientInfo extends ClientInfo { 2015 private static final int INTERNAL_CLIENT_HANDLER = 0; 2016 2017 /** 2018 * The UID here is used to proxy the original external requester UID. 2019 */ 2020 InternalClientInfo(int requesterUid, Messenger messenger) { 2021 super(requesterUid, messenger); 2022 } 2023 2024 @Override 2025 public void reportEvent(int what, int arg1, int arg2, Object obj) { 2026 Message message = Message.obtain(); 2027 message.what = what; 2028 message.arg1 = arg1; 2029 message.arg2 = arg2; 2030 message.obj = obj; 2031 try { 2032 mMessenger.send(message); 2033 } catch (RemoteException e) { 2034 loge("Failed to send message: " + what); 2035 } 2036 } 2037 2038 /** 2039 * Send a message to the client handler which should reroute the message to the appropriate 2040 * state machine. 2041 */ 2042 public void sendRequestToClientHandler(int what, ScanSettings settings, 2043 WorkSource workSource) { 2044 Message msg = Message.obtain(); 2045 msg.what = what; 2046 msg.arg2 = INTERNAL_CLIENT_HANDLER; 2047 if (settings != null) { 2048 Bundle bundle = new Bundle(); 2049 bundle.putParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); 2050 bundle.putParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY, workSource); 2051 msg.obj = bundle; 2052 } 2053 msg.replyTo = mMessenger; 2054 msg.sendingUid = getUid(); 2055 mClientHandler.sendMessage(msg); 2056 } 2057 2058 /** 2059 * Send a message to the client handler which should reroute the message to the appropriate 2060 * state machine. 2061 */ 2062 public void sendRequestToClientHandler(int what) { 2063 sendRequestToClientHandler(what, null, null); 2064 } 2065 2066 @Override 2067 public String toString() { 2068 return "InternalClientInfo[]"; 2069 } 2070 } 2071 2072 void replySucceeded(Message msg) { 2073 if (msg.replyTo != null) { 2074 Message reply = Message.obtain(); 2075 reply.what = WifiScanner.CMD_OP_SUCCEEDED; 2076 reply.arg2 = msg.arg2; 2077 try { 2078 msg.replyTo.send(reply); 2079 } catch (RemoteException e) { 2080 // There's not much we can do if reply can't be sent! 2081 } 2082 } else { 2083 // locally generated message; doesn't need a reply! 2084 } 2085 } 2086 2087 void replyFailed(Message msg, int reason, String description) { 2088 if (msg.replyTo != null) { 2089 Message reply = Message.obtain(); 2090 reply.what = WifiScanner.CMD_OP_FAILED; 2091 reply.arg2 = msg.arg2; 2092 reply.obj = new WifiScanner.OperationResult(reason, description); 2093 try { 2094 msg.replyTo.send(reply); 2095 } catch (RemoteException e) { 2096 // There's not much we can do if reply can't be sent! 2097 } 2098 } else { 2099 // locally generated message; doesn't need a reply! 2100 } 2101 } 2102 2103 /** 2104 * Wifi Change state machine is used to handle any wifi change tracking requests. 2105 * TODO: This state machine doesn't handle driver loading/unloading yet. 2106 */ 2107 class WifiChangeStateMachine extends StateMachine 2108 implements WifiNative.SignificantWifiChangeEventHandler { 2109 2110 private static final int MAX_APS_TO_TRACK = 3; 2111 private static final int MOVING_SCAN_PERIOD_MS = 10000; 2112 private static final int STATIONARY_SCAN_PERIOD_MS = 5000; 2113 private static final int MOVING_STATE_TIMEOUT_MS = 30000; 2114 2115 State mDefaultState = new DefaultState(); 2116 State mStationaryState = new StationaryState(); 2117 State mMovingState = new MovingState(); 2118 2119 private static final String ACTION_TIMEOUT = 2120 "com.android.server.WifiScanningServiceImpl.action.TIMEOUT"; 2121 private PendingIntent mTimeoutIntent; 2122 private ScanResult[] mCurrentBssids; 2123 private InternalClientInfo mInternalClientInfo; 2124 2125 private final Set<Pair<ClientInfo, Integer>> mActiveWifiChangeHandlers = new HashSet<>(); 2126 2127 WifiChangeStateMachine(Looper looper) { 2128 super("SignificantChangeStateMachine", looper); 2129 2130 // CHECKSTYLE:OFF IndentationCheck 2131 addState(mDefaultState); 2132 addState(mStationaryState, mDefaultState); 2133 addState(mMovingState, mDefaultState); 2134 // CHECKSTYLE:ON IndentationCheck 2135 2136 setInitialState(mDefaultState); 2137 } 2138 2139 public void removeWifiChangeHandler(ClientInfo ci) { 2140 Iterator<Pair<ClientInfo, Integer>> iter = mActiveWifiChangeHandlers.iterator(); 2141 while (iter.hasNext()) { 2142 Pair<ClientInfo, Integer> entry = iter.next(); 2143 if (entry.first == ci) { 2144 iter.remove(); 2145 } 2146 } 2147 untrackSignificantWifiChangeOnEmpty(); 2148 } 2149 2150 class DefaultState extends State { 2151 @Override 2152 public void enter() { 2153 if (DBG) localLog("Entering IdleState"); 2154 } 2155 2156 @Override 2157 public boolean processMessage(Message msg) { 2158 if (DBG) localLog("DefaultState state got " + msg); 2159 ClientInfo ci = mClients.get(msg.replyTo); 2160 switch (msg.what) { 2161 case WifiScanner.CMD_START_TRACKING_CHANGE: 2162 addWifiChangeHandler(ci, msg.arg2); 2163 replySucceeded(msg); 2164 transitionTo(mMovingState); 2165 break; 2166 case WifiScanner.CMD_STOP_TRACKING_CHANGE: 2167 // nothing to do 2168 break; 2169 case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: 2170 /* save configuration till we transition to moving state */ 2171 deferMessage(msg); 2172 break; 2173 case WifiScanner.CMD_SCAN_RESULT: 2174 // nothing to do 2175 break; 2176 default: 2177 return NOT_HANDLED; 2178 } 2179 return HANDLED; 2180 } 2181 } 2182 2183 class StationaryState extends State { 2184 @Override 2185 public void enter() { 2186 if (DBG) localLog("Entering StationaryState"); 2187 reportWifiStabilized(mCurrentBssids); 2188 } 2189 2190 @Override 2191 public boolean processMessage(Message msg) { 2192 if (DBG) localLog("Stationary state got " + msg); 2193 ClientInfo ci = mClients.get(msg.replyTo); 2194 switch (msg.what) { 2195 case WifiScanner.CMD_START_TRACKING_CHANGE: 2196 addWifiChangeHandler(ci, msg.arg2); 2197 replySucceeded(msg); 2198 break; 2199 case WifiScanner.CMD_STOP_TRACKING_CHANGE: 2200 removeWifiChangeHandler(ci, msg.arg2); 2201 break; 2202 case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: 2203 /* save configuration till we transition to moving state */ 2204 deferMessage(msg); 2205 break; 2206 case CMD_WIFI_CHANGE_DETECTED: 2207 if (DBG) localLog("Got wifi change detected"); 2208 reportWifiChanged((ScanResult[]) msg.obj); 2209 transitionTo(mMovingState); 2210 break; 2211 case WifiScanner.CMD_SCAN_RESULT: 2212 // nothing to do 2213 break; 2214 default: 2215 return NOT_HANDLED; 2216 } 2217 return HANDLED; 2218 } 2219 } 2220 2221 class MovingState extends State { 2222 boolean mWifiChangeDetected = false; 2223 boolean mScanResultsPending = false; 2224 2225 @Override 2226 public void enter() { 2227 if (DBG) localLog("Entering MovingState"); 2228 if (mTimeoutIntent == null) { 2229 Intent intent = new Intent(ACTION_TIMEOUT, null); 2230 mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 2231 2232 mContext.registerReceiver( 2233 new BroadcastReceiver() { 2234 @Override 2235 public void onReceive(Context context, Intent intent) { 2236 sendMessage(CMD_WIFI_CHANGE_TIMEOUT); 2237 } 2238 }, new IntentFilter(ACTION_TIMEOUT)); 2239 } 2240 issueFullScan(); 2241 } 2242 2243 @Override 2244 public boolean processMessage(Message msg) { 2245 if (DBG) localLog("MovingState state got " + msg); 2246 ClientInfo ci = mClients.get(msg.replyTo); 2247 switch (msg.what) { 2248 case WifiScanner.CMD_START_TRACKING_CHANGE: 2249 addWifiChangeHandler(ci, msg.arg2); 2250 replySucceeded(msg); 2251 break; 2252 case WifiScanner.CMD_STOP_TRACKING_CHANGE: 2253 removeWifiChangeHandler(ci, msg.arg2); 2254 break; 2255 case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: 2256 if (DBG) localLog("Got configuration from app"); 2257 WifiScanner.WifiChangeSettings settings = 2258 (WifiScanner.WifiChangeSettings) msg.obj; 2259 reconfigureScan(settings); 2260 mWifiChangeDetected = false; 2261 long unchangedDelay = settings.unchangedSampleSize * settings.periodInMs; 2262 mAlarmManager.cancel(mTimeoutIntent); 2263 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2264 mClock.elapsedRealtime() + unchangedDelay, 2265 mTimeoutIntent); 2266 break; 2267 case WifiScanner.CMD_SCAN_RESULT: 2268 if (DBG) localLog("Got scan results"); 2269 if (mScanResultsPending) { 2270 if (DBG) localLog("reconfiguring scan"); 2271 reconfigureScan((ScanData[])msg.obj, 2272 STATIONARY_SCAN_PERIOD_MS); 2273 mWifiChangeDetected = false; 2274 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2275 mClock.elapsedRealtime() + MOVING_STATE_TIMEOUT_MS, 2276 mTimeoutIntent); 2277 mScanResultsPending = false; 2278 } 2279 break; 2280 case CMD_WIFI_CHANGE_DETECTED: 2281 if (DBG) localLog("Change detected"); 2282 mAlarmManager.cancel(mTimeoutIntent); 2283 reportWifiChanged((ScanResult[])msg.obj); 2284 mWifiChangeDetected = true; 2285 issueFullScan(); 2286 break; 2287 case CMD_WIFI_CHANGE_TIMEOUT: 2288 if (DBG) localLog("Got timeout event"); 2289 if (mWifiChangeDetected == false) { 2290 transitionTo(mStationaryState); 2291 } 2292 break; 2293 default: 2294 return NOT_HANDLED; 2295 } 2296 return HANDLED; 2297 } 2298 2299 @Override 2300 public void exit() { 2301 mAlarmManager.cancel(mTimeoutIntent); 2302 } 2303 2304 void issueFullScan() { 2305 if (DBG) localLog("Issuing full scan"); 2306 ScanSettings settings = new ScanSettings(); 2307 settings.band = WifiScanner.WIFI_BAND_BOTH; 2308 settings.periodInMs = MOVING_SCAN_PERIOD_MS; 2309 settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 2310 addScanRequest(settings); 2311 mScanResultsPending = true; 2312 } 2313 2314 } 2315 2316 private void reconfigureScan(ScanData[] results, int period) { 2317 // find brightest APs and set them as sentinels 2318 if (results.length < MAX_APS_TO_TRACK) { 2319 localLog("too few APs (" + results.length + ") available to track wifi change"); 2320 return; 2321 } 2322 2323 removeScanRequest(); 2324 2325 // remove duplicate BSSIDs 2326 HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>(); 2327 for (ScanResult result : results[0].getResults()) { 2328 ScanResult saved = bssidToScanResult.get(result.BSSID); 2329 if (saved == null) { 2330 bssidToScanResult.put(result.BSSID, result); 2331 } else if (saved.level > result.level) { 2332 bssidToScanResult.put(result.BSSID, result); 2333 } 2334 } 2335 2336 // find brightest BSSIDs 2337 ScanResult brightest[] = new ScanResult[MAX_APS_TO_TRACK]; 2338 Collection<ScanResult> results2 = bssidToScanResult.values(); 2339 for (ScanResult result : results2) { 2340 for (int j = 0; j < brightest.length; j++) { 2341 if (brightest[j] == null 2342 || (brightest[j].level < result.level)) { 2343 for (int k = brightest.length; k > (j + 1); k--) { 2344 brightest[k - 1] = brightest[k - 2]; 2345 } 2346 brightest[j] = result; 2347 break; 2348 } 2349 } 2350 } 2351 2352 // Get channels to scan for 2353 ArrayList<Integer> channels = new ArrayList<Integer>(); 2354 for (int i = 0; i < brightest.length; i++) { 2355 boolean found = false; 2356 for (int j = i + 1; j < brightest.length; j++) { 2357 if (brightest[j].frequency == brightest[i].frequency) { 2358 found = true; 2359 } 2360 } 2361 if (!found) { 2362 channels.add(brightest[i].frequency); 2363 } 2364 } 2365 2366 if (DBG) localLog("Found " + channels.size() + " channels"); 2367 2368 // set scanning schedule 2369 ScanSettings settings = new ScanSettings(); 2370 settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED; 2371 settings.channels = new ChannelSpec[channels.size()]; 2372 for (int i = 0; i < channels.size(); i++) { 2373 settings.channels[i] = new ChannelSpec(channels.get(i)); 2374 } 2375 2376 settings.periodInMs = period; 2377 addScanRequest(settings); 2378 2379 WifiScanner.WifiChangeSettings settings2 = new WifiScanner.WifiChangeSettings(); 2380 settings2.rssiSampleSize = 3; 2381 settings2.lostApSampleSize = 3; 2382 settings2.unchangedSampleSize = 3; 2383 settings2.minApsBreachingThreshold = 2; 2384 settings2.bssidInfos = new BssidInfo[brightest.length]; 2385 2386 for (int i = 0; i < brightest.length; i++) { 2387 BssidInfo BssidInfo = new BssidInfo(); 2388 BssidInfo.bssid = brightest[i].BSSID; 2389 int threshold = (100 + brightest[i].level) / 32 + 2; 2390 BssidInfo.low = brightest[i].level - threshold; 2391 BssidInfo.high = brightest[i].level + threshold; 2392 settings2.bssidInfos[i] = BssidInfo; 2393 2394 if (DBG) localLog("Setting bssid=" + BssidInfo.bssid + ", " + 2395 "low=" + BssidInfo.low + ", high=" + BssidInfo.high); 2396 } 2397 2398 trackSignificantWifiChange(settings2); 2399 mCurrentBssids = brightest; 2400 } 2401 2402 private void reconfigureScan(WifiScanner.WifiChangeSettings settings) { 2403 2404 if (settings.bssidInfos.length < MAX_APS_TO_TRACK) { 2405 localLog("too few APs (" + settings.bssidInfos.length 2406 + ") available to track wifi change"); 2407 return; 2408 } 2409 2410 if (DBG) localLog("Setting configuration specified by app"); 2411 2412 mCurrentBssids = new ScanResult[settings.bssidInfos.length]; 2413 HashSet<Integer> channels = new HashSet<Integer>(); 2414 2415 for (int i = 0; i < settings.bssidInfos.length; i++) { 2416 ScanResult result = new ScanResult(); 2417 result.BSSID = settings.bssidInfos[i].bssid; 2418 mCurrentBssids[i] = result; 2419 channels.add(settings.bssidInfos[i].frequencyHint); 2420 } 2421 2422 // cancel previous scan 2423 removeScanRequest(); 2424 2425 // set new scanning schedule 2426 ScanSettings settings2 = new ScanSettings(); 2427 settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED; 2428 settings2.channels = new ChannelSpec[channels.size()]; 2429 int i = 0; 2430 for (Integer channel : channels) { 2431 settings2.channels[i++] = new ChannelSpec(channel); 2432 } 2433 2434 settings2.periodInMs = settings.periodInMs; 2435 addScanRequest(settings2); 2436 2437 // start tracking new APs 2438 trackSignificantWifiChange(settings); 2439 } 2440 2441 2442 @Override 2443 public void onChangesFound(ScanResult results[]) { 2444 sendMessage(CMD_WIFI_CHANGE_DETECTED, 0, 0, results); 2445 } 2446 2447 private void addScanRequest(ScanSettings settings) { 2448 if (DBG) localLog("Starting scans"); 2449 if (mInternalClientInfo != null) { 2450 mInternalClientInfo.sendRequestToClientHandler( 2451 WifiScanner.CMD_START_BACKGROUND_SCAN, settings, null); 2452 } 2453 } 2454 2455 private void removeScanRequest() { 2456 if (DBG) localLog("Stopping scans"); 2457 if (mInternalClientInfo != null) { 2458 mInternalClientInfo.sendRequestToClientHandler( 2459 WifiScanner.CMD_STOP_BACKGROUND_SCAN); 2460 } 2461 } 2462 2463 private void trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings) { 2464 if (mScannerImpl != null) { 2465 mScannerImpl.untrackSignificantWifiChange(); 2466 mScannerImpl.trackSignificantWifiChange(settings, this); 2467 } 2468 } 2469 2470 private void untrackSignificantWifiChange() { 2471 if (mScannerImpl != null) { 2472 mScannerImpl.untrackSignificantWifiChange(); 2473 } 2474 } 2475 2476 private void addWifiChangeHandler(ClientInfo ci, int handler) { 2477 mActiveWifiChangeHandlers.add(Pair.create(ci, handler)); 2478 // Add an internal client to make background scan requests. 2479 if (mInternalClientInfo == null) { 2480 mInternalClientInfo = 2481 new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler())); 2482 mInternalClientInfo.register(); 2483 } 2484 } 2485 2486 private void removeWifiChangeHandler(ClientInfo ci, int handler) { 2487 mActiveWifiChangeHandlers.remove(Pair.create(ci, handler)); 2488 untrackSignificantWifiChangeOnEmpty(); 2489 } 2490 2491 private void untrackSignificantWifiChangeOnEmpty() { 2492 if (mActiveWifiChangeHandlers.isEmpty()) { 2493 if (DBG) localLog("Got Disable Wifi Change"); 2494 mCurrentBssids = null; 2495 untrackSignificantWifiChange(); 2496 // Remove the internal client when there are no more external clients. 2497 if (mInternalClientInfo != null) { 2498 mInternalClientInfo.cleanup(); 2499 mInternalClientInfo = null; 2500 } 2501 transitionTo(mDefaultState); 2502 } 2503 } 2504 2505 private void reportWifiChanged(ScanResult[] results) { 2506 WifiScanner.ParcelableScanResults parcelableScanResults = 2507 new WifiScanner.ParcelableScanResults(results); 2508 Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator(); 2509 while (it.hasNext()) { 2510 Pair<ClientInfo, Integer> entry = it.next(); 2511 ClientInfo ci = entry.first; 2512 int handler = entry.second; 2513 ci.reportEvent(WifiScanner.CMD_WIFI_CHANGE_DETECTED, 0, handler, 2514 parcelableScanResults); 2515 } 2516 } 2517 2518 private void reportWifiStabilized(ScanResult[] results) { 2519 WifiScanner.ParcelableScanResults parcelableScanResults = 2520 new WifiScanner.ParcelableScanResults(results); 2521 Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator(); 2522 while (it.hasNext()) { 2523 Pair<ClientInfo, Integer> entry = it.next(); 2524 ClientInfo ci = entry.first; 2525 int handler = entry.second; 2526 ci.reportEvent(WifiScanner.CMD_WIFI_CHANGES_STABILIZED, 0, handler, 2527 parcelableScanResults); 2528 } 2529 } 2530 } 2531 2532 private static String toString(int uid, ScanSettings settings) { 2533 StringBuilder sb = new StringBuilder(); 2534 sb.append("ScanSettings[uid=").append(uid); 2535 sb.append(", period=").append(settings.periodInMs); 2536 sb.append(", report=").append(settings.reportEvents); 2537 if (settings.reportEvents == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL 2538 && settings.numBssidsPerScan > 0 2539 && settings.maxScansToCache > 1) { 2540 sb.append(", batch=").append(settings.maxScansToCache); 2541 sb.append(", numAP=").append(settings.numBssidsPerScan); 2542 } 2543 sb.append(", ").append(ChannelHelper.toString(settings)); 2544 sb.append("]"); 2545 2546 return sb.toString(); 2547 } 2548 2549 @Override 2550 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2551 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 2552 != PackageManager.PERMISSION_GRANTED) { 2553 pw.println("Permission Denial: can't dump WifiScanner from from pid=" 2554 + Binder.getCallingPid() 2555 + ", uid=" + Binder.getCallingUid() 2556 + " without permission " 2557 + android.Manifest.permission.DUMP); 2558 return; 2559 } 2560 pw.println("WifiScanningService - Log Begin ----"); 2561 mLocalLog.dump(fd, pw, args); 2562 pw.println("WifiScanningService - Log End ----"); 2563 pw.println(); 2564 pw.println("clients:"); 2565 for (ClientInfo client : mClients.values()) { 2566 pw.println(" " + client); 2567 } 2568 pw.println("listeners:"); 2569 for (ClientInfo client : mClients.values()) { 2570 Collection<ScanSettings> settingsList = 2571 mBackgroundScanStateMachine.getBackgroundScanSettings(client); 2572 for (ScanSettings settings : settingsList) { 2573 pw.println(" " + toString(client.mUid, settings)); 2574 } 2575 } 2576 if (mBackgroundScheduler != null) { 2577 WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule(); 2578 if (schedule != null) { 2579 pw.println("schedule:"); 2580 pw.println(" base period: " + schedule.base_period_ms); 2581 pw.println(" max ap per scan: " + schedule.max_ap_per_scan); 2582 pw.println(" batched scans: " + schedule.report_threshold_num_scans); 2583 pw.println(" buckets:"); 2584 for (int b = 0; b < schedule.num_buckets; b++) { 2585 WifiNative.BucketSettings bucket = schedule.buckets[b]; 2586 pw.println(" bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)[" 2587 + bucket.report_events + "]: " 2588 + ChannelHelper.toString(bucket)); 2589 } 2590 } 2591 } 2592 if (mPnoScanStateMachine != null) { 2593 mPnoScanStateMachine.dump(fd, pw, args); 2594 } 2595 } 2596 2597 void logScanRequest(String request, ClientInfo ci, int id, WorkSource workSource, 2598 ScanSettings settings, PnoSettings pnoSettings) { 2599 StringBuilder sb = new StringBuilder(); 2600 sb.append(request) 2601 .append(": ") 2602 .append(ci.toString()) 2603 .append(",Id=") 2604 .append(id); 2605 if (workSource != null) { 2606 sb.append(",").append(workSource); 2607 } 2608 if (settings != null) { 2609 sb.append(", "); 2610 describeTo(sb, settings); 2611 } 2612 if (pnoSettings != null) { 2613 sb.append(", "); 2614 describeTo(sb, pnoSettings); 2615 } 2616 localLog(sb.toString()); 2617 } 2618 2619 void logCallback(String callback, ClientInfo ci, int id, String extra) { 2620 StringBuilder sb = new StringBuilder(); 2621 sb.append(callback) 2622 .append(": ") 2623 .append(ci.toString()) 2624 .append(",Id=") 2625 .append(id); 2626 if (extra != null) { 2627 sb.append(",").append(extra); 2628 } 2629 localLog(sb.toString()); 2630 } 2631 2632 static String describeForLog(ScanData[] results) { 2633 StringBuilder sb = new StringBuilder(); 2634 sb.append("results="); 2635 for (int i = 0; i < results.length; ++i) { 2636 if (i > 0) sb.append(";"); 2637 sb.append(results[i].getResults().length); 2638 } 2639 return sb.toString(); 2640 } 2641 2642 static String describeForLog(ScanResult[] results) { 2643 return "results=" + results.length; 2644 } 2645 2646 static String describeTo(StringBuilder sb, ScanSettings scanSettings) { 2647 sb.append("ScanSettings { ") 2648 .append(" band:").append(scanSettings.band) 2649 .append(" period:").append(scanSettings.periodInMs) 2650 .append(" reportEvents:").append(scanSettings.reportEvents) 2651 .append(" numBssidsPerScan:").append(scanSettings.numBssidsPerScan) 2652 .append(" maxScansToCache:").append(scanSettings.maxScansToCache) 2653 .append(" channels:[ "); 2654 if (scanSettings.channels != null) { 2655 for (int i = 0; i < scanSettings.channels.length; i++) { 2656 sb.append(scanSettings.channels[i].frequency) 2657 .append(" "); 2658 } 2659 } 2660 sb.append(" ] ") 2661 .append(" } "); 2662 return sb.toString(); 2663 } 2664 2665 static String describeTo(StringBuilder sb, PnoSettings pnoSettings) { 2666 sb.append("PnoSettings { ") 2667 .append(" min5GhzRssi:").append(pnoSettings.min5GHzRssi) 2668 .append(" min24GhzRssi:").append(pnoSettings.min24GHzRssi) 2669 .append(" initialScoreMax:").append(pnoSettings.initialScoreMax) 2670 .append(" currentConnectionBonus:").append(pnoSettings.currentConnectionBonus) 2671 .append(" sameNetworkBonus:").append(pnoSettings.sameNetworkBonus) 2672 .append(" secureBonus:").append(pnoSettings.secureBonus) 2673 .append(" band5GhzBonus:").append(pnoSettings.band5GHzBonus) 2674 .append(" isConnected:").append(pnoSettings.isConnected) 2675 .append(" networks:[ "); 2676 if (pnoSettings.networkList != null) { 2677 for (int i = 0; i < pnoSettings.networkList.length; i++) { 2678 sb.append(pnoSettings.networkList[i].ssid) 2679 .append(",") 2680 .append(pnoSettings.networkList[i].networkId) 2681 .append(" "); 2682 } 2683 } 2684 sb.append(" ] ") 2685 .append(" } "); 2686 return sb.toString(); 2687 } 2688} 2689