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