am 3ef8f54b: Prefetch respects "background data" setting and waits for connectivity

* commit '3ef8f54bae6a3e02919cfd7add7ed6bf7fdda901':
  Prefetch respects "background data" setting and waits for connectivity
This commit is contained in:
Marc Blank 2011-01-24 19:32:48 -08:00 committed by Android Git Automerger
commit b802246f99
2 changed files with 205 additions and 6 deletions

View File

@ -0,0 +1,194 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;
/**
* Encapsulates functionality of ConnectivityManager for use in the Email application. In
* particular, this class provides callbacks for connectivity lost, connectivity restored, and
* background setting changed, as well as providing a method that waits for connectivity
* to be available without holding a wake lock
*
* To use, EmailConnectivityManager mgr = new EmailConnectivityManager(context, "Name");
* When done, mgr.unregister() to unregister the internal receiver
*
* TODO: Use this class in ExchangeService
*/
public class EmailConnectivityManager extends BroadcastReceiver {
private static final String TAG = "EmailConnectivityManager";
// Loop time while waiting (stopgap in case we don't get a broadcast)
private static final int CONNECTIVITY_WAIT_TIME = 10*60*1000;
// The name of this manager (used for logging)
private final String mName;
// The monitor lock we use while waiting for connectivity
private final Object mLock = new Object();
// The instantiator's context
private final Context mContext;
// The wake lock used while running (so we don't fall asleep during execution/callbacks)
private final WakeLock mWakeLock;
private final android.net.ConnectivityManager mConnectivityManager;
// Set when we abort waitForConnectivity() via stopWait
private boolean mStop = false;
// The thread waiting for connectivity
private Thread mWaitThread;
// Whether or not we're registered with the system connectivity manager
private boolean mRegistered = true;
public EmailConnectivityManager(Context context, String name) {
mContext = context;
mName = name;
mConnectivityManager =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
mContext.registerReceiver(this, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
public boolean isBackgroundDataAllowed() {
return mConnectivityManager.getBackgroundDataSetting();
}
public void stopWait() {
mStop = true;
Thread thread= mWaitThread;
if (thread != null) {
thread.interrupt();
}
}
/**
* Called when network connectivity has been restored; this method should be overridden by
* subclasses as necessary. NOTE: CALLED ON UI THREAD
* @param networkType as defined by ConnectivityManager
*/
public void onConnectivityRestored(int networkType) {
}
/**
* Called when network connectivity has been lost; this method should be overridden by
* subclasses as necessary. NOTE: CALLED ON UI THREAD
* @param networkType as defined by ConnectivityManager
*/
public void onConnectivityLost(int networkType) {
}
/**
* Called when the user changes the state of the "Background Data" setting; this method should
* be overridden by subclasses as necessary. NOTE: CALLED ON UI THREAD
* @param state the new state of the "Background Data" setting
*/
public void onBackgroundDataChanged(boolean state) {
}
public void unregister() {
try {
mContext.unregisterReceiver(this);
} catch (RuntimeException e) {
// Don't crash if we didn't register
} finally {
mRegistered = false;
}
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
Bundle extras = intent.getExtras();
if (extras != null) {
NetworkInfo networkInfo =
(NetworkInfo)extras.get(ConnectivityManager.EXTRA_NETWORK_INFO);
if (networkInfo == null) return;
State state = networkInfo.getState();
if (state == State.CONNECTED) {
synchronized (mLock) {
mLock.notifyAll();
}
onConnectivityRestored(networkInfo.getType());
} else if (state == State.DISCONNECTED) {
onConnectivityLost(networkInfo.getType());
}
}
} else if (intent.getAction().equals(
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED)) {
onBackgroundDataChanged(isBackgroundDataAllowed());
}
}
public void waitForConnectivity() {
// If we're unregistered, throw an exception
if (!mRegistered) {
throw new IllegalStateException("ConnectivityManager not registered");
}
boolean waiting = false;
mWaitThread = Thread.currentThread();
// Acquire the wait lock while we work
mWakeLock.acquire();
try {
while (!mStop) {
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
if (info != null) {
// We're done if there's an active network
if (waiting) {
if (Email.DEBUG) {
Log.d(TAG, mName + ": Connectivity wait ended");
}
}
return;
} else {
if (!waiting) {
if (Email.DEBUG) {
Log.d(TAG, mName + ": Connectivity waiting...");
}
waiting = true;
}
// Wait until a network is connected (or 10 mins), but let the device sleep
synchronized (mLock) {
// Don't hold a lock during our wait
mWakeLock.release();
try {
mLock.wait(CONNECTIVITY_WAIT_TIME);
} catch (InterruptedException e) {
// This is fine; we just go around the loop again
}
// Get the lock back and check again for connectivity
mWakeLock.acquire();
}
}
}
} finally {
// Make sure we always release the wait lock
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
mWaitThread = null;
}
}
}

View File

@ -19,9 +19,9 @@ package com.android.email.service;
import com.android.email.AttachmentInfo;
import com.android.email.Controller.ControllerService;
import com.android.email.Email;
import com.android.email.EmailConnectivityManager;
import com.android.email.ExchangeUtils.NullEmailService;
import com.android.email.NotificationController;
import com.android.email.Preferences;
import com.android.email.Utility;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent;
@ -91,7 +91,8 @@ public class AttachmentDownloadService extends Service implements Runnable {
/*package*/ static AttachmentDownloadService sRunningService = null;
/*package*/ Context mContext;
private final Preferences mPreferences;
private EmailConnectivityManager mConnectivityManager;
/*package*/ final DownloadSet mDownloadSet = new DownloadSet(new DownloadComparator());
private final HashMap<Long, Class<? extends Service>> mAccountServiceMap =
@ -305,6 +306,10 @@ public class AttachmentDownloadService extends Service implements Runnable {
if (Email.DEBUG) {
Log.d(TAG, "== Checking attachment queue, " + mDownloadSet.size() + " entries");
}
// Don't run unless/until we have connectivity
mConnectivityManager.waitForConnectivity();
Iterator<DownloadRequest> iterator = mDownloadSet.descendingIterator();
// First, start up any required downloads, in priority order
while (iterator.hasNext() &&
@ -324,6 +329,8 @@ public class AttachmentDownloadService extends Service implements Runnable {
}
}
// Don't prefetch if background downloading is disallowed
if (!mConnectivityManager.isBackgroundDataAllowed()) return;
// Then, try opportunistic download of appropriate attachments
int backgroundDownloads = MAX_SIMULTANEOUS_DOWNLOADS - mDownloadsInProgress.size();
// Always leave one slot for user requested download
@ -582,10 +589,6 @@ public class AttachmentDownloadService extends Service implements Runnable {
}
}
public AttachmentDownloadService() {
mPreferences = Preferences.getPreferences(this);
}
/**
* Calculate the download priority of an Attachment. A priority of zero means that the
* attachment is not marked for download.
@ -884,6 +887,7 @@ public class AttachmentDownloadService extends Service implements Runnable {
public void onCreate() {
// Start up our service thread
new Thread(this, "AttachmentDownloadService").start();
mConnectivityManager = new EmailConnectivityManager(this, TAG);
}
@Override
public IBinder onBind(Intent intent) {
@ -898,6 +902,7 @@ public class AttachmentDownloadService extends Service implements Runnable {
kick();
}
sRunningService = null;
mConnectivityManager.unregister();
}
@Override