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