1 /*
2  * Copyright (C) 2011, 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.bandwidthtest.util;
18 
19 import android.app.DownloadManager;
20 import android.app.DownloadManager.Query;
21 import android.app.DownloadManager.Request;
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.ApplicationInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.PackageManager.NameNotFoundException;
29 import android.database.Cursor;
30 import android.net.ConnectivityManager;
31 import android.net.NetworkInfo;
32 import android.net.NetworkInfo.State;
33 import android.net.Uri;
34 import android.net.wifi.ScanResult;
35 import android.net.wifi.WifiConfiguration;
36 import android.net.wifi.WifiConfiguration.KeyMgmt;
37 import android.net.wifi.WifiManager;
38 import android.os.Handler;
39 import android.os.Message;
40 import android.provider.Settings;
41 import android.util.Log;
42 
43 import com.android.bandwidthtest.NetworkState;
44 import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
45 import com.android.internal.util.AsyncChannel;
46 
47 import java.io.IOException;
48 import java.net.UnknownHostException;
49 import java.util.List;
50 
51 /*
52  * Utility class used to set the connectivity of the device and to download files.
53  */
54 public class ConnectionUtil {
55     private static final String LOG_TAG = "ConnectionUtil";
56     private static final String DOWNLOAD_MANAGER_PKG_NAME = "com.android.providers.downloads";
57     private static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; // 10 seconds
58     private static final int WIFI_SCAN_TIMEOUT = 50 * 1000;
59     public static final int SHORT_TIMEOUT = 5 * 1000;
60     public static final int LONG_TIMEOUT = 5 * 60 * 1000; // 5 minutes
61     private ConnectivityReceiver mConnectivityReceiver = null;
62     private WifiReceiver mWifiReceiver = null;
63     private DownloadReceiver mDownloadReceiver = null;
64     private DownloadManager mDownloadManager;
65     private NetworkInfo mNetworkInfo;
66     private NetworkInfo mOtherNetworkInfo;
67     private boolean mScanResultIsAvailable = false;
68     private ConnectivityManager mCM;
69     private Object mWifiMonitor = new Object();
70     private Object mConnectivityMonitor = new Object();
71     private Object mDownloadMonitor = new Object();
72     private int mWifiState;
73     private NetworkInfo mWifiNetworkInfo;
74     private WifiManager mWifiManager;
75     private Context mContext;
76     // Verify connectivity state
77     // ConnectivityManager.TYPE_* is deprecated and no longer extended, so use the max public
78     // network type - TYPE_VPN should be enough.
79     // TODO: Replace registering CONNECTIVITY_ACTION with registering NetworkCallback and check
80     //  network by NetworkCapabilities.TRANSPORT_* and NetworkCapabilities.hasTransport() instead.
81     private static final int NUM_NETWORK_TYPES = ConnectivityManager.TYPE_VPN + 1;
82     private NetworkState[] mConnectivityState = new NetworkState[NUM_NETWORK_TYPES];
83 
ConnectionUtil(Context context)84     public ConnectionUtil(Context context) {
85         mContext = context;
86     }
87 
88     /**
89      * Initialize the class. Needs to be called before any other methods in {@link ConnectionUtil}
90      *
91      * @throws Exception
92      */
initialize()93     public void initialize() throws Exception {
94         // Register a connectivity receiver for CONNECTIVITY_ACTION
95         mConnectivityReceiver = new ConnectivityReceiver();
96         mContext.registerReceiver(mConnectivityReceiver,
97                 new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
98 
99         // Register a download receiver for ACTION_DOWNLOAD_COMPLETE
100         mDownloadReceiver = new DownloadReceiver();
101         mContext.registerReceiver(mDownloadReceiver,
102                 new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
103                 Context.RECEIVER_EXPORTED_UNAUDITED);
104 
105         // Register a wifi receiver
106         mWifiReceiver = new WifiReceiver();
107         IntentFilter mIntentFilter = new IntentFilter();
108         mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
109         mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
110         mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
111         mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
112         mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
113         mContext.registerReceiver(mWifiReceiver, mIntentFilter);
114 
115         // Get an instance of ConnectivityManager
116         mCM = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
117 
118         // Get an instance of WifiManager
119         mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
120 
121         mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
122 
123         initializeNetworkStates();
124 
125 
126     }
127 
128     /**
129      * Additional initialization needed for wifi related tests.
130      */
wifiTestInit()131     public void wifiTestInit() {
132         mWifiManager.setWifiEnabled(true);
133         Log.v(LOG_TAG, "Clear Wifi before we start the test.");
134         sleep(SHORT_TIMEOUT);
135         removeConfiguredNetworksAndDisableWifi();
136     }
137 
138 
139     /**
140      * A wrapper of a broadcast receiver which provides network connectivity information
141      * for all kinds of network: wifi, mobile, etc.
142      */
143     private class ConnectivityReceiver extends BroadcastReceiver {
144         /**
145          * {@inheritDoc}
146          */
147         @Override
onReceive(Context context, Intent intent)148         public void onReceive(Context context, Intent intent) {
149             if (isInitialStickyBroadcast()) {
150                 Log.d(LOG_TAG, "This is a sticky broadcast don't do anything.");
151                 return;
152             }
153             Log.v(LOG_TAG, "ConnectivityReceiver: onReceive() is called with " + intent);
154             String action = intent.getAction();
155             if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
156                 Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
157                 return;
158             }
159 
160             final ConnectivityManager connManager = (ConnectivityManager) context
161                     .getSystemService(Context.CONNECTIVITY_SERVICE);
162             mNetworkInfo = connManager.getActiveNetworkInfo();
163 
164             if (intent.hasExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO)) {
165                 mOtherNetworkInfo = (NetworkInfo)
166                         intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
167             }
168 
169             Log.v(LOG_TAG, "mNetworkInfo: " + mNetworkInfo.toString());
170             recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
171             if (mOtherNetworkInfo != null) {
172                 Log.v(LOG_TAG, "mOtherNetworkInfo: " + mOtherNetworkInfo.toString());
173                 recordNetworkState(mOtherNetworkInfo.getType(), mOtherNetworkInfo.getState());
174             }
175             notifyNetworkConnectivityChange();
176         }
177     }
178 
179     /**
180      * A wrapper of a broadcast receiver which provides wifi information.
181      */
182     private class WifiReceiver extends BroadcastReceiver {
183         /**
184          * {@inheritDoc}
185          */
186         @Override
onReceive(Context context, Intent intent)187         public void onReceive(Context context, Intent intent) {
188             String action = intent.getAction();
189             Log.v("WifiReceiver", "onReceive() is calleld with " + intent);
190             if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
191                 Log.v(LOG_TAG, "Scan results are available");
192                 notifyScanResult();
193             } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
194                 mWifiNetworkInfo =
195                         (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
196                 Log.v(LOG_TAG, "mWifiNetworkInfo: " + mWifiNetworkInfo.toString());
197                 if (mWifiNetworkInfo.getState() == State.CONNECTED) {
198                     intent.getStringExtra(WifiManager.EXTRA_BSSID);
199                 }
200                 notifyWifiState();
201             } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
202                 mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
203                         WifiManager.WIFI_STATE_UNKNOWN);
204                 notifyWifiState();
205             } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
206                 notifyWifiAPState();
207             } else {
208                 return;
209             }
210         }
211     }
212 
213     /**
214      * A wrapper of a broadcast receiver which provides download manager information.
215      */
216     private class DownloadReceiver extends BroadcastReceiver {
217         /**
218          * {@inheritDoc}
219          */
220         @Override
onReceive(Context context, Intent intent)221         public void onReceive(Context context, Intent intent) {
222             String action = intent.getAction();
223             Log.v("DownloadReceiver", "onReceive() is called with " + intent);
224             // Download complete
225             if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
226                 notifiyDownloadState();
227             }
228         }
229     }
230 
231     private class WifiServiceHandler extends Handler {
232         /**
233          * {@inheritDoc}
234          */
235         @Override
handleMessage(Message msg)236         public void handleMessage(Message msg) {
237             switch (msg.what) {
238                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
239                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
240                         // AsyncChannel in msg.obj
241                     } else {
242                         Log.v(LOG_TAG, "Failed to establish AsyncChannel connection");
243                     }
244                     break;
245                 default:
246                     // Ignore
247                     break;
248             }
249         }
250     }
251 
252     /**
253      * Initialize all the network states.
254      */
initializeNetworkStates()255     public void initializeNetworkStates() {
256         // For each network type, initialize network states to UNKNOWN, and no verification
257         // flag is set.
258         for (int networkType = NUM_NETWORK_TYPES - 1; networkType >= 0; networkType--) {
259             mConnectivityState[networkType] =  new NetworkState();
260             Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " +
261                     mConnectivityState[networkType].toString());
262         }
263     }
264 
recordNetworkState(int networkType, State networkState)265     public void recordNetworkState(int networkType, State networkState) {
266         // deposit a network state
267         Log.v(LOG_TAG, "record network state for network " +  networkType +
268                 ", state is " + networkState);
269         mConnectivityState[networkType].recordState(networkState);
270     }
271 
272     /**
273      * Set the state transition criteria
274      *
275      * @param networkType
276      * @param initState
277      * @param transitionDir
278      * @param targetState
279      */
setStateTransitionCriteria(int networkType, State initState, StateTransitionDirection transitionDir, State targetState)280     public void setStateTransitionCriteria(int networkType, State initState,
281             StateTransitionDirection transitionDir, State targetState) {
282         mConnectivityState[networkType].setStateTransitionCriteria(
283                 initState, transitionDir, targetState);
284     }
285 
286     /**
287      * Validate the states recorded.
288      * @param networkType
289      * @return
290      */
validateNetworkStates(int networkType)291     public boolean validateNetworkStates(int networkType) {
292         Log.v(LOG_TAG, "validate network state for " + networkType + ": ");
293         return mConnectivityState[networkType].validateStateTransition();
294     }
295 
296     /**
297      * Fetch the failure reason for the transition.
298      * @param networkType
299      * @return result from network state validation
300      */
getTransitionFailureReason(int networkType)301     public String getTransitionFailureReason(int networkType) {
302         Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " +
303                 mConnectivityState[networkType].toString());
304         return mConnectivityState[networkType].getFailureReason();
305     }
306 
307     /**
308      * Send a notification via the mConnectivityMonitor when the network connectivity changes.
309      */
notifyNetworkConnectivityChange()310     private void notifyNetworkConnectivityChange() {
311         synchronized(mConnectivityMonitor) {
312             Log.v(LOG_TAG, "notify network connectivity changed");
313             mConnectivityMonitor.notifyAll();
314         }
315     }
316 
317     /**
318      * Send a notification when a scan for the wifi network is done.
319      */
notifyScanResult()320     private void notifyScanResult() {
321         synchronized (this) {
322             Log.v(LOG_TAG, "notify that scan results are available");
323             this.notify();
324         }
325     }
326 
327     /**
328      * Send a notification via the mWifiMonitor when the wifi state changes.
329      */
notifyWifiState()330     private void notifyWifiState() {
331         synchronized (mWifiMonitor) {
332             Log.v(LOG_TAG, "notify wifi state changed.");
333             mWifiMonitor.notify();
334         }
335     }
336 
337     /**
338      * Send a notification via the mDownloadMonitor when a download is complete.
339      */
notifiyDownloadState()340     private void notifiyDownloadState() {
341         synchronized (mDownloadMonitor) {
342             Log.v(LOG_TAG, "notifiy download manager state changed.");
343             mDownloadMonitor.notify();
344         }
345     }
346 
347     /**
348      * Send a notification when the wifi ap state changes.
349      */
notifyWifiAPState()350     private void notifyWifiAPState() {
351         synchronized (this) {
352             Log.v(LOG_TAG, "notify wifi AP state changed.");
353             this.notify();
354         }
355     }
356 
357     /**
358      * Start a download on a given url and wait for completion.
359      *
360      * @param targetUrl the target to download.x
361      * @param timeout to wait for download to finish
362      * @return true if we successfully downloaded the requestedUrl, false otherwise.
363      */
startDownloadAndWait(String targetUrl, long timeout)364     public boolean startDownloadAndWait(String targetUrl, long timeout) {
365         if (targetUrl.length() == 0 || targetUrl == null) {
366             Log.v(LOG_TAG, "Empty or Null target url requested to DownloadManager");
367             return true;
368         }
369         Request request = new Request(Uri.parse(targetUrl));
370         long enqueue = mDownloadManager.enqueue(request);
371         Log.v(LOG_TAG, "Sending download request of " + targetUrl + " to DownloadManager");
372         long startTime = System.currentTimeMillis();
373         while (true) {
374             if ((System.currentTimeMillis() - startTime) > timeout) {
375                 Log.v(LOG_TAG, "startDownloadAndWait timed out, failed to fetch " + targetUrl +
376                         " within " + timeout);
377                 return downloadSuccessful(enqueue);
378             }
379             Log.v(LOG_TAG, "Waiting for the download to finish " + targetUrl);
380             synchronized (mDownloadMonitor) {
381                 try {
382                     mDownloadMonitor.wait(SHORT_TIMEOUT);
383                 } catch (InterruptedException e) {
384                     e.printStackTrace();
385                 }
386                 if (!downloadSuccessful(enqueue)) {
387                     continue;
388                 }
389                 return true;
390             }
391         }
392     }
393 
394     /**
395      * Fetch the Download Manager's UID.
396      * @return the Download Manager's UID
397      */
downloadManagerUid()398     public int downloadManagerUid() {
399         try {
400             PackageManager pm = mContext.getPackageManager();
401             ApplicationInfo appInfo = pm.getApplicationInfo(DOWNLOAD_MANAGER_PKG_NAME,
402                     PackageManager.GET_META_DATA);
403             return appInfo.uid;
404         } catch (NameNotFoundException e) {
405             Log.d(LOG_TAG, "Did not find the package for the download service.");
406             return -1;
407         }
408     }
409 
410     /**
411      * Determines if a given download was successful by querying the DownloadManager.
412      *
413      * @param enqueue the id used to identify/query the DownloadManager with.
414      * @return true if download was successful, false otherwise.
415      */
downloadSuccessful(long enqueue)416     private boolean downloadSuccessful(long enqueue) {
417         Query query = new Query();
418         query.setFilterById(enqueue);
419         Cursor c = mDownloadManager.query(query);
420         if (c.moveToFirst()) {
421             int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
422             if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
423                 Log.v(LOG_TAG, "Successfully downloaded file!");
424                 return true;
425             }
426         }
427         return false;
428     }
429 
430     /**
431      * Wait for network connectivity state.
432      * @param networkType the network to check for
433      * @param expectedState the desired state
434      * @param timeout in milliseconds
435      * @return true if the network connectivity state matched what was expected
436      */
waitForNetworkState(int networkType, State expectedState, long timeout)437     public boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
438         long startTime = System.currentTimeMillis();
439         while (true) {
440             if ((System.currentTimeMillis() - startTime) > timeout) {
441                 Log.v(LOG_TAG, "waitForNetworkState time out, the state of network type " + networkType +
442                         " is: " + mCM.getNetworkInfo(networkType).getState());
443                 if (mCM.getNetworkInfo(networkType).getState() != expectedState) {
444                     return false;
445                 } else {
446                     // the broadcast has been sent out. the state has been changed.
447                     Log.v(LOG_TAG, "networktype: " + networkType + " state: " +
448                             mCM.getNetworkInfo(networkType));
449                     return true;
450                 }
451             }
452             Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
453                     " to be " + expectedState.toString());
454             synchronized (mConnectivityMonitor) {
455                 try {
456                     mConnectivityMonitor.wait(SHORT_TIMEOUT);
457                 } catch (InterruptedException e) {
458                     e.printStackTrace();
459                 }
460                 if (mNetworkInfo == null) {
461                     Log.v(LOG_TAG, "Do not have networkInfo! Force fetch of network info.");
462                     mNetworkInfo = mCM.getActiveNetworkInfo();
463                 }
464                 // Still null after force fetch? Maybe the network did not have time to be brought
465                 // up yet.
466                 if (mNetworkInfo == null) {
467                     Log.v(LOG_TAG, "Failed to force fetch networkInfo. " +
468                             "The network is still not ready. Wait for the next broadcast");
469                     continue;
470                 }
471                 if ((mNetworkInfo.getType() != networkType) ||
472                         (mNetworkInfo.getState() != expectedState)) {
473                     Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() +
474                             "is: " + mNetworkInfo.getState());
475                     continue;
476                 }
477                 return true;
478             }
479         }
480     }
481 
482     /**
483      * Wait for a given wifi state to occur within a given timeout.
484      * @param expectedState the expected wifi state.
485      * @param timeout for the state to be set in milliseconds.
486      * @return true if the state was achieved within the timeout, false otherwise.
487      */
waitForWifiState(int expectedState, long timeout)488     public boolean waitForWifiState(int expectedState, long timeout) {
489         // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
490         //                      WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
491         long startTime = System.currentTimeMillis();
492         while (true) {
493             if ((System.currentTimeMillis() - startTime) > timeout) {
494                 if (mWifiState != expectedState) {
495                     return false;
496                 } else {
497                     return true;
498                 }
499             }
500             Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
501             synchronized (mWifiMonitor) {
502                 try {
503                     mWifiMonitor.wait(SHORT_TIMEOUT);
504                 } catch (InterruptedException e) {
505                     e.printStackTrace();
506                 }
507                 if (mWifiState != expectedState) {
508                     Log.v(LOG_TAG, "Wifi state is: " + mWifiState);
509                     continue;
510                 }
511                 return true;
512             }
513         }
514     }
515 
516     /**
517      * Convenience method to determine if we are connected to a mobile network.
518      * @return true if connected to a mobile network, false otherwise.
519      */
isConnectedToMobile()520     public boolean isConnectedToMobile() {
521         NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
522         return networkInfo.isConnected();
523     }
524 
525     /**
526      * Convenience method to determine if we are connected to wifi.
527      * @return true if connected to wifi, false otherwise.
528      */
isConnectedToWifi()529     public boolean isConnectedToWifi() {
530         NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
531         return networkInfo.isConnected();
532     }
533 
534     /**
535      * Associate the device to given SSID
536      * If the device is already associated with a WiFi, disconnect and forget it,
537      * We don't verify whether the connection is successful or not, leave this to the test
538      */
connectToWifi(String knownSSID)539     public boolean connectToWifi(String knownSSID) {
540         WifiConfiguration config = new WifiConfiguration();
541         config.SSID = knownSSID;
542         config.allowedKeyManagement.set(KeyMgmt.NONE);
543         return connectToWifiWithConfiguration(config);
544     }
545 
546     /**
547      * Connect to Wi-Fi with the given configuration.
548      * @param config
549      * @return true if we are connected to a given AP.
550      */
connectToWifiWithConfiguration(WifiConfiguration config)551     public boolean connectToWifiWithConfiguration(WifiConfiguration config) {
552         //  The SSID in the configuration is a pure string, need to convert it to a quoted string.
553         String ssid = config.SSID;
554         config.SSID = convertToQuotedString(ssid);
555 
556         // If wifi is not enabled, enable it
557         if (!mWifiManager.isWifiEnabled()) {
558             Log.v(LOG_TAG, "Wifi is not enabled, enable it");
559             mWifiManager.setWifiEnabled(true);
560             // wait for the wifi state change before start scanning.
561             if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, 2 * SHORT_TIMEOUT)) {
562                 Log.v(LOG_TAG, "Wait for WIFI_STATE_ENABLED failed");
563                 return false;
564             }
565         }
566 
567         boolean foundApInScanResults = false;
568         for (int retry = 0; retry < 5; retry++) {
569             List<ScanResult> netList = mWifiManager.getScanResults();
570             if (netList != null) {
571                 Log.v(LOG_TAG, "size of scan result list: " + netList.size());
572                 for (int i = 0; i < netList.size(); i++) {
573                     ScanResult sr= netList.get(i);
574                     if (sr.SSID.equals(ssid)) {
575                         Log.v(LOG_TAG, "Found " + ssid + " in the scan result list.");
576                         Log.v(LOG_TAG, "Retry: " + retry);
577                         foundApInScanResults = true;
578                         mWifiManager.connect(config, new WifiManager.ActionListener() {
579                                 public void onSuccess() {
580                                 }
581                                 public void onFailure(int reason) {
582                                     Log.e(LOG_TAG, "connect failed " + reason);
583                                 }
584                             });
585 
586                         break;
587                     }
588                 }
589             }
590             if (foundApInScanResults) {
591                 return true;
592             } else {
593                 // Start an active scan
594                 mWifiManager.startScan();
595                 mScanResultIsAvailable = false;
596                 long startTime = System.currentTimeMillis();
597                 while (!mScanResultIsAvailable) {
598                     if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
599                         Log.v(LOG_TAG, "wait for scan results timeout");
600                         return false;
601                     }
602                     // wait for the scan results to be available
603                     synchronized (this) {
604                         // wait for the scan result to be available
605                         try {
606                             this.wait(WAIT_FOR_SCAN_RESULT);
607                         } catch (InterruptedException e) {
608                             e.printStackTrace();
609                         }
610                         if ((mWifiManager.getScanResults() == null) ||
611                                 (mWifiManager.getScanResults().size() <= 0)) {
612                             continue;
613                         }
614                         mScanResultIsAvailable = true;
615                     }
616                 }
617             }
618         }
619         return false;
620     }
621 
622     /*
623      * Disconnect from the current AP and remove configured networks.
624      */
disconnectAP()625     public boolean disconnectAP() {
626         // remove saved networks
627         List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
628         Log.v(LOG_TAG, "size of wifiConfigList: " + wifiConfigList.size());
629         for (WifiConfiguration wifiConfig: wifiConfigList) {
630             Log.v(LOG_TAG, "Remove wifi configuration: " + wifiConfig.networkId);
631             int netId = wifiConfig.networkId;
632             mWifiManager.forget(netId, new WifiManager.ActionListener() {
633                     public void onSuccess() {
634                     }
635                     public void onFailure(int reason) {
636                         Log.e(LOG_TAG, "forget failed " + reason);
637                     }
638                 });
639         }
640         return true;
641     }
642 
643     /**
644      * Enable Wifi
645      * @return true if Wifi is enabled successfully
646      */
enableWifi()647     public boolean enableWifi() {
648         return mWifiManager.setWifiEnabled(true);
649     }
650 
651     /**
652      * Disable Wifi
653      * @return true if Wifi is disabled successfully
654      */
disableWifi()655     public boolean disableWifi() {
656         return mWifiManager.setWifiEnabled(false);
657     }
658 
659     /**
660      * Remove configured networks and disable wifi
661      */
removeConfiguredNetworksAndDisableWifi()662     public boolean removeConfiguredNetworksAndDisableWifi() {
663         if (!disconnectAP()) {
664             return false;
665         }
666         sleep(SHORT_TIMEOUT);
667         if (!mWifiManager.setWifiEnabled(false)) {
668             return false;
669         }
670         sleep(SHORT_TIMEOUT);
671         return true;
672     }
673 
674     /**
675      * Make the current thread sleep.
676      * @param sleeptime the time to sleep in milliseconds
677      */
sleep(long sleeptime)678     private void sleep(long sleeptime) {
679         try {
680             Thread.sleep(sleeptime);
681         } catch (InterruptedException e) {}
682     }
683 
684     /**
685      * Set airplane mode on device, caller is responsible to ensuring correct state.
686      * @param context {@link Context}
687      * @param enableAM to enable or disable airplane mode.
688      */
setAirplaneMode(Context context, boolean enableAM)689     public void setAirplaneMode(Context context, boolean enableAM) {
690         //set the airplane mode
691         Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
692                 enableAM ? 1 : 0);
693         // Post the intent
694         Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
695         intent.putExtra("state", enableAM);
696         context.sendBroadcast(intent);
697     }
698 
699     /**
700      * Add quotes around the string.
701      * @param string to convert
702      * @return string with quotes around it
703      */
convertToQuotedString(String string)704     protected static String convertToQuotedString(String string) {
705         return "\"" + string + "\"";
706     }
707 
cleanUp()708     public void cleanUp() {
709         // Unregister receivers if defined.
710         if (mConnectivityReceiver != null) {
711             mContext.unregisterReceiver(mConnectivityReceiver);
712         }
713         if (mWifiReceiver != null) {
714             mContext.unregisterReceiver(mWifiReceiver);
715         }
716         if (mDownloadReceiver != null) {
717             mContext.unregisterReceiver(mDownloadReceiver);
718         }
719         Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
720     }
721 
722     /**
723      * Helper method used to test data connectivity by pinging a series of popular sites.
724      * @return true if device has data connectivity, false otherwise.
725      */
hasData()726     public boolean hasData() {
727         String[] hostList = {"www.google.com", "www.yahoo.com",
728                 "www.bing.com", "www.facebook.com", "www.ask.com"};
729         try {
730             for (int i = 0; i < hostList.length; ++i) {
731                 String host = hostList[i];
732                 Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host);
733                 int status = p.waitFor();
734                 if (status == 0) {
735                     return true;
736                 }
737             }
738         } catch (UnknownHostException e) {
739             Log.e(LOG_TAG, "Ping test Failed: Unknown Host");
740         } catch (IOException e) {
741             Log.e(LOG_TAG, "Ping test Failed: IOException");
742         } catch (InterruptedException e) {
743             Log.e(LOG_TAG, "Ping test Failed: InterruptedException");
744         }
745         return false;
746     }
747 }
748