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