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