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