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