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