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