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