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