| /* |
| * Copyright (C) 2008-2009 Marc Blank |
| * Licensed to 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.exchange; |
| |
| import android.app.Service; |
| import android.content.Intent; |
| import android.os.IBinder; |
| import android.util.Log; |
| |
| import java.util.ArrayList; |
| import java.util.GregorianCalendar; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| import com.android.email.mail.MessagingException; |
| import com.android.exchange.EmailContent.Attachment; |
| import com.android.exchange.EmailContent.Account; |
| import com.android.exchange.EmailContent.HostAuth; |
| import com.android.exchange.EmailContent.Mailbox; |
| import com.android.exchange.EmailContent.Message; |
| |
| import android.app.AlarmManager; |
| import android.app.PendingIntent; |
| import android.content.BroadcastReceiver; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.IntentFilter; |
| import android.content.SharedPreferences; |
| import android.database.Cursor; |
| import android.net.ConnectivityManager; |
| import android.net.NetworkInfo; |
| import android.net.Uri; |
| import android.net.NetworkInfo.State; |
| import android.os.Bundle; |
| import android.os.Debug; |
| import android.os.Handler; |
| import android.os.PowerManager; |
| import android.os.RemoteCallbackList; |
| import android.os.RemoteException; |
| import android.os.PowerManager.WakeLock; |
| import android.preference.PreferenceManager; |
| import android.database.ContentObserver; |
| |
| public class SyncManager extends Service implements Runnable { |
| |
| public static final int AWAKE = 0; |
| public static final int SLEEP_WEEKEND = 1; |
| public static final int SLEEP_HOURS = 2; |
| public static final int OFFLINE = 3; |
| |
| public static final int DEFAULT_WINDOW = Integer.MIN_VALUE; |
| |
| public static final int SECS = 1000; |
| public static final int MINS = 60*SECS; |
| |
| static SyncManager INSTANCE; |
| static int mStatus = AWAKE; |
| static boolean mToothpicks = false; |
| static Object mSyncToken = new Object(); |
| static Thread mServiceThread = null; |
| |
| HashMap<Long, ProtocolService> serviceMap = new HashMap<Long, ProtocolService> (); |
| boolean mStop = false; |
| SharedPreferences mSettings; |
| Handler mHandler = new Handler(); |
| AccountObserver mAccountObserver; |
| MailboxObserver mMailboxObserver; |
| |
| final RemoteCallbackList<ISyncManagerCallback> mCallbacks |
| = new RemoteCallbackList<ISyncManagerCallback>(); |
| |
| private final ISyncManager.Stub mBinder = new ISyncManager.Stub() { |
| public int validate(String protocol, String host, String userName, String password, |
| int port, boolean ssl) throws RemoteException { |
| try { |
| ProtocolService.validate(EasService.class, host, userName, password, port, ssl, |
| SyncManager.this); |
| return MessagingException.NO_ERROR; |
| } catch (MessagingException e) { |
| return e.getExceptionType(); |
| } |
| } |
| public void registerCallback(ISyncManagerCallback cb) { |
| if (cb != null) mCallbacks.register(cb); |
| } |
| public void unregisterCallback(ISyncManagerCallback cb) { |
| if (cb != null) mCallbacks.unregister(cb); |
| } |
| public boolean startSync(long mailboxId) throws RemoteException { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| public boolean stopSync(long mailboxId) throws RemoteException { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| public boolean updateFolderList(long accountId) throws RemoteException { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| public boolean loadMore(long messageId, ISyncManagerCallback cb) throws RemoteException { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| public boolean createFolder(long accountId, String name) throws RemoteException { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| public boolean deleteFolder(long accountId, String name) throws RemoteException { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| public boolean renameFolder(long accountId, String oldName, String newName) |
| throws RemoteException { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| public boolean loadAttachment(long messageId, Attachment att, ISyncManagerCallback cb) |
| throws RemoteException { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| }; |
| |
| class AccountObserver extends ContentObserver { |
| ArrayList<Long> mAccountIds = new ArrayList<Long>(); |
| |
| public AccountObserver(Handler handler) { |
| super(handler); |
| Context context = getContext(); |
| |
| // At startup, we want to see what EAS accounts exist and cache them |
| Cursor c = getContentResolver().query(Account.CONTENT_URI, |
| Account.CONTENT_PROJECTION, null, null, null); |
| try { |
| collectEasAccounts(c, mAccountIds); |
| } finally { |
| c.close(); |
| } |
| |
| for (long accountId: mAccountIds) { |
| int cnt = Mailbox.count(context, Mailbox.CONTENT_URI, |
| "accountKey=" + accountId, null); |
| if (cnt == 0) { |
| initializeAccount(accountId); |
| } |
| } |
| } |
| |
| public void onChange (boolean selfChange) { |
| // A change to the list requires us to scan for deletions (to stop running syncs) |
| // At startup, we want to see what accounts exist and cache them |
| ArrayList<Long> currentIds = new ArrayList<Long>(); |
| Cursor c = getContentResolver().query(Account.CONTENT_URI, |
| Account.CONTENT_PROJECTION, null, null, null); |
| try { |
| collectEasAccounts(c, currentIds); |
| for (long accountId: mAccountIds) { |
| if (!currentIds.contains(accountId)) { |
| // This is a deletion; shut down any account-related syncs |
| accountDeleted(accountId); |
| } |
| } |
| for (long accountId: currentIds) { |
| if (!mAccountIds.contains(accountId)) { |
| // This is an addition; create our magic hidden mailbox... |
| initializeAccount(accountId); |
| mAccountIds.add(accountId); |
| } |
| } |
| } finally { |
| c.close(); |
| } |
| |
| // See if there's anything to do... |
| kick(); |
| } |
| |
| private void collectEasAccounts (Cursor c, ArrayList<Long> ids) { |
| Context context = getContext(); |
| while (c.moveToNext()) { |
| long hostAuthId = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN); |
| if (hostAuthId > 0) { |
| HostAuth ha = HostAuth.restoreHostAuthWithId(context, hostAuthId); |
| if (ha != null && ha.mProtocol.equals("eas")) { |
| ids.add(c.getLong(Account.CONTENT_ID_COLUMN)); |
| } |
| } |
| } |
| } |
| |
| private void initializeAccount (long acctId) { |
| Account acct = Account.restoreAccountWithId(getContext(), acctId); |
| Mailbox main = new Mailbox(); |
| main.mDisplayName = "_main"; |
| main.mServerId = "_main"; |
| main.mAccountKey = acct.mId; |
| main.mType = Mailbox.TYPE_MAIL; |
| main.mSyncFrequency = Account.CHECK_INTERVAL_PUSH; |
| main.mFlagVisible = false; |
| main.save(getContext()); |
| INSTANCE.log("Initializing account: " + acct.mDisplayName); |
| } |
| |
| private void accountDeleted (long acctId) { |
| synchronized (mSyncToken) { |
| List<Long> deletedBoxes = new ArrayList<Long>(); |
| for (Long mid : INSTANCE.serviceMap.keySet()) { |
| Mailbox box = |
| Mailbox.restoreMailboxWithId(INSTANCE, mid); |
| if (box != null) { |
| if (box.mAccountKey == acctId) { |
| ProtocolService svc = INSTANCE.serviceMap.get(mid); |
| if (svc != null) { |
| svc.stop(); |
| svc.mThread.interrupt(); |
| } |
| deletedBoxes.add(mid); |
| } |
| } |
| } |
| for (Long mid : deletedBoxes) { |
| INSTANCE.serviceMap.remove(mid); |
| } |
| } |
| } |
| } |
| |
| class MailboxObserver extends ContentObserver { |
| public MailboxObserver(Handler handler) { |
| super(handler); |
| } |
| |
| public void onChange (boolean selfChange) { |
| // See if there's anything to do... |
| kick(); |
| } |
| } |
| |
| @Override |
| public IBinder onBind(Intent arg0) { |
| return mBinder; |
| } |
| |
| public void log (String str) { |
| Log.v("EmailApp:MailService", str); |
| } |
| |
| @Override |
| public void onCreate () { |
| INSTANCE = this; |
| |
| mAccountObserver = new AccountObserver(mHandler); |
| mMailboxObserver = new MailboxObserver(mHandler); |
| |
| // Start our thread... |
| if (mServiceThread == null || !mServiceThread.isAlive()) { |
| log(mServiceThread == null ? "Starting thread..." : "Restarting thread..."); |
| mServiceThread = new Thread(this, "<MailService>"); |
| mServiceThread.start(); |
| } else { |
| log("Attempt to start MailService though already started before?"); |
| } |
| } |
| |
| static private HashMap<Long, Boolean> mWakeLocks = new HashMap<Long, Boolean>(); |
| static private HashMap<Long, PendingIntent> mPendingIntents = |
| new HashMap<Long, PendingIntent>(); |
| static private WakeLock mWakeLock = null; |
| |
| static public void acquireWakeLock (long id) { |
| synchronized (mWakeLocks) { |
| Boolean lock = mWakeLocks.get(id); |
| if (lock == null) { |
| INSTANCE.log("+WakeLock requested for " + id); |
| if (mWakeLock == null) { |
| PowerManager pm = |
| (PowerManager) INSTANCE.getSystemService(Context.POWER_SERVICE); |
| mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MAIL_SERVICE"); |
| mWakeLock.acquire(); |
| INSTANCE.log("+WAKE LOCK ACQUIRED"); |
| } |
| mWakeLocks.put(id, true); |
| } |
| } |
| } |
| |
| static public void releaseWakeLock (long id) { |
| synchronized (mWakeLocks) { |
| Boolean lock = mWakeLocks.get(id); |
| if (lock != null) { |
| INSTANCE.log("+WakeLock not needed for " + id); |
| mWakeLocks.remove(id); |
| if (mWakeLocks.isEmpty()) { |
| mWakeLock.release(); |
| mWakeLock = null; |
| INSTANCE.log("+WAKE LOCK RELEASED"); |
| } |
| } |
| } |
| } |
| |
| static private String alarmOwner (long id) { |
| if (id == -1) { |
| return "MailService"; |
| } |
| else return "Mailbox " + Long.toString(id); |
| } |
| |
| static private void clearAlarm (long id) { |
| synchronized (mPendingIntents) { |
| PendingIntent pi = mPendingIntents.get(id); |
| if (pi != null) { |
| AlarmManager alarmManager = |
| (AlarmManager)INSTANCE.getSystemService(Context.ALARM_SERVICE); |
| alarmManager.cancel(pi); |
| INSTANCE.log("+Alarm cleared for " + alarmOwner(id)); |
| mPendingIntents.remove(id); |
| } |
| } |
| } |
| |
| static private void setAlarm (long id, long millis) { |
| synchronized (mPendingIntents) { |
| PendingIntent pi = mPendingIntents.get(id); |
| if (pi == null) { |
| Intent i = new Intent(INSTANCE, KeepAliveReceiver.class); |
| i.putExtra("mailbox", id); |
| i.setData(Uri.parse("Box" + id)); |
| pi = PendingIntent.getBroadcast(INSTANCE, 0, i, 0); |
| mPendingIntents.put(id, pi); |
| |
| AlarmManager alarmManager = |
| (AlarmManager)INSTANCE.getSystemService(Context.ALARM_SERVICE); |
| alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millis, pi); |
| INSTANCE.log("+Alarm set for " + alarmOwner(id) + ", " + millis + "ms"); |
| } |
| } |
| } |
| |
| static private void clearAlarms () { |
| AlarmManager alarmManager = (AlarmManager)INSTANCE.getSystemService(Context.ALARM_SERVICE); |
| synchronized (mPendingIntents) { |
| for (PendingIntent pi : mPendingIntents.values()) { |
| alarmManager.cancel(pi); |
| } |
| mPendingIntents.clear(); |
| } |
| } |
| |
| static public void runAwake (long id) { |
| acquireWakeLock(id); |
| clearAlarm(id); |
| } |
| |
| static public void runAsleep (long id, long millis) { |
| setAlarm(id, millis); |
| releaseWakeLock(id); |
| } |
| |
| static public void ping (long id) { |
| ProtocolService service = INSTANCE.serviceMap.get(id); |
| if (service != null) { |
| Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, id); |
| if (m != null) { |
| service.mAccount = |
| Account.restoreAccountWithId(INSTANCE, m.mAccountKey); |
| service.mMailbox = m; |
| service.ping(); |
| } |
| } |
| } |
| |
| @Override |
| public void onDestroy () { |
| log("!!! MaiLService onDestroy"); |
| if (mWakeLock != null) { |
| mWakeLock.release(); |
| mWakeLock = null; |
| } |
| clearAlarms(); |
| } |
| |
| public class ConnectivityReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive (Context context, Intent intent) { |
| Bundle b = intent.getExtras(); |
| if (b != null) { |
| NetworkInfo a = (NetworkInfo)b.get("networkInfo"); |
| String info = "CM Info: " + a.getTypeName(); |
| State state = a.getState(); |
| if (state == State.CONNECTED) { |
| info += " CONNECTED"; |
| } else if (state == State.CONNECTING) { |
| info += " CONNECTING"; |
| } else if (state == State.DISCONNECTED) { |
| info += " DISCONNECTED"; |
| } else if (state == State.DISCONNECTING) { |
| info += " DISCONNECTING"; |
| } else if (state == State.SUSPENDED) { |
| info += " SUSPENDED"; |
| } else if (state == State.UNKNOWN) { |
| info += " UNKNOWN"; |
| } |
| log("CONNECTIVITY: " + info); |
| } |
| } |
| } |
| |
| private void pause (int ms) { |
| try { |
| Thread.sleep(ms); |
| } catch (InterruptedException e) { |
| } |
| } |
| |
| private void startService (ProtocolService service, Mailbox m) { |
| synchronized (mSyncToken) { |
| String mailboxName = m.mDisplayName; |
| String accountName = service.mAccount.mDisplayName; |
| Thread thread = new Thread(service, mailboxName + "(" + accountName + ")"); |
| log("Starting thread for " + mailboxName + " in account " + accountName); |
| thread.start(); |
| serviceMap.put(m.mId, service); |
| } |
| } |
| |
| private void startService (Mailbox m) { |
| synchronized (mSyncToken) { |
| Account acct = |
| Account.restoreAccountWithId(this, m.mAccountKey); |
| if (acct != null) { |
| ProtocolService service; |
| service = new EasService(this, m); |
| startService(service, m); |
| } |
| } |
| } |
| |
| private void startSleep () { |
| synchronized (mSyncToken) { |
| // Shut everything down |
| boolean stoppedOne = false; |
| // Keep track of which services we've stopped |
| ArrayList<Long> toStop = new ArrayList<Long>(); |
| // Shut down all of our running services |
| for (Long mid : serviceMap.keySet()) { |
| toStop.add(mid); |
| stoppedOne = true; |
| } |
| |
| for (Long mid: toStop) { |
| ProtocolService svc = serviceMap.get(mid); |
| log("Going to sleep: shutting down " + svc.mAccount.mDisplayName + |
| "/" + svc.mMailboxName); |
| svc.stop(); |
| svc.mThread.interrupt(); |
| stoppedOne = true; |
| } |
| // Remove the stopped services from the map |
| //for (Long mid : stopped) |
| // serviceMap.remove(mid); |
| // Let the UI know |
| if (stoppedOne) { |
| } |
| } |
| } |
| |
| private void broadcastSleep () { |
| } |
| |
| public void run () { |
| log("MailService: run"); |
| Debug.waitForDebugger(); |
| mStop = false; |
| |
| runAwake(-1); |
| |
| ContentResolver resolver = getContentResolver(); |
| resolver.registerContentObserver(Account.CONTENT_URI, false, mAccountObserver); |
| resolver.registerContentObserver(Mailbox.CONTENT_URI, false, mMailboxObserver); |
| |
| ConnectivityReceiver cr = new ConnectivityReceiver(); |
| registerReceiver(cr, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); |
| ConnectivityManager cm = |
| (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); |
| |
| mSettings = PreferenceManager.getDefaultSharedPreferences(this); |
| GregorianCalendar calendar = new GregorianCalendar(); |
| |
| mStatus = AWAKE; |
| |
| try { |
| while (!mStop) { |
| runAwake(-1); |
| log("%%MailService heartbeat"); |
| while (!mStop) { |
| NetworkInfo info = cm.getActiveNetworkInfo(); |
| if (info != null && info.isConnected()) { |
| break; |
| } else { |
| pause(10*SECS); |
| } |
| } |
| |
| long nextWait = 10*MINS; |
| long now = System.currentTimeMillis(); |
| |
| // We could send notices of sleep time changes and otherwise cache all of this... |
| long sleepHours = mSettings.getLong("sleep_hours", 0); |
| if (sleepHours != 0) { |
| boolean wantSleep = false; |
| calendar.setTimeInMillis(now); |
| int nowHour = calendar.get(GregorianCalendar.HOUR_OF_DAY); |
| int nowMinute = calendar.get(GregorianCalendar.MINUTE); |
| |
| long sleepStart = sleepHours >> 32; |
| int startHour = (int)(sleepStart / 100); |
| int startMinute = (int)(sleepStart % 100); |
| |
| long sleepEnd = sleepHours & 0x00000000FFFFFFFFL; |
| int endHour = (int)(sleepEnd / 100); |
| int endMinute = (int)(sleepEnd % 100); |
| |
| if (sleepStart > sleepEnd) { |
| if ((nowHour > startHour) || |
| (nowHour == startHour && nowMinute >= startMinute) || |
| (nowHour < endHour) || |
| (nowHour == endHour && nowMinute <= endMinute)) |
| wantSleep = true; |
| } else if (((startHour < nowHour || |
| (startHour == nowHour && nowMinute >= startMinute)) && |
| ((nowHour < endHour) || |
| (nowHour == endHour && nowMinute <= endMinute)))) |
| wantSleep = true; |
| |
| if (wantSleep && (mStatus == AWAKE)) { |
| mStatus = SLEEP_HOURS; |
| startSleep(); |
| broadcastSleep(); |
| } else if (!wantSleep && (mStatus == SLEEP_HOURS)) { |
| mStatus = AWAKE; |
| broadcastSleep(); |
| } |
| } |
| |
| boolean sleepWeekends = mSettings.getBoolean("sleep_weekends", false); |
| if ((mStatus != SLEEP_HOURS) && ((mStatus != AWAKE) || sleepWeekends)) { |
| boolean wantSleep = false; |
| calendar.setTimeInMillis(now); |
| int day = calendar.get(GregorianCalendar.DAY_OF_WEEK); |
| if (sleepWeekends && |
| (day == GregorianCalendar.SATURDAY || |
| day == GregorianCalendar.SUNDAY)) { |
| wantSleep = true; |
| } |
| if ((mStatus == AWAKE) && wantSleep) { |
| mStatus = SLEEP_WEEKEND; |
| startSleep(); |
| broadcastSleep(); |
| } else if ((mStatus != AWAKE) && !wantSleep) { |
| // Wake up!! |
| mStatus = AWAKE; |
| broadcastSleep(); |
| } |
| } |
| |
| boolean offline = mSettings.getBoolean("offline", false); |
| if (mStatus == AWAKE || mStatus == OFFLINE) { |
| boolean wantSleep = offline; |
| if ((mStatus == AWAKE) && wantSleep) { |
| mStatus = OFFLINE; |
| startSleep(); |
| broadcastSleep(); |
| } else if ((mStatus == OFFLINE) && !wantSleep) { |
| // Wake up!! |
| mStatus = AWAKE; |
| broadcastSleep(); |
| } |
| } |
| |
| if (!mStop && ((mStatus == AWAKE) || mToothpicks)) { |
| // Start up threads that need it... |
| try { |
| Cursor c = getContentResolver().query(Mailbox.CONTENT_URI, |
| Mailbox.CONTENT_PROJECTION, null, null, null); |
| while (c.moveToNext()) { |
| // TODO Could be much faster - just get cursor of ones we're watching... |
| long aid = c.getLong(Mailbox.CONTENT_ACCOUNT_KEY_COLUMN); |
| // Only check mailboxes for EAS accounts |
| if (!mAccountObserver.mAccountIds.contains(aid)) { |
| continue; |
| } |
| long mid = c.getLong(Mailbox.CONTENT_ID_COLUMN); |
| ProtocolService service = serviceMap.get(mid); |
| if (service == null) { |
| long freq = c.getInt(Mailbox.CONTENT_SYNC_FREQUENCY_COLUMN); |
| if (freq == Account.CHECK_INTERVAL_PUSH) { |
| Mailbox m = |
| EmailContent.getContent(c, Mailbox.class); |
| // Either push, or 30 mins (default for idle timeout) |
| if (((m.mFlags & Mailbox.FLAG_CANT_PUSH) == 0) |
| || ((now - m.mSyncTime) > (1000 * 60 * 30L))) { |
| startService(m); |
| } |
| } else if (freq == -19) { |
| // See if we've got anything to do... |
| int cnt = EmailContent.count(this, |
| Message.CONTENT_URI, "mailboxKey=" + |
| mid + " and syncServerId=0", null); |
| if (cnt > 0) { |
| Mailbox m = EmailContent.getContent(c, Mailbox.class); |
| startService(new EasOutboxService(this, m), m); |
| } |
| } else if (freq > 0 && freq <= 1440) { |
| long lastSync = |
| c.getLong(Mailbox.CONTENT_SYNC_TIME_COLUMN); |
| if (now - lastSync > (freq * 60000L)) { |
| Mailbox m = EmailContent.getContent(c, Mailbox.class); |
| startService(m); |
| } |
| } |
| } else { |
| Thread thread = service.mThread; |
| if (!thread.isAlive()) { |
| serviceMap.remove(mid); |
| // Restart this if necessary |
| if (nextWait > 3*SECS) { |
| nextWait = 3*SECS; |
| } |
| } else { |
| long requestTime = service.mRequestTime; |
| if (requestTime > 0) { |
| long timeToRequest = requestTime - now; |
| if (service instanceof ProtocolService && |
| timeToRequest <= 0) { |
| service.mRequestTime = 0; |
| service.ping(); |
| } else if (requestTime > 0 && timeToRequest < nextWait) { |
| if (timeToRequest < 11*MINS) { |
| nextWait = |
| timeToRequest < 250 ? 250 : timeToRequest; |
| } else { |
| log("Illegal timeToRequest: " + timeToRequest); |
| } |
| } |
| } |
| } |
| } |
| } |
| c.close(); |
| |
| } catch (Exception e1) { |
| log("Exception to follow..."); |
| } |
| } |
| |
| try { |
| synchronized (INSTANCE) { |
| if (nextWait < 0) { |
| System.err.println("WTF?"); |
| nextWait = 1*SECS; |
| } |
| if (nextWait > 30*SECS) { |
| runAsleep(-1, nextWait - 1000); |
| } |
| |
| log("%%MailService sleeping for " + (nextWait / 1000) + " s"); |
| INSTANCE.wait(nextWait); |
| } |
| } catch (InterruptedException e) { |
| log("IOException to follow..."); |
| } |
| |
| if (mStop) { |
| startSleep(); |
| log("Shutdown requested."); |
| return; |
| } |
| |
| } |
| } catch (Throwable e) { |
| log("MailService crashed."); |
| } finally { |
| log("Goodbye."); |
| } |
| |
| startService(new Intent(this, SyncManager.class)); |
| throw new RuntimeException("MailService crash; please restart me..."); |
| } |
| |
| static public void serviceRequest (Mailbox m) { |
| serviceRequest(m.mId, 10*SECS); |
| } |
| |
| static public void serviceRequest (long mailboxId) { |
| serviceRequest(mailboxId, 10*SECS); |
| } |
| |
| static public void serviceRequest (long mailboxId, long ms) { |
| try { |
| if (INSTANCE == null) |
| return; |
| ProtocolService service = INSTANCE.serviceMap.get(mailboxId); |
| if (service != null) { |
| service.mRequestTime = System.currentTimeMillis() + ms; |
| kick(); |
| } else { |
| startManualSync(mailboxId); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| static public void serviceRequestImmediate (long mailboxId) { |
| ProtocolService service = INSTANCE.serviceMap.get(mailboxId); |
| if (service != null) { |
| service.mRequestTime = System.currentTimeMillis() ; |
| Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, mailboxId); |
| service.mAccount = Account.restoreAccountWithId(INSTANCE, m.mAccountKey); |
| service.mMailbox = m; |
| kick(); |
| } |
| } |
| |
| static public void partRequest (PartRequest req) { |
| Message msg = Message.restoreMessageWithId(INSTANCE, req.emailId); |
| if (msg == null) { |
| return; |
| } |
| long mailboxId = msg.mMailboxKey; |
| ProtocolService service = INSTANCE.serviceMap.get(mailboxId); |
| |
| if (service == null) |
| service = startManualSync(mailboxId); |
| |
| if (service != null) { |
| service.mRequestTime = System.currentTimeMillis(); |
| service.addPartRequest(req); |
| kick(); |
| } |
| } |
| |
| static public PartRequest hasPartRequest(long emailId, String part) { |
| Message msg = Message.restoreMessageWithId(INSTANCE, emailId); |
| if (msg == null) { |
| return null; |
| } |
| long mailboxId = msg.mMailboxKey; |
| ProtocolService service = INSTANCE.serviceMap.get(mailboxId); |
| if (service != null) { |
| service.mRequestTime = System.currentTimeMillis(); |
| return service.hasPartRequest(emailId, part); |
| } |
| return null; |
| } |
| |
| static public void cancelPartRequest(long emailId, String part) { |
| Message msg = Message.restoreMessageWithId(INSTANCE, emailId); |
| if (msg == null) { |
| return; |
| } |
| long mailboxId = msg.mMailboxKey; |
| ProtocolService service = INSTANCE.serviceMap.get(mailboxId); |
| if (service != null) { |
| service.mRequestTime = System.currentTimeMillis(); |
| service.cancelPartRequest(emailId, part); |
| } |
| } |
| |
| public class SyncStatus { |
| static public final int NOT_RUNNING = 0; |
| static public final int DIED = 1; |
| static public final int SYNC = 2; |
| static public final int IDLE = 3; |
| } |
| |
| static public int getSyncStatus (long mid) { |
| synchronized (mSyncToken) { |
| if (INSTANCE == null || INSTANCE.serviceMap == null) { |
| return SyncStatus.NOT_RUNNING; |
| } |
| ProtocolService svc = INSTANCE.serviceMap.get(mid); |
| if (svc == null) { |
| return SyncStatus.NOT_RUNNING; |
| } else { |
| if (!svc.mThread.isAlive()) { |
| return SyncStatus.DIED; |
| } else { |
| return svc.getSyncStatus(); |
| } |
| } |
| } |
| } |
| |
| static public ProtocolService startManualSync (long mid) { |
| if (INSTANCE == null || INSTANCE.serviceMap == null) |
| return null; |
| INSTANCE.log("startManualSync"); |
| synchronized (mSyncToken) { |
| if (INSTANCE.serviceMap.get(mid) == null) { |
| Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, mid); |
| INSTANCE.log("Starting sync for " + m.mDisplayName); |
| INSTANCE.startService(m); |
| } |
| } |
| return INSTANCE.serviceMap.get(mid); |
| } |
| |
| // DO NOT CALL THIS IN A LOOP ON THE SERVICEMAP |
| static public void stopManualSync (long mid) { |
| if (INSTANCE == null || INSTANCE.serviceMap == null) { |
| return; |
| } |
| synchronized (mSyncToken) { |
| ProtocolService svc = INSTANCE.serviceMap.get(mid); |
| if (svc != null) { |
| INSTANCE.log("Stopping sync for " + svc.mMailboxName); |
| svc.stop(); |
| svc.mThread.interrupt(); |
| } |
| } |
| } |
| |
| static public void kick () { |
| if (INSTANCE == null) { |
| return; |
| } |
| synchronized (INSTANCE) { |
| INSTANCE.log("We've been kicked!"); |
| INSTANCE.notify(); |
| } |
| } |
| |
| static public void kick (long mid) { |
| Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, mid); |
| int syncType = m.mSyncFrequency; |
| if (syncType == Account.CHECK_INTERVAL_PUSH) { |
| SyncManager.serviceRequestImmediate(mid); |
| } else { |
| SyncManager.startManualSync(mid); |
| } |
| } |
| |
| |
| static public void accountUpdated (long acctId) { |
| synchronized (mSyncToken) { |
| for (ProtocolService svc : INSTANCE.serviceMap.values()) { |
| if (svc.mAccount.mId == acctId) { |
| svc.mAccount = Account.restoreAccountWithId(INSTANCE, acctId); |
| } |
| } |
| } |
| } |
| |
| static public int status () { |
| return mStatus; |
| } |
| |
| static public boolean isSleeping () { |
| return (mStatus == SLEEP_HOURS || mStatus == SLEEP_WEEKEND); |
| } |
| |
| static public void forceAwake (boolean wake) { |
| mToothpicks = wake; |
| kick(); |
| } |
| |
| static public boolean isForceAwake () { |
| return mToothpicks; |
| } |
| |
| static public void done (ProtocolService svc) { |
| INSTANCE.serviceMap.remove(svc.mMailboxId); |
| } |
| |
| public static void shutdown () { |
| INSTANCE.mStop = true; |
| kick(); |
| INSTANCE.stopSelf(); |
| } |
| |
| static public String serviceName (long id) { |
| if (id < 0) { |
| return "MailService"; |
| } else { |
| ProtocolService service = INSTANCE.serviceMap.get(id); |
| if (service != null) { |
| return service.mThread.getName(); |
| } else { |
| return "Not running?"; |
| } |
| } |
| } |
| |
| static public Context getContext () { |
| if (INSTANCE == null) { |
| return null; |
| } |
| return (Context)INSTANCE; |
| } |
| } |