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