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