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