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