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