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