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