EmailConnectivityManager.java revision 973702b30e8c2fb2f622f4ef37b42b3bdbd3ef17
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.email;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.net.ConnectivityManager;
24import android.net.NetworkInfo;
25import android.net.NetworkInfo.State;
26import android.os.Bundle;
27import android.os.PowerManager;
28import android.os.PowerManager.WakeLock;
29import android.util.Log;
30
31/**
32 * Encapsulates functionality of ConnectivityManager for use in the Email application.  In
33 * particular, this class provides callbacks for connectivity lost, connectivity restored, and
34 * background setting changed, as well as providing a method that waits for connectivity
35 * to be available without holding a wake lock
36 *
37 * To use, EmailConnectivityManager mgr = new EmailConnectivityManager(context, "Name");
38 * When done, mgr.unregister() to unregister the internal receiver
39 *
40 * TODO: Use this class in ExchangeService
41 */
42public class EmailConnectivityManager extends BroadcastReceiver {
43    private static final String TAG = "EmailConnectivityManager";
44
45    // Loop time while waiting (stopgap in case we don't get a broadcast)
46    private static final int CONNECTIVITY_WAIT_TIME = 10*60*1000;
47
48    // Sentinel value for "no active network"
49    public static final int NO_ACTIVE_NETWORK = -1;
50
51    // The name of this manager (used for logging)
52    private final String mName;
53    // The monitor lock we use while waiting for connectivity
54    private final Object mLock = new Object();
55    // The instantiator's context
56    private final Context mContext;
57    // The wake lock used while running (so we don't fall asleep during execution/callbacks)
58    private final WakeLock mWakeLock;
59    private final android.net.ConnectivityManager mConnectivityManager;
60
61    // Set when we abort waitForConnectivity() via stopWait
62    private boolean mStop = false;
63    // The thread waiting for connectivity
64    private Thread mWaitThread;
65    // Whether or not we're registered with the system connectivity manager
66    private boolean mRegistered = true;
67
68    public EmailConnectivityManager(Context context, String name)  {
69        mContext = context;
70        mName = name;
71        mConnectivityManager =
72            (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
73        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
74        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
75        mContext.registerReceiver(this, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
76    }
77
78    public boolean isBackgroundDataAllowed() {
79        return mConnectivityManager.getBackgroundDataSetting();
80    }
81
82    public void stopWait() {
83        mStop = true;
84        Thread thread= mWaitThread;
85        if (thread != null) {
86            thread.interrupt();
87        }
88    }
89
90    /**
91     * Called when network connectivity has been restored; this method should be overridden by
92     * subclasses as necessary. NOTE: CALLED ON UI THREAD
93     * @param networkType as defined by ConnectivityManager
94     */
95    public void onConnectivityRestored(int networkType) {
96    }
97
98    /**
99     * Called when network connectivity has been lost; this method should be overridden by
100     * subclasses as necessary. NOTE: CALLED ON UI THREAD
101     * @param networkType as defined by ConnectivityManager
102     */
103    public void onConnectivityLost(int networkType) {
104    }
105
106    /**
107     * Called when the user changes the state of the "Background Data" setting; this method should
108     * be overridden by subclasses as necessary.  NOTE: CALLED ON UI THREAD
109     * @param state the new state of the "Background Data" setting
110     */
111    public void onBackgroundDataChanged(boolean state) {
112    }
113
114    public void unregister() {
115        try {
116            mContext.unregisterReceiver(this);
117        } catch (RuntimeException e) {
118            // Don't crash if we didn't register
119        } finally {
120            mRegistered = false;
121        }
122    }
123
124    @Override
125    public void onReceive(Context context, Intent intent) {
126        if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
127            Bundle extras = intent.getExtras();
128            if (extras != null) {
129                NetworkInfo networkInfo =
130                    (NetworkInfo)extras.get(ConnectivityManager.EXTRA_NETWORK_INFO);
131                if (networkInfo == null) return;
132                State state = networkInfo.getState();
133                if (state == State.CONNECTED) {
134                    synchronized (mLock) {
135                        mLock.notifyAll();
136                    }
137                    onConnectivityRestored(networkInfo.getType());
138                } else if (state == State.DISCONNECTED) {
139                    onConnectivityLost(networkInfo.getType());
140                }
141            }
142        } else if (intent.getAction().equals(
143                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED)) {
144            onBackgroundDataChanged(isBackgroundDataAllowed());
145        }
146    }
147
148    /**
149     * Request current connectivity status
150     * @return whether there is connectivity at this time
151     */
152    public boolean hasConnectivity() {
153        NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
154        return (info != null);
155    }
156
157    /**
158     * Get the type of the currently active data network
159     * @return the type of the active network (or NO_ACTIVE_NETWORK)
160     */
161    public int getActiveNetworkType() {
162        return getActiveNetworkType(mConnectivityManager);
163    }
164
165    static public int getActiveNetworkType(Context context) {
166        ConnectivityManager cm =
167            (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
168        return getActiveNetworkType(cm);
169    }
170
171    static public int getActiveNetworkType(ConnectivityManager cm) {
172        NetworkInfo info = cm.getActiveNetworkInfo();
173        if (info == null) return NO_ACTIVE_NETWORK;
174        return info.getType();
175    }
176
177    public void waitForConnectivity() {
178        // If we're unregistered, throw an exception
179        if (!mRegistered) {
180            throw new IllegalStateException("ConnectivityManager not registered");
181        }
182        boolean waiting = false;
183        mWaitThread = Thread.currentThread();
184        // Acquire the wait lock while we work
185        mWakeLock.acquire();
186        try {
187            while (!mStop) {
188                NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
189                if (info != null) {
190                    // We're done if there's an active network
191                    if (waiting) {
192                        if (Email.DEBUG) {
193                            Log.d(TAG, mName + ": Connectivity wait ended");
194                        }
195                    }
196                    return;
197                } else {
198                    if (!waiting) {
199                        if (Email.DEBUG) {
200                            Log.d(TAG, mName + ": Connectivity waiting...");
201                        }
202                        waiting = true;
203                    }
204                    // Wait until a network is connected (or 10 mins), but let the device sleep
205                    synchronized (mLock) {
206                        // Don't hold a lock during our wait
207                        mWakeLock.release();
208                        try {
209                            mLock.wait(CONNECTIVITY_WAIT_TIME);
210                        } catch (InterruptedException e) {
211                            // This is fine; we just go around the loop again
212                        }
213                        // Get the lock back and check again for connectivity
214                        mWakeLock.acquire();
215                    }
216                }
217            }
218        } finally {
219            // Make sure we always release the wait lock
220            if (mWakeLock.isHeld()) {
221                mWakeLock.release();
222            }
223            mWaitThread = null;
224        }
225    }
226}
227