xref: /aosp_15_r20/external/sl4a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiScannerFacade.java (revision 456ef56af69dcf0481dd36cc45216c4002d72fa3)
1 /*
2  * Copyright (C) 2017 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.googlecode.android_scripting.facade.wifi;
18 
19 import android.app.Service;
20 import android.content.Context;
21 import android.net.wifi.ScanResult;
22 import android.net.wifi.WifiManager;
23 import android.net.wifi.WifiScanner;
24 import android.net.wifi.WifiScanner.BssidInfo;
25 import android.net.wifi.WifiScanner.ChannelSpec;
26 import android.net.wifi.WifiScanner.ScanData;
27 import android.net.wifi.WifiScanner.ScanSettings;
28 import android.os.Bundle;
29 import android.os.SystemClock;
30 import android.provider.Settings.SettingNotFoundException;
31 
32 import com.googlecode.android_scripting.Log;
33 import com.googlecode.android_scripting.MainThread;
34 import com.googlecode.android_scripting.facade.EventFacade;
35 import com.googlecode.android_scripting.facade.FacadeManager;
36 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
37 import com.googlecode.android_scripting.rpc.Rpc;
38 import com.googlecode.android_scripting.rpc.RpcOptional;
39 import com.googlecode.android_scripting.rpc.RpcParameter;
40 
41 import org.json.JSONArray;
42 import org.json.JSONException;
43 import org.json.JSONObject;
44 
45 import java.util.Arrays;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Set;
49 import java.util.concurrent.Callable;
50 import java.util.concurrent.ConcurrentHashMap;
51 
52 /**
53  * WifiScanner functions.
54  */
55 public class WifiScannerFacade extends RpcReceiver {
56     private final Service mService;
57     private final EventFacade mEventFacade;
58     private final WifiScanner mScan;
59     private final WifiManager mWifiManager;
60     // These counters are just for indexing;
61     // they do not represent the total number of listeners
62     private static int WifiScanListenerCnt;
63     private static int WifiChangeListenerCnt;
64     private static int WifiBssidListenerCnt;
65     private final ConcurrentHashMap<Integer, WifiScanListener> scanListeners;
66     private final ConcurrentHashMap<Integer, WifiScanListener> scanBackgroundListeners;
67     private final ConcurrentHashMap<Integer, ChangeListener> trackChangeListeners;
68     private final ConcurrentHashMap<Integer, WifiBssidListener> trackBssidListeners;
69     private static ConcurrentHashMap<Integer, ScanResult[]> wifiScannerResultList;
70     private static ConcurrentHashMap<Integer, ScanData[]> wifiScannerDataList;
71 
WifiScannerFacade(FacadeManager manager)72     public WifiScannerFacade(FacadeManager manager) {
73         super(manager);
74         mService = manager.getService();
75         mScan = (WifiScanner) mService.getSystemService(Context.WIFI_SCANNING_SERVICE);
76         mWifiManager = mService.getSystemService(WifiManager.class);
77         mEventFacade = manager.getReceiver(EventFacade.class);
78         scanListeners = new ConcurrentHashMap<Integer, WifiScanListener>();
79         scanBackgroundListeners = new ConcurrentHashMap<Integer, WifiScanListener>();
80         trackChangeListeners = new ConcurrentHashMap<Integer, ChangeListener>();
81         trackBssidListeners = new ConcurrentHashMap<Integer, WifiBssidListener>();
82         wifiScannerResultList = new ConcurrentHashMap<Integer, ScanResult[]>();
83         wifiScannerDataList = new ConcurrentHashMap<Integer, ScanData[]>();
84     }
85 
getWifiScanResult(Integer listenerIndex)86     public static List<ScanResult> getWifiScanResult(Integer listenerIndex) {
87         ScanResult[] sr = wifiScannerResultList.get(listenerIndex);
88         return Arrays.asList(sr);
89     }
90 
91     private class WifiActionListener implements WifiScanner.ActionListener {
92         private final Bundle mResults;
93         public int mIndex;
94         protected String mEventType;
95         private long startScanElapsedRealTime;
96 
WifiActionListener(String type, int idx, Bundle resultBundle, long startScanERT)97         public WifiActionListener(String type, int idx, Bundle resultBundle, long startScanERT) {
98             this.mIndex = idx;
99             this.mEventType = type;
100             this.mResults = resultBundle;
101             this.startScanElapsedRealTime = startScanERT;
102         }
103 
104         @Override
onSuccess()105         public void onSuccess() {
106             Log.d("onSuccess " + mEventType + " " + mIndex);
107             mResults.putString("Type", "onSuccess");
108             mResults.putInt("Index", mIndex);
109             mResults.putLong("ScanElapsedRealtime", startScanElapsedRealTime);
110             mEventFacade.postEvent(mEventType + mIndex + "onSuccess", mResults.clone());
111             mResults.clear();
112         }
113 
114         @Override
onFailure(int reason, String description)115         public void onFailure(int reason, String description) {
116             Log.d("onFailure " + mEventType + " " + mIndex);
117             mResults.putString("Type", "onFailure");
118             mResults.putInt("Index", mIndex);
119             mResults.putInt("Reason", reason);
120             mResults.putString("Description", description);
121             mEventFacade.postEvent(mEventType + mIndex + "onFailure", mResults.clone());
122             mResults.clear();
123         }
124 
reportResult(ScanResult[] results, String type)125         public void reportResult(ScanResult[] results, String type) {
126             Log.d("reportResult " + mEventType + " " + mIndex);
127             mResults.putInt("Index", mIndex);
128             mResults.putLong("ResultElapsedRealtime", SystemClock.elapsedRealtime());
129             mResults.putString("Type", type);
130             mResults.putParcelableArray("Results", results);
131             mEventFacade.postEvent(mEventType + mIndex + type, mResults.clone());
132             mResults.clear();
133         }
134     }
135 
136     /**
137      * Constructs a wifiScanListener obj and returns it
138      *
139      * @return WifiScanListener
140      */
genWifiScanListener()141     private WifiScanListener genWifiScanListener() {
142         WifiScanListener mWifiScannerListener = MainThread.run(mService,
143                 new Callable<WifiScanListener>() {
144                     @Override
145                     public WifiScanListener call() throws Exception {
146                         return new WifiScanListener();
147                     }
148                 });
149         scanListeners.put(mWifiScannerListener.mIndex, mWifiScannerListener);
150         return mWifiScannerListener;
151     }
152 
153     /**
154      * Constructs a wifiScanListener obj for background scan and returns it
155      *
156      * @return WifiScanListener
157      */
genBackgroundWifiScanListener()158     private WifiScanListener genBackgroundWifiScanListener() {
159         WifiScanListener mWifiScannerListener = MainThread.run(mService,
160                 new Callable<WifiScanListener>() {
161                     @Override
162                     public WifiScanListener call() throws Exception {
163                         return new WifiScanListener();
164                     }
165                 });
166         scanBackgroundListeners.put(mWifiScannerListener.mIndex, mWifiScannerListener);
167         return mWifiScannerListener;
168     }
169 
170     private class WifiScanListener implements WifiScanner.ScanListener {
171         private static final String mEventType = "WifiScannerScan";
172         protected final Bundle mScanResults;
173         protected final Bundle mScanData;
174         private final WifiActionListener mWAL;
175         public int mIndex;
176 
WifiScanListener()177         public WifiScanListener() {
178             mScanResults = new Bundle();
179             mScanData = new Bundle();
180             WifiScanListenerCnt += 1;
181             mIndex = WifiScanListenerCnt;
182             mWAL = new WifiActionListener(mEventType, mIndex, mScanResults,
183                     SystemClock.elapsedRealtime());
184         }
185 
186         @Override
onSuccess()187         public void onSuccess() {
188             mWAL.onSuccess();
189         }
190 
191         @Override
onFailure(int reason, String description)192         public void onFailure(int reason, String description) {
193             scanListeners.remove(mIndex);
194             mWAL.onFailure(reason, description);
195         }
196 
197         @Override
onPeriodChanged(int periodInMs)198         public void onPeriodChanged(int periodInMs) {
199             Log.d("onPeriodChanged " + mEventType + " " + mIndex);
200             mScanResults.putString("Type", "onPeriodChanged");
201             mScanResults.putInt("NewPeriod", periodInMs);
202             mEventFacade.postEvent(mEventType + mIndex, mScanResults.clone());
203             mScanResults.clear();
204         }
205 
206         @Override
onFullResult(ScanResult fullScanResult)207         public void onFullResult(ScanResult fullScanResult) {
208             Log.d("onFullResult WifiScanListener " + mIndex);
209             mWAL.reportResult(new ScanResult[] {
210                     fullScanResult
211             }, "onFullResult");
212         }
213 
onResults(ScanData[] results)214         public void onResults(ScanData[] results) {
215             Log.d("onResult WifiScanListener " + mIndex);
216             wifiScannerDataList.put(mIndex, results);
217             mScanData.putInt("Index", mIndex);
218             mScanData.putLong("ResultElapsedRealtime", SystemClock.elapsedRealtime());
219             mScanData.putString("Type", "onResults");
220             mScanData.putParcelableArray("Results", results);
221             mEventFacade.postEvent(mEventType + mIndex + "onResults", mScanData.clone());
222             mScanData.clear();
223         }
224     }
225 
226     /**
227      * Constructs a ChangeListener obj and returns it
228      *
229      * @return ChangeListener
230      */
genWifiChangeListener()231     private ChangeListener genWifiChangeListener() {
232         ChangeListener mWifiChangeListener = MainThread.run(mService,
233                 new Callable<ChangeListener>() {
234                     @Override
235                     public ChangeListener call() throws Exception {
236                         return new ChangeListener();
237                     }
238                 });
239         trackChangeListeners.put(mWifiChangeListener.mIndex, mWifiChangeListener);
240         return mWifiChangeListener;
241     }
242 
243     private class ChangeListener implements WifiScanner.WifiChangeListener {
244         private static final String mEventType = "WifiScannerChange";
245         protected final Bundle mResults;
246         private final WifiActionListener mWAL;
247         public int mIndex;
248 
ChangeListener()249         public ChangeListener() {
250             mResults = new Bundle();
251             WifiChangeListenerCnt += 1;
252             mIndex = WifiChangeListenerCnt;
253             mWAL = new WifiActionListener(mEventType, mIndex, mResults,
254                     SystemClock.elapsedRealtime());
255         }
256 
257         @Override
onSuccess()258         public void onSuccess() {
259             mWAL.onSuccess();
260         }
261 
262         @Override
onFailure(int reason, String description)263         public void onFailure(int reason, String description) {
264             trackChangeListeners.remove(mIndex);
265             mWAL.onFailure(reason, description);
266         }
267 
268         /**
269          * indicates that changes were detected in wifi environment
270          *
271          * @param results indicate the access points that exhibited change
272          */
273         @Override
onChanging(ScanResult[] results)274         public void onChanging(ScanResult[] results) { /* changes are found */
275             mWAL.reportResult(results, "onChanging");
276         }
277 
278         /**
279          * indicates that no wifi changes are being detected for a while
280          *
281          * @param results indicate the access points that are bing monitored for change
282          */
283         @Override
onQuiescence(ScanResult[] results)284         public void onQuiescence(ScanResult[] results) { /* changes settled down */
285             mWAL.reportResult(results, "onQuiescence");
286         }
287     }
288 
genWifiBssidListener()289     private WifiBssidListener genWifiBssidListener() {
290         WifiBssidListener mWifiBssidListener = MainThread.run(mService,
291                 new Callable<WifiBssidListener>() {
292                     @Override
293                     public WifiBssidListener call() throws Exception {
294                         return new WifiBssidListener();
295                     }
296                 });
297         trackBssidListeners.put(mWifiBssidListener.mIndex, mWifiBssidListener);
298         return mWifiBssidListener;
299     }
300 
301     private class WifiBssidListener implements WifiScanner.BssidListener {
302         private static final String mEventType = "WifiScannerBssid";
303         protected final Bundle mResults;
304         private final WifiActionListener mWAL;
305         public int mIndex;
306 
WifiBssidListener()307         public WifiBssidListener() {
308             mResults = new Bundle();
309             WifiBssidListenerCnt += 1;
310             mIndex = WifiBssidListenerCnt;
311             mWAL = new WifiActionListener(mEventType, mIndex, mResults,
312                     SystemClock.elapsedRealtime());
313         }
314 
315         @Override
onSuccess()316         public void onSuccess() {
317             mWAL.onSuccess();
318         }
319 
320         @Override
onFailure(int reason, String description)321         public void onFailure(int reason, String description) {
322             trackBssidListeners.remove(mIndex);
323             mWAL.onFailure(reason, description);
324         }
325 
326         @Override
onFound(ScanResult[] results)327         public void onFound(ScanResult[] results) {
328             mWAL.reportResult(results, "onFound");
329         }
330 
331         @Override
onLost(ScanResult[] results)332         public void onLost(ScanResult[] results) {
333             mWAL.reportResult(results, "onLost");
334         }
335     }
336 
parseScanSettings(JSONObject j)337     private ScanSettings parseScanSettings(JSONObject j) throws JSONException {
338         if (j == null) {
339             return null;
340         }
341         ScanSettings result = new ScanSettings();
342         if (j.has("band")) {
343             result.band = j.optInt("band");
344         }
345         if (j.has("channels")) {
346             JSONArray chs = j.getJSONArray("channels");
347             ChannelSpec[] channels = new ChannelSpec[chs.length()];
348             for (int i = 0; i < channels.length; i++) {
349                 channels[i] = new ChannelSpec(chs.getInt(i));
350             }
351             result.channels = channels;
352         }
353         if (j.has("maxScansToCache")) {
354             result.maxScansToCache = j.getInt("maxScansToCache");
355         }
356         /* periodInMs and reportEvents are required */
357         result.periodInMs = j.getInt("periodInMs");
358         if (j.has("maxPeriodInMs")) {
359             result.maxPeriodInMs = j.getInt("maxPeriodInMs");
360         }
361         if (j.has("stepCount")) {
362             result.stepCount = j.getInt("stepCount");
363         }
364         result.reportEvents = j.getInt("reportEvents");
365         if (j.has("numBssidsPerScan")) {
366             result.numBssidsPerScan = j.getInt("numBssidsPerScan");
367         }
368         if (j.has("type")) {
369             result.type = j.getInt("type");
370         }
371         return result;
372     }
373 
parseBssidInfo(JSONArray jBssids)374     private BssidInfo[] parseBssidInfo(JSONArray jBssids) throws JSONException {
375         BssidInfo[] bssids = new BssidInfo[jBssids.length()];
376         for (int i = 0; i < bssids.length; i++) {
377             JSONObject bi = (JSONObject) jBssids.get(i);
378             BssidInfo bssidInfo = new BssidInfo();
379             bssidInfo.bssid = bi.getString("BSSID");
380             bssidInfo.high = bi.getInt("high");
381             bssidInfo.low = bi.getInt("low");
382             if (bi.has("frequencyHint")) {
383                 bssidInfo.frequencyHint = bi.getInt("frequencyHint");
384             }
385             bssids[i] = bssidInfo;
386         }
387         return bssids;
388     }
389 
390     /**
391      * Starts periodic WifiScanner scan
392      *
393      * @param scanSettings
394      * @return the id of the scan listener associated with this scan
395      * @throws JSONException
396      */
397     @Rpc(description = "Starts a WifiScanner Background scan")
wifiScannerStartBackgroundScan( @pcParametername = "scanSettings") JSONObject scanSettings)398     public Integer wifiScannerStartBackgroundScan(
399             @RpcParameter(name = "scanSettings") JSONObject scanSettings)
400                     throws JSONException {
401         ScanSettings ss = parseScanSettings(scanSettings);
402         Log.d("startWifiScannerScan with " + Arrays.toString(ss.channels));
403         WifiScanListener listener = genBackgroundWifiScanListener();
404         mScan.startBackgroundScan(ss, listener);
405         return listener.mIndex;
406     }
407 
408     /**
409      * Get currently available scan results on appropriate listeners
410      *
411      * @return true if all scan results were reported correctly
412      * @throws JSONException
413      */
414     @Rpc(description = "Get currently available scan results on appropriate listeners")
wifiScannerGetScanResults()415     public Boolean wifiScannerGetScanResults() throws JSONException {
416         mScan.getScanResults();
417         return true;
418     }
419 
420     /**
421      * Stops a WifiScanner scan
422      *
423      * @param listenerIndex the id of the scan listener whose scan to stop
424      * @throws Exception
425      */
426     @Rpc(description = "Stops an ongoing  WifiScanner Background scan")
wifiScannerStopBackgroundScan( @pcParametername = "listener") Integer listenerIndex)427     public void wifiScannerStopBackgroundScan(
428             @RpcParameter(name = "listener") Integer listenerIndex)
429                     throws Exception {
430         if (!scanBackgroundListeners.containsKey(listenerIndex)) {
431             throw new Exception("Background scan session " + listenerIndex + " does not exist");
432         }
433         WifiScanListener listener = scanBackgroundListeners.get(listenerIndex);
434         Log.d("stopWifiScannerScan listener " + listener.mIndex);
435         mScan.stopBackgroundScan(listener);
436         wifiScannerResultList.remove(listenerIndex);
437         scanBackgroundListeners.remove(listenerIndex);
438     }
439 
440     /**
441      * Starts periodic WifiScanner scan
442      *
443      * @param scanSettings
444      * @return the id of the scan listener associated with this scan
445      * @throws JSONException
446      */
447     @Rpc(description = "Starts a WifiScanner single scan")
wifiScannerStartScan( @pcParametername = "scanSettings") JSONObject scanSettings)448     public Integer wifiScannerStartScan(
449             @RpcParameter(name = "scanSettings") JSONObject scanSettings)
450                     throws JSONException {
451         ScanSettings ss = parseScanSettings(scanSettings);
452         Log.d("startWifiScannerScan with " + Arrays.toString(ss.channels));
453         WifiScanListener listener = genWifiScanListener();
454         mScan.startScan(ss, listener);
455         return listener.mIndex;
456     }
457 
458     /**
459      * Stops a WifiScanner scan
460      *
461      * @param listenerIndex the id of the scan listener whose scan to stop
462      * @throws Exception
463      */
464     @Rpc(description = "Stops an ongoing  WifiScanner Single scan")
wifiScannerStopScan(@pcParametername = "listener") Integer listenerIndex)465     public void wifiScannerStopScan(@RpcParameter(name = "listener") Integer listenerIndex)
466             throws Exception {
467         if (!scanListeners.containsKey(listenerIndex)) {
468             throw new Exception("Single scan session " + listenerIndex + " does not exist");
469         }
470         WifiScanListener listener = scanListeners.get(listenerIndex);
471         Log.d("stopWifiScannerScan listener " + listener.mIndex);
472         mScan.stopScan(listener);
473         wifiScannerResultList.remove(listener.mIndex);
474         scanListeners.remove(listenerIndex);
475     }
476 
477     /** RPC Methods */
478     @Rpc(description = "Returns the channels covered by the specified band number.")
wifiScannerGetAvailableChannels( @pcParametername = "band") Integer band)479     public List<Integer> wifiScannerGetAvailableChannels(
480             @RpcParameter(name = "band") Integer band) {
481         return mScan.getAvailableChannels(band);
482     }
483 
484     /**
485      * Starts tracking wifi changes
486      *
487      * @return the id of the change listener associated with this track
488      * @throws Exception
489      */
490     @Rpc(description = "Starts tracking wifi changes")
wifiScannerStartTrackingChange()491     public Integer wifiScannerStartTrackingChange() throws Exception {
492         ChangeListener listener = genWifiChangeListener();
493         mScan.startTrackingWifiChange(listener);
494         return listener.mIndex;
495     }
496 
497     /**
498      * Stops tracking wifi changes
499      *
500      * @param listenerIndex the id of the change listener whose track to stop
501      * @throws Exception
502      */
503     @Rpc(description = "Stops tracking wifi changes")
wifiScannerStopTrackingChange( @pcParametername = "listener") Integer listenerIndex)504     public void wifiScannerStopTrackingChange(
505             @RpcParameter(name = "listener") Integer listenerIndex) throws Exception {
506         if (!trackChangeListeners.containsKey(listenerIndex)) {
507             throw new Exception("Wifi change tracking session " + listenerIndex
508                     + " does not exist");
509         }
510         ChangeListener listener = trackChangeListeners.get(listenerIndex);
511         mScan.stopTrackingWifiChange(listener);
512         trackChangeListeners.remove(listenerIndex);
513     }
514 
515     /**
516      * Starts tracking changes of the specified bssids.
517      *
518      * @param bssidInfos An array of json strings, each representing a BssidInfo object.
519      * @param apLostThreshold
520      * @return The index of the listener used to start the tracking.
521      * @throws JSONException
522      */
523     @Rpc(description = "Starts tracking changes of the specified bssids.")
wifiScannerStartTrackingBssids( @pcParametername = "bssidInfos") JSONArray bssidInfos, @RpcParameter(name = "apLostThreshold") Integer apLostThreshold)524     public Integer wifiScannerStartTrackingBssids(
525             @RpcParameter(name = "bssidInfos") JSONArray bssidInfos,
526             @RpcParameter(name = "apLostThreshold") Integer apLostThreshold) throws JSONException {
527         BssidInfo[] bssids = parseBssidInfo(bssidInfos);
528         WifiBssidListener listener = genWifiBssidListener();
529         mScan.startTrackingBssids(bssids, apLostThreshold, listener);
530         return listener.mIndex;
531     }
532 
533     /**
534      * Stops tracking the list of APs associated with the input listener
535      *
536      * @param listenerIndex the id of the bssid listener whose track to stop
537      * @throws Exception
538      */
539     @Rpc(description = "Stops tracking changes in the APs on the list")
wifiScannerStopTrackingBssids( @pcParametername = "listener") Integer listenerIndex)540     public void wifiScannerStopTrackingBssids(
541             @RpcParameter(name = "listener") Integer listenerIndex) throws Exception {
542         if (!trackBssidListeners.containsKey(listenerIndex)) {
543             throw new Exception("Bssid tracking session " + listenerIndex + " does not exist");
544         }
545         WifiBssidListener listener = trackBssidListeners.get(listenerIndex);
546         mScan.stopTrackingBssids(listener);
547         trackBssidListeners.remove(listenerIndex);
548     }
549 
550     @Rpc(description = "Toggle the 'WiFi scan always available' option. If an input is given, the "
551             + "option is set to what the input boolean indicates.")
wifiScannerToggleAlwaysAvailable( @pcParametername = "alwaysAvailable") @pcOptional Boolean alwaysAvailable)552     public void wifiScannerToggleAlwaysAvailable(
553             @RpcParameter(name = "alwaysAvailable") @RpcOptional Boolean alwaysAvailable)
554                     throws SettingNotFoundException {
555         mWifiManager.setScanAlwaysAvailable(
556                 alwaysAvailable == null ? !mWifiManager.isScanAlwaysAvailable() : alwaysAvailable);
557     }
558 
559     @Rpc(description = "Returns true if WiFi scan is always available, false otherwise.")
wifiScannerIsAlwaysAvailable()560     public Boolean wifiScannerIsAlwaysAvailable() throws SettingNotFoundException {
561         return mWifiManager.isScanAlwaysAvailable();
562     }
563 
564     @Rpc(description = "Returns a list of mIndexes of existing listeners")
wifiGetCurrentScanIndexes()565     public Set<Integer> wifiGetCurrentScanIndexes() {
566         return scanListeners.keySet();
567     }
568 
569     /**
570      * Starts tracking wifi changes
571      *
572      * @return the id of the change listener associated with this track
573      * @throws Exception
574      */
575     @Rpc(description = "Starts tracking wifi changes with track settings")
wifiScannerStartTrackingChangeWithSetting( @pcParametername = "trackSettings") JSONArray bssidSettings, @RpcParameter(name = "rssiSS") Integer rssiSS, @RpcParameter(name = "lostApSS") Integer lostApSS, @RpcParameter(name = "unchangedSS") Integer unchangedSS, @RpcParameter(name = "minApsBreachingThreshold") Integer minApsBreachingThreshold, @RpcParameter(name = "periodInMs") Integer periodInMs)576     public Integer wifiScannerStartTrackingChangeWithSetting(
577             @RpcParameter(name = "trackSettings") JSONArray bssidSettings,
578             @RpcParameter(name = "rssiSS") Integer rssiSS,
579             @RpcParameter(name = "lostApSS") Integer lostApSS,
580             @RpcParameter(name = "unchangedSS") Integer unchangedSS,
581             @RpcParameter(name = "minApsBreachingThreshold") Integer minApsBreachingThreshold,
582             @RpcParameter(name = "periodInMs") Integer periodInMs) throws Exception {
583         Log.d("starting change track with track settings");
584         BssidInfo[] bssids = parseBssidInfo(bssidSettings);
585         mScan.configureWifiChange(rssiSS, lostApSS, unchangedSS, minApsBreachingThreshold,
586               periodInMs, bssids);
587         ChangeListener listener = genWifiChangeListener();
588         mScan.startTrackingWifiChange(listener);
589         return listener.mIndex;
590     }
591 
592     /**
593      * Shuts down all activities associated with WifiScanner
594      */
595     @Rpc(description = "Shuts down all WifiScanner activities and remove listeners.")
wifiScannerShutdown()596     public void wifiScannerShutdown() {
597         this.shutdown();
598     }
599 
600     /**
601      * Stops all activity
602      */
603     @Override
shutdown()604     public void shutdown() {
605         try {
606             if (!scanListeners.isEmpty()) {
607                 Iterator<ConcurrentHashMap.Entry<Integer, WifiScanListener>> iter = scanListeners
608                         .entrySet().iterator();
609                 while (iter.hasNext()) {
610                     ConcurrentHashMap.Entry<Integer, WifiScanListener> entry = iter.next();
611                     this.wifiScannerStopScan(entry.getKey());
612                 }
613             }
614             if (!scanBackgroundListeners.isEmpty()) {
615                 Iterator<ConcurrentHashMap.Entry<Integer, WifiScanListener>> iter = scanBackgroundListeners
616                         .entrySet().iterator();
617                 while (iter.hasNext()) {
618                     ConcurrentHashMap.Entry<Integer, WifiScanListener> entry = iter.next();
619                     this.wifiScannerStopBackgroundScan(entry.getKey());
620                 }
621             }
622             if (!trackChangeListeners.isEmpty()) {
623                 Iterator<ConcurrentHashMap.Entry<Integer, ChangeListener>> iter = trackChangeListeners
624                         .entrySet().iterator();
625                 while (iter.hasNext()) {
626                     ConcurrentHashMap.Entry<Integer, ChangeListener> entry = iter.next();
627                     this.wifiScannerStopTrackingChange(entry.getKey());
628                 }
629             }
630             if (!trackBssidListeners.isEmpty()) {
631                 Iterator<ConcurrentHashMap.Entry<Integer, WifiBssidListener>> iter = trackBssidListeners
632                         .entrySet().iterator();
633                 while (iter.hasNext()) {
634                     ConcurrentHashMap.Entry<Integer, WifiBssidListener> entry = iter.next();
635                     this.wifiScannerStopTrackingBssids(entry.getKey());
636                 }
637             }
638         } catch (Exception e) {
639             Log.e("Shutdown failed: " + e.toString());
640         }
641     }
642 }
643