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