k-9/src/com/fsck/k9/service/MailService.java

537 lines
20 KiB
Java

package com.fsck.k9.service;
import java.util.Collection;
import java.util.Date;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.os.IBinder;
import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.Account.FolderMode;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.helper.AutoSyncHelper;
import com.fsck.k9.mail.Pusher;
/**
*/
public class MailService extends CoreService
{
private static final String ACTION_CHECK_MAIL = "com.fsck.k9.intent.action.MAIL_SERVICE_WAKEUP";
private static final String ACTION_RESET = "com.fsck.k9.intent.action.MAIL_SERVICE_RESET";
private static final String ACTION_RESCHEDULE_POLL = "com.fsck.k9.intent.action.MAIL_SERVICE_RESCHEDULE_POLL";
private static final String ACTION_CANCEL = "com.fsck.k9.intent.action.MAIL_SERVICE_CANCEL";
private static final String ACTION_REFRESH_PUSHERS = "com.fsck.k9.intent.action.MAIL_SERVICE_REFRESH_PUSHERS";
private static final String ACTION_RESTART_PUSHERS = "com.fsck.k9.intent.action.MAIL_SERVICE_RESTART_PUSHERS";
private static final String CONNECTIVITY_CHANGE = "com.fsck.k9.intent.action.MAIL_SERVICE_CONNECTIVITY_CHANGE";
private static final String CANCEL_CONNECTIVITY_NOTICE = "com.fsck.k9.intent.action.MAIL_SERVICE_CANCEL_CONNECTIVITY_NOTICE";
private static long nextCheck = -1;
public static void actionReset(Context context, Integer wakeLockId)
{
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.ACTION_RESET);
addWakeLockId(i, wakeLockId);
if (wakeLockId == null)
{
addWakeLock(context, i);
}
context.startService(i);
}
public static void actionRestartPushers(Context context, Integer wakeLockId)
{
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.ACTION_RESTART_PUSHERS);
addWakeLockId(i, wakeLockId);
if (wakeLockId == null)
{
addWakeLock(context, i);
}
context.startService(i);
}
public static void actionReschedulePoll(Context context, Integer wakeLockId)
{
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.ACTION_RESCHEDULE_POLL);
addWakeLockId(i, wakeLockId);
if (wakeLockId == null)
{
addWakeLock(context, i);
}
context.startService(i);
}
public static void actionCancel(Context context, Integer wakeLockId)
{
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.ACTION_CANCEL);
addWakeLockId(i, wakeLockId);
context.startService(i);
}
public static void connectivityChange(Context context, Integer wakeLockId)
{
Intent i = new Intent();
i.setClass(context, MailService.class);
i.setAction(MailService.CONNECTIVITY_CHANGE);
addWakeLockId(i, wakeLockId);
context.startService(i);
}
@Override
public void onCreate()
{
super.onCreate();
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: onCreate");
}
@Override
public void startService(Intent intent, int startId)
{
Integer startIdObj = startId;
long startTime = System.currentTimeMillis();
try
{
ConnectivityManager connectivityManager = (ConnectivityManager)getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
boolean doBackground = true;
boolean hasConnectivity = false;
if (connectivityManager != null)
{
NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
if (netInfo != null)
{
State state = netInfo.getState();
hasConnectivity = state == State.CONNECTED;
}
boolean backgroundData = connectivityManager.getBackgroundDataSetting();
boolean autoSync = true;
if (AutoSyncHelper.isAvailable())
{
autoSync = AutoSyncHelper.getMasterSyncAutomatically();
Log.i(K9.LOG_TAG, "AutoSync help is available, autoSync = " + autoSync);
}
K9.BACKGROUND_OPS bOps = K9.getBackgroundOps();
switch (bOps)
{
case NEVER:
doBackground = false;
break;
case ALWAYS:
doBackground = true;
break;
case WHEN_CHECKED:
doBackground = backgroundData;
break;
case WHEN_CHECKED_AUTO_SYNC:
doBackground = backgroundData & autoSync;
break;
}
}
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "MailService.onStart(" + intent + ", " + startId
+ "), hasConnectivity = " + hasConnectivity + ", doBackground = " + doBackground);
// MessagingController.getInstance(getApplication()).addListener(mListener);
if (ACTION_CHECK_MAIL.equals(intent.getAction()))
{
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "***** MailService *****: checking mail");
if (hasConnectivity && doBackground)
{
PollService.startService(this);
}
reschedulePoll(hasConnectivity, doBackground, startIdObj, false);
startIdObj = null;
}
else if (ACTION_CANCEL.equals(intent.getAction()))
{
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: cancel");
cancel();
}
else if (ACTION_RESET.equals(intent.getAction()))
{
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: reschedule");
rescheduleAll(hasConnectivity, doBackground, startIdObj);
startIdObj = null;
}
else if (ACTION_RESTART_PUSHERS.equals(intent.getAction()))
{
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: restarting pushers");
reschedulePushers(hasConnectivity, doBackground, startIdObj);
startIdObj = null;
}
else if (ACTION_RESCHEDULE_POLL.equals(intent.getAction()))
{
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: rescheduling poll");
reschedulePoll(hasConnectivity, doBackground, startIdObj, true);
startIdObj = null;
}
else if (ACTION_REFRESH_PUSHERS.equals(intent.getAction()))
{
if (hasConnectivity && doBackground)
{
refreshPushers(null);
schedulePushers(startIdObj);
startIdObj = null;
}
}
else if (CONNECTIVITY_CHANGE.equals(intent.getAction()))
{
notifyConnectionStatus(hasConnectivity);
rescheduleAll(hasConnectivity, doBackground, startIdObj);
startIdObj = null;
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Got connectivity action with hasConnectivity = " + hasConnectivity + ", doBackground = " + doBackground);
}
else if (CANCEL_CONNECTIVITY_NOTICE.equals(intent.getAction()))
{
notifyConnectionStatus(true);
}
}
finally
{
if (startIdObj != null)
{
stopSelf(startId);
}
}
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "MailService.onStart took " + (System.currentTimeMillis() - startTime) + "ms");
}
private void rescheduleAll(final boolean hasConnectivity, final boolean doBackground, final Integer startId)
{
reschedulePoll(hasConnectivity, doBackground, null, true);
reschedulePushers(hasConnectivity, doBackground, startId);
}
private void notifyConnectionStatus(boolean hasConnectivity)
{
if (true) return;
NotificationManager notifMgr =
(NotificationManager)getApplication().getSystemService(Context.NOTIFICATION_SERVICE);
if (hasConnectivity == false)
{
String notice = getApplication().getString(R.string.no_connection_alert);
String header = getApplication().getString(R.string.alert_header);
Notification notif = new Notification(R.drawable.stat_notify_email_generic,
header, System.currentTimeMillis());
Intent i = new Intent();
i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService");
i.setAction(MailService.CANCEL_CONNECTIVITY_NOTICE);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
notif.setLatestEventInfo(getApplication(), header, notice, pi);
notif.flags = Notification.FLAG_ONGOING_EVENT;
notifMgr.notify(K9.CONNECTIVITY_ID, notif);
}
else
{
notifMgr.cancel(K9.CONNECTIVITY_ID);
}
}
@Override
public void onDestroy()
{
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "***** MailService *****: onDestroy()");
super.onDestroy();
// MessagingController.getInstance(getApplication()).removeListener(mListener);
}
private void cancel()
{
Intent i = new Intent();
i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService");
i.setAction(ACTION_CHECK_MAIL);
BootReceiver.cancelIntent(this, i);
}
private final static String PREVIOUS_INTERVAL = "MailService.previousInterval";
private final static String LAST_CHECK_END = "MailService.lastCheckEnd";
public static void saveLastCheckEnd(Context context)
{
long lastCheckEnd = System.currentTimeMillis();
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Saving lastCheckEnd = " + new Date(lastCheckEnd));
Preferences prefs = Preferences.getPreferences(context);
SharedPreferences sPrefs = prefs.getPreferences();
SharedPreferences.Editor editor = sPrefs.edit();
editor.putLong(LAST_CHECK_END, lastCheckEnd);
editor.commit();
}
private void reschedulePoll(final boolean hasConnectivity, final boolean doBackground, Integer startId, final boolean considerLastCheckEnd)
{
if (hasConnectivity && doBackground)
{
execute(getApplication(), new Runnable()
{
public void run()
{
int shortestInterval = -1;
Preferences prefs = Preferences.getPreferences(MailService.this);
SharedPreferences sPrefs = prefs.getPreferences();
int previousInterval = sPrefs.getInt(PREVIOUS_INTERVAL, -1);
long lastCheckEnd = sPrefs.getLong(LAST_CHECK_END, -1);
for (Account account : prefs.getAccounts())
{
if (account.getAutomaticCheckIntervalMinutes() != -1
&& account.getFolderSyncMode() != FolderMode.NONE
&& (account.getAutomaticCheckIntervalMinutes() < shortestInterval || shortestInterval == -1))
{
shortestInterval = account.getAutomaticCheckIntervalMinutes();
}
}
SharedPreferences.Editor editor = sPrefs.edit();
editor.putInt(PREVIOUS_INTERVAL, shortestInterval);
editor.commit();
if (shortestInterval == -1)
{
nextCheck = -1;
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "No next check scheduled for package " + getApplication().getPackageName());
cancel();
}
else
{
long delay = (shortestInterval * (60 * 1000));
long base = (previousInterval == -1 || lastCheckEnd == -1 || considerLastCheckEnd == false ? System.currentTimeMillis() : lastCheckEnd);
long nextTime = base + delay;
if (K9.DEBUG)
Log.i(K9.LOG_TAG,
"previousInterval = " + previousInterval
+ ", shortestInterval = " + shortestInterval
+ ", lastCheckEnd = " + new Date(lastCheckEnd)
+ ", considerLastCheckEnd = " + considerLastCheckEnd);
nextCheck = nextTime;
try
{
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Next check for package " + getApplication().getPackageName() + " scheduled for " + new Date(nextTime));
}
catch (Exception e)
{
// I once got a NullPointerException deep in new Date();
Log.e(K9.LOG_TAG, "Exception while logging", e);
}
Intent i = new Intent();
i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService");
i.setAction(ACTION_CHECK_MAIL);
BootReceiver.scheduleIntent(MailService.this, nextTime, i);
}
}
}
, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
}
else
{
nextCheck = -1;
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "No connectivity, canceling check for " + getApplication().getPackageName());
cancel();
}
}
private void stopPushers(final Integer startId)
{
execute(getApplication(), new Runnable()
{
public void run()
{
MessagingController.getInstance(getApplication()).stopAllPushing();
PushService.stopService(MailService.this);
}
}
, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
}
private void reschedulePushers(final boolean hasConnectivity, final boolean doBackground, final Integer startId)
{
execute(getApplication(), new Runnable()
{
public void run()
{
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Rescheduling pushers");
stopPushers(null);
if (hasConnectivity && doBackground)
{
setupPushers(null);
schedulePushers(startId);
}
}
}
, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, null);
}
private void setupPushers(final Integer startId)
{
execute(getApplication(), new Runnable()
{
public void run()
{
boolean pushing = false;
for (Account account : Preferences.getPreferences(MailService.this).getAccounts())
{
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Setting up pushers for account " + account.getDescription());
pushing |= MessagingController.getInstance(getApplication()).setupPushing(account);
}
if (pushing)
{
PushService.startService(MailService.this);
}
}
}
, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
}
private void refreshPushers(final Integer startId)
{
execute(getApplication(), new Runnable()
{
public void run()
{
try
{
long nowTime = System.currentTimeMillis();
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Refreshing pushers");
Collection<Pusher> pushers = MessagingController.getInstance(getApplication()).getPushers();
for (Pusher pusher : pushers)
{
long lastRefresh = pusher.getLastRefresh();
int refreshInterval = pusher.getRefreshInterval();
long sinceLast = nowTime - lastRefresh;
if (sinceLast + 10000 > refreshInterval) // Add 10 seconds to keep pushers in sync, avoid drift
{
if (K9.DEBUG)
{
Log.d(K9.LOG_TAG, "PUSHREFRESH: refreshing lastRefresh = " + lastRefresh + ", interval = " + refreshInterval
+ ", nowTime = " + nowTime + ", sinceLast = " + sinceLast);
}
pusher.refresh();
pusher.setLastRefresh(nowTime);
}
else
{
if (K9.DEBUG)
{
Log.d(K9.LOG_TAG, "PUSHREFRESH: NOT refreshing lastRefresh = " + lastRefresh + ", interval = " + refreshInterval
+ ", nowTime = " + nowTime + ", sinceLast = " + sinceLast);
}
}
}
}
catch (Exception e)
{
Log.e(K9.LOG_TAG, "Exception while refreshing pushers", e);
}
}
}
, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
}
private void schedulePushers(final Integer startId)
{
execute(getApplication(), new Runnable()
{
public void run()
{
int minInterval = -1;
Collection<Pusher> pushers = MessagingController.getInstance(getApplication()).getPushers();
for (Pusher pusher : pushers)
{
int interval = pusher.getRefreshInterval();
if (interval > 0 && (interval < minInterval || minInterval == -1))
{
minInterval = interval;
}
}
if (K9.DEBUG)
{
Log.v(K9.LOG_TAG, "Pusher refresh interval = " + minInterval);
}
if (minInterval > 0)
{
long nextTime = System.currentTimeMillis() + minInterval;
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "Next pusher refresh scheduled for " + new Date(nextTime));
Intent i = new Intent();
i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.MailService");
i.setAction(ACTION_REFRESH_PUSHERS);
BootReceiver.scheduleIntent(MailService.this, nextTime, i);
}
}
}
, K9.MAIL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
}
@Override
public IBinder onBind(Intent intent)
{
return null;
}
public static long getNextPollTime()
{
return nextCheck;
}
}