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