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