EmailConnectivityManager.java revision 81273dfcee3b075451860f60ee15f2aa06ba81ec
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    // The name of this manager (used for logging)
49    private final String mName;
50    // The monitor lock we use while waiting for connectivity
51    private final Object mLock = new Object();
52    // The instantiator's context
53    private final Context mContext;
54    // The wake lock used while running (so we don't fall asleep during execution/callbacks)
55    private final WakeLock mWakeLock;
56    private final android.net.ConnectivityManager mConnectivityManager;
57
58    // Set when we abort waitForConnectivity() via stopWait
59    private boolean mStop = false;
60    // The thread waiting for connectivity
61    private Thread mWaitThread;
62    // Whether or not we're registered with the system connectivity manager
63    private boolean mRegistered = true;
64
65    public EmailConnectivityManager(Context context, String name)  {
66        mContext = context;
67        mName = name;
68        mConnectivityManager =
69            (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
70        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
71        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
72        mContext.registerReceiver(this, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
73    }
74
75    public boolean isBackgroundDataAllowed() {
76        return mConnectivityManager.getBackgroundDataSetting();
77    }
78
79    public void stopWait() {
80        mStop = true;
81        Thread thread= mWaitThread;
82        if (thread != null) {
83            thread.interrupt();
84        }
85    }
86
87    /**
88     * Called when network connectivity has been restored; this method should be overridden by
89     * subclasses as necessary. NOTE: CALLED ON UI THREAD
90     * @param networkType as defined by ConnectivityManager
91     */
92    public void onConnectivityRestored(int networkType) {
93    }
94
95    /**
96     * Called when network connectivity has been lost; this method should be overridden by
97     * subclasses as necessary. NOTE: CALLED ON UI THREAD
98     * @param networkType as defined by ConnectivityManager
99     */
100    public void onConnectivityLost(int networkType) {
101    }
102
103    /**
104     * Called when the user changes the state of the "Background Data" setting; this method should
105     * be overridden by subclasses as necessary.  NOTE: CALLED ON UI THREAD
106     * @param state the new state of the "Background Data" setting
107     */
108    public void onBackgroundDataChanged(boolean state) {
109    }
110
111    public void unregister() {
112        try {
113            mContext.unregisterReceiver(this);
114        } catch (RuntimeException e) {
115            // Don't crash if we didn't register
116        } finally {
117            mRegistered = false;
118        }
119    }
120
121    @Override
122    public void onReceive(Context context, Intent intent) {
123        if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
124            Bundle extras = intent.getExtras();
125            if (extras != null) {
126                NetworkInfo networkInfo =
127                    (NetworkInfo)extras.get(ConnectivityManager.EXTRA_NETWORK_INFO);
128                if (networkInfo == null) return;
129                State state = networkInfo.getState();
130                if (state == State.CONNECTED) {
131                    synchronized (mLock) {
132                        mLock.notifyAll();
133                    }
134                    onConnectivityRestored(networkInfo.getType());
135                } else if (state == State.DISCONNECTED) {
136                    onConnectivityLost(networkInfo.getType());
137                }
138            }
139        } else if (intent.getAction().equals(
140                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED)) {
141            onBackgroundDataChanged(isBackgroundDataAllowed());
142        }
143    }
144
145    /**
146     * Request current connectivity status
147     * @return whether there is connectivity at this time
148     */
149    public boolean hasConnectivity() {
150        NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
151        return (info != null);
152    }
153
154    public void waitForConnectivity() {
155        // If we're unregistered, throw an exception
156        if (!mRegistered) {
157            throw new IllegalStateException("ConnectivityManager not registered");
158        }
159        boolean waiting = false;
160        mWaitThread = Thread.currentThread();
161        // Acquire the wait lock while we work
162        mWakeLock.acquire();
163        try {
164            while (!mStop) {
165                NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
166                if (info != null) {
167                    // We're done if there's an active network
168                    if (waiting) {
169                        if (Email.DEBUG) {
170                            Log.d(TAG, mName + ": Connectivity wait ended");
171                        }
172                    }
173                    return;
174                } else {
175                    if (!waiting) {
176                        if (Email.DEBUG) {
177                            Log.d(TAG, mName + ": Connectivity waiting...");
178                        }
179                        waiting = true;
180                    }
181                    // Wait until a network is connected (or 10 mins), but let the device sleep
182                    synchronized (mLock) {
183                        // Don't hold a lock during our wait
184                        mWakeLock.release();
185                        try {
186                            mLock.wait(CONNECTIVITY_WAIT_TIME);
187                        } catch (InterruptedException e) {
188                            // This is fine; we just go around the loop again
189                        }
190                        // Get the lock back and check again for connectivity
191                        mWakeLock.acquire();
192                    }
193                }
194            }
195        } finally {
196            // Make sure we always release the wait lock
197            if (mWakeLock.isHeld()) {
198                mWakeLock.release();
199            }
200            mWaitThread = null;
201        }
202    }
203}
204