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