From ee4339a971ebe228bb4aa2b3961ad12b4ff2c85a Mon Sep 17 00:00:00 2001 From: M66B Date: Sun, 7 Oct 2018 10:13:32 +0000 Subject: [PATCH] Simplify store keep alive --- .../eu/faircode/email/FragmentFolder.java | 53 ++++++++- .../eu/faircode/email/ServiceSynchronize.java | 101 ++++++++---------- 2 files changed, 94 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/FragmentFolder.java b/app/src/main/java/eu/faircode/email/FragmentFolder.java index a93856c28f..04d8a2a40d 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolder.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolder.java @@ -337,10 +337,61 @@ public class FragmentFolder extends FragmentEx { pbWait.setVisibility(View.GONE); Helper.setViewsEnabled(view, true); etRename.setEnabled(folder == null || EntityFolder.USER.equals(folder.type)); - grpInterval.setVisibility(folder == null || EntityFolder.USER.equals(folder.type) ? View.VISIBLE : View.GONE); btnSave.setEnabled(true); ibDelete.setVisibility(folder == null || !EntityFolder.USER.equals(folder.type) ? View.GONE : View.VISIBLE); } }); + + Bundle args = new Bundle(); + args.putLong("id", id); + args.putLong("account", account); + + new SimpleTask() { + @Override + protected Boolean onLoad(Context context, Bundle args) throws Throwable { + long fid = args.getLong("id"); + long aid = args.getLong("account"); + + IMAPStore istore = null; + DB db = DB.getInstance(getContext()); + try { + db.beginTransaction(); + + EntityAccount account; + if (fid < 0) + account = db.account().getAccount(aid); + else { + EntityFolder folder = db.folder().getFolder(fid); + account = db.account().getAccount(folder.account); + } + + db.setTransactionSuccessful(); + + Properties props = MessageHelper.getSessionProperties(account.auth_type); + Session isession = Session.getInstance(props, null); + istore = (IMAPStore) isession.getStore("imaps"); + istore.connect(account.host, account.port, account.user, account.password); + + return istore.hasCapability("IDLE"); + } finally { + db.endTransaction(); + + if (istore != null) + istore.close(); + } + } + + @Override + protected void onLoaded(Bundle args, Boolean capIdle) { + grpInterval.setVisibility(capIdle ? View.GONE : View.VISIBLE); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + grpInterval.setVisibility(View.VISIBLE); + if (BuildConfig.DEBUG) + Helper.unexpectedError(getContext(), ex); + } + }.load(this, args); } } diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 73570c593c..5050baf1e2 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -50,12 +50,10 @@ import android.text.TextUtils; import android.util.Log; import com.sun.mail.iap.ConnectionException; -import com.sun.mail.iap.ProtocolException; import com.sun.mail.imap.AppendUID; import com.sun.mail.imap.IMAPFolder; import com.sun.mail.imap.IMAPMessage; import com.sun.mail.imap.IMAPStore; -import com.sun.mail.imap.protocol.IMAPProtocol; import com.sun.mail.util.FolderClosedIOException; import com.sun.mail.util.MailConnectException; @@ -129,7 +127,6 @@ public class ServiceSynchronize extends LifecycleService { private static final int CONNECT_BACKOFF_START = 8; // seconds private static final int CONNECT_BACKOFF_MAX = 1024; // seconds (1024 sec ~ 17 min) - private static final long STORE_NOOP_INTERVAL = 9 * 60 * 1000L; // milliseconds private static final int SYNC_BATCH_SIZE = 20; private static final int DOWNLOAD_BATCH_SIZE = 20; private static final int MESSAGE_AUTO_DOWNLOAD_SIZE = 32 * 1024; // bytes @@ -559,6 +556,7 @@ public class ServiceSynchronize extends LifecycleService { // Debug boolean debug = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("debug", false); + debug = debug || BuildConfig.DEBUG; System.setProperty("mail.socket.debug", Boolean.toString(debug)); // Create session @@ -569,7 +567,7 @@ public class ServiceSynchronize extends LifecycleService { final IMAPStore istore = (IMAPStore) isession.getStore("imaps"); final Map folders = new HashMap<>(); - List noops = new ArrayList<>(); + List pollers = new ArrayList<>(); List idlers = new ArrayList<>(); try { // Listen for store events @@ -672,7 +670,7 @@ public class ServiceSynchronize extends LifecycleService { db.folder().setFolderError(folder.id, null); // Keep folder connection alive - Thread noop = new Thread(new Runnable() { + Thread poller = new Thread(new Runnable() { @Override public void run() { try { @@ -811,30 +809,15 @@ public class ServiceSynchronize extends LifecycleService { } }); - Log.i(Helper.TAG, folder.name + " start noop"); - while (state.running && ifolder.isOpen()) { - try { - if (!EntityFolder.USER.equals(folder.type) && capIdle) { - Thread.sleep(account.poll_interval * 60 * 1000L); - Log.i(Helper.TAG, folder.name + " request NOOP"); - ifolder.doCommand(new IMAPFolder.ProtocolCommand() { - public Object doCommand(IMAPProtocol p) throws ProtocolException { - Log.i(Helper.TAG, ifolder.getName() + " start NOOP"); - p.simpleCommand("NOOP", null); - Log.i(Helper.TAG, ifolder.getName() + " end NOOP"); - return null; - } - }); - } else { - if (folder.poll_interval == null) - Thread.sleep(account.poll_interval * 60 * 1000L); - else - Thread.sleep(folder.poll_interval * 60 * 1000L); + if (!capIdle) { + Log.i(Helper.TAG, folder.name + " start polling"); + while (state.running) { + try { + Thread.sleep((folder.poll_interval == null ? 9 : folder.poll_interval) * 60 * 1000L); synchronizeMessages(account, folder, ifolder, state); + } catch (InterruptedException ex) { + Log.w(Helper.TAG, folder.name + " poll " + ex.toString()); } - - } catch (InterruptedException ex) { - Log.w(Helper.TAG, folder.name + " noop " + ex.toString()); } } } catch (Throwable ex) { @@ -847,16 +830,17 @@ public class ServiceSynchronize extends LifecycleService { state.notifyAll(); } } finally { - Log.i(Helper.TAG, folder.name + " end noop"); + if (!capIdle) + Log.i(Helper.TAG, folder.name + " end polling"); } } - }, "sync.noop." + folder.id); - noop.start(); - noops.add(noop); + }, "sync.poller." + folder.id); + poller.start(); + pollers.add(poller); // Receive folder events - if (!EntityFolder.USER.equals(folder.type) && capIdle) { - Thread idle = new Thread(new Runnable() { + if (capIdle) { + Thread idler = new Thread(new Runnable() { @Override public void run() { try { @@ -880,8 +864,8 @@ public class ServiceSynchronize extends LifecycleService { } } }, "sync.idle." + folder.id); - idle.start(); - idlers.add(idle); + idler.start(); + idlers.add(idler); } } @@ -969,22 +953,18 @@ public class ServiceSynchronize extends LifecycleService { try { // Keep store alive - while (state.running && istore.isConnected()) { - Log.i(Helper.TAG, "Checking folders"); - for (EntityFolder folder : folders.keySet()) - if (!folders.get(folder).isOpen()) - throw new FolderClosedException(folders.get(folder)); - - // Wait for stop or folder error + while (state.running) { Log.i(Helper.TAG, account.name + " wait"); synchronized (state) { try { - state.wait(STORE_NOOP_INTERVAL); + state.wait(account.poll_interval * 60 * 1000L); } catch (InterruptedException ex) { Log.w(Helper.TAG, account.name + " wait " + ex.toString()); } } - Log.i(Helper.TAG, account.name + " waited"); + + if (!istore.isConnected()) + throw new StoreClosedException(istore); } Log.i(Helper.TAG, account.name + " done running=" + state.running); } finally { @@ -996,11 +976,18 @@ public class ServiceSynchronize extends LifecycleService { db.account().setAccountError(account.id, Helper.formatThrowable(ex)); } finally { - // Close store EntityLog.log(this, account.name + " closing"); db.account().setAccountState(account.id, "closing"); for (EntityFolder folder : folders.keySet()) db.folder().setFolderState(folder.id, "closing"); + + // Stop pollers + for (Thread poller : pollers) { + poller.interrupt(); + join(poller); + } + + // Close store try { Thread t = new Thread(new Runnable() { @Override @@ -1030,16 +1017,10 @@ public class ServiceSynchronize extends LifecycleService { db.folder().setFolderState(folder.id, null); } - // Stop noop - for (Thread noop : noops) { - noop.interrupt(); - join(noop); - } - - // Stop idle - for (Thread idle : idlers) { - idle.interrupt(); - join(idle); + // Stop idlers + for (Thread idler : idlers) { + idler.interrupt(); + join(idler); } } @@ -1541,10 +1522,12 @@ public class ServiceSynchronize extends LifecycleService { if (message == null) full.add(imessage); } - long headers = SystemClock.elapsedRealtime(); - ifolder.fetch(full.toArray(new Message[0]), fp); - Log.i(Helper.TAG, folder.name + " fetched headers=" + full.size() + - " " + (SystemClock.elapsedRealtime() - headers) + " ms"); + if (full.size() > 0) { + long headers = SystemClock.elapsedRealtime(); + ifolder.fetch(full.toArray(new Message[0]), fp); + Log.i(Helper.TAG, folder.name + " fetched headers=" + full.size() + + " " + (SystemClock.elapsedRealtime() - headers) + " ms"); + } for (int j = isub.length - 1; j >= 0; j--) try {