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.email2.ui.MailActivityEmail;
     32 import 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  */
     45 public class EmailConnectivityManager extends BroadcastReceiver {
     46     private static final String TAG = "EmailConnectivityMgr";
     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