Home | History | Annotate | Download | only in email
      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 
     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 
     80     public boolean isAutoSyncAllowed() {
     81         return ContentResolver.getMasterSyncAutomatically();
     82     }
     83 
     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      */
     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      */
    105     public void onConnectivityLost(int networkType) {
    106     }
    107 
    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
    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      */
    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      */
    152     public int getActiveNetworkType() {
    153         return getActiveNetworkType(mConnectivityManager);
    154     }
    155 
    156     static public int getActiveNetworkType(Context context) {
    157         ConnectivityManager cm =
    158             (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
    159         return getActiveNetworkType(cm);
    160     }
    161 
    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 
    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