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