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