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