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