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