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