pull/145/head
M66B 6 years ago
parent bf310662c1
commit 155506ce81

@ -79,7 +79,6 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import javax.mail.Address; import javax.mail.Address;
import javax.mail.AuthenticationFailedException; import javax.mail.AuthenticationFailedException;
@ -550,6 +549,13 @@ public class ServiceSynchronize extends LifecycleService {
} }
private void monitorAccount(final EntityAccount account, final ServiceState state) throws NoSuchProviderException { private void monitorAccount(final EntityAccount account, final ServiceState state) throws NoSuchProviderException {
final PowerManager pm = getSystemService(PowerManager.class);
final PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
BuildConfig.APPLICATION_ID + ":account." + account.id);
try {
wl.acquire();
final DB db = DB.getInstance(this); final DB db = DB.getInstance(this);
final ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory); final ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory);
@ -587,12 +593,23 @@ public class ServiceSynchronize extends LifecycleService {
istore.addFolderListener(new FolderAdapter() { istore.addFolderListener(new FolderAdapter() {
@Override @Override
public void folderCreated(FolderEvent e) { public void folderCreated(FolderEvent e) {
try {
wl.acquire();
Log.i(Helper.TAG, "Folder created=" + e.getFolder().getFullName()); Log.i(Helper.TAG, "Folder created=" + e.getFolder().getFullName());
state.thread.interrupt(); state.thread.interrupt();
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {
}
} finally {
wl.release();
}
} }
@Override @Override
public void folderRenamed(FolderEvent e) { public void folderRenamed(FolderEvent e) {
try {
wl.acquire();
Log.i(Helper.TAG, "Folder renamed=" + e.getFolder()); Log.i(Helper.TAG, "Folder renamed=" + e.getFolder());
String old = e.getFolder().getFullName(); String old = e.getFolder().getFullName();
@ -601,12 +618,28 @@ public class ServiceSynchronize extends LifecycleService {
Log.i(Helper.TAG, "Renamed to " + name + " count=" + count); Log.i(Helper.TAG, "Renamed to " + name + " count=" + count);
state.thread.interrupt(); state.thread.interrupt();
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {
}
} finally {
wl.release();
}
} }
@Override @Override
public void folderDeleted(FolderEvent e) { public void folderDeleted(FolderEvent e) {
try {
wl.acquire();
Log.i(Helper.TAG, "Folder deleted=" + e.getFolder().getFullName()); Log.i(Helper.TAG, "Folder deleted=" + e.getFolder().getFullName());
state.thread.interrupt(); state.thread.interrupt();
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {
}
} finally {
wl.release();
}
} }
}); });
@ -644,7 +677,7 @@ public class ServiceSynchronize extends LifecycleService {
// Update folder list // Update folder list
synchronizeFolders(account, istore, state); synchronizeFolders(account, istore, state);
// Synchronize folders // Open folders
for (final EntityFolder folder : db.folder().getFolders(account.id, true)) { for (final EntityFolder folder : db.folder().getFolders(account.id, true)) {
Log.i(Helper.TAG, account.name + " sync folder " + folder.name); Log.i(Helper.TAG, account.name + " sync folder " + folder.name);
@ -667,6 +700,8 @@ public class ServiceSynchronize extends LifecycleService {
@Override @Override
public void run() { public void run() {
try { try {
wl.acquire();
// Process pending operations // Process pending operations
processOperations(folder, isession, istore, ifolder); processOperations(folder, isession, istore, ifolder);
@ -676,6 +711,7 @@ public class ServiceSynchronize extends LifecycleService {
public void messagesAdded(MessageCountEvent e) { public void messagesAdded(MessageCountEvent e) {
synchronized (lock) { synchronized (lock) {
try { try {
wl.acquire();
Log.i(Helper.TAG, folder.name + " messages added"); Log.i(Helper.TAG, folder.name + " messages added");
FetchProfile fp = new FetchProfile(); FetchProfile fp = new FetchProfile();
@ -716,6 +752,8 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex)); db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
state.thread.interrupt(); state.thread.interrupt();
} finally {
wl.release();
} }
} }
} }
@ -724,6 +762,7 @@ public class ServiceSynchronize extends LifecycleService {
public void messagesRemoved(MessageCountEvent e) { public void messagesRemoved(MessageCountEvent e) {
synchronized (lock) { synchronized (lock) {
try { try {
wl.acquire();
Log.i(Helper.TAG, folder.name + " messages removed"); Log.i(Helper.TAG, folder.name + " messages removed");
for (Message imessage : e.getMessages()) for (Message imessage : e.getMessages())
try { try {
@ -743,6 +782,8 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex)); db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
state.thread.interrupt(); state.thread.interrupt();
} finally {
wl.release();
} }
} }
} }
@ -759,6 +800,7 @@ public class ServiceSynchronize extends LifecycleService {
public void messageChanged(MessageChangedEvent e) { public void messageChanged(MessageChangedEvent e) {
synchronized (lock) { synchronized (lock) {
try { try {
wl.acquire();
try { try {
Log.i(Helper.TAG, folder.name + " message changed"); Log.i(Helper.TAG, folder.name + " message changed");
@ -791,6 +833,8 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex)); db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
state.thread.interrupt(); state.thread.interrupt();
} finally {
wl.release();
} }
} }
} }
@ -802,6 +846,8 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex)); db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
state.thread.interrupt(); state.thread.interrupt();
} finally {
wl.release();
} }
} }
}, "sync." + folder.id); }, "sync." + folder.id);
@ -814,6 +860,7 @@ public class ServiceSynchronize extends LifecycleService {
@Override @Override
public void run() { public void run() {
try { try {
wl.acquire();
Log.i(Helper.TAG, folder.name + " start idle"); Log.i(Helper.TAG, folder.name + " start idle");
while (state.running) { while (state.running) {
Log.i(Helper.TAG, folder.name + " do idle"); Log.i(Helper.TAG, folder.name + " do idle");
@ -829,6 +876,7 @@ public class ServiceSynchronize extends LifecycleService {
state.thread.interrupt(); state.thread.interrupt();
} finally { } finally {
Log.i(Helper.TAG, folder.name + " end idle"); Log.i(Helper.TAG, folder.name + " end idle");
wl.release();
} }
} }
}, "idler." + folder.id); }, "idler." + folder.id);
@ -837,6 +885,7 @@ public class ServiceSynchronize extends LifecycleService {
} }
} }
// Successfully connected: reset back off time
backoff = CONNECT_BACKOFF_START; backoff = CONNECT_BACKOFF_START;
// Process folder actions // Process folder actions
@ -847,6 +896,8 @@ public class ServiceSynchronize extends LifecycleService {
@Override @Override
public void run() { public void run() {
long fid = intent.getLongExtra("folder", -1); long fid = intent.getLongExtra("folder", -1);
try {
wl.acquire();
Log.i(Helper.TAG, "Process folder=" + fid + " intent=" + intent); Log.i(Helper.TAG, "Process folder=" + fid + " intent=" + intent);
// Get folder // Get folder
@ -859,7 +910,7 @@ public class ServiceSynchronize extends LifecycleService {
break; break;
} }
final boolean shouldClose = (ifolder == null); final boolean shouldClose = (folder == null);
try { try {
if (folder == null) if (folder == null)
@ -884,6 +935,7 @@ public class ServiceSynchronize extends LifecycleService {
if (ACTION_PROCESS_OPERATIONS.equals(intent.getAction())) if (ACTION_PROCESS_OPERATIONS.equals(intent.getAction()))
processOperations(folder, isession, istore, ifolder); processOperations(folder, isession, istore, ifolder);
else if (ACTION_SYNCHRONIZE_FOLDER.equals(intent.getAction())) { else if (ACTION_SYNCHRONIZE_FOLDER.equals(intent.getAction())) {
processOperations(folder, isession, istore, ifolder); processOperations(folder, isession, istore, ifolder);
synchronizeMessages(account, folder, ifolder, state); synchronizeMessages(account, folder, ifolder, state);
@ -907,6 +959,9 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderState(folder.id, null); db.folder().setFolderState(folder.id, null);
} }
} }
} finally {
wl.release();
}
} }
}); });
} }
@ -921,25 +976,47 @@ public class ServiceSynchronize extends LifecycleService {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(ServiceSynchronize.this); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(ServiceSynchronize.this);
lbm.registerReceiver(processFolder, f); lbm.registerReceiver(processFolder, f);
// Create barrier // Keep alive alarm receiver
final Semaphore sem = new Semaphore(0); BroadcastReceiver alarm = new BroadcastReceiver() {
// Keep alive
final PowerManager pm = getSystemService(PowerManager.class);
final PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "account." + account.id);
final AlarmManager am = getSystemService(AlarmManager.class);
final String id = BuildConfig.APPLICATION_ID + ".POLL." + account.id;
final PendingIntent pi = PendingIntent.getBroadcast(ServiceSynchronize.this, 0, new Intent(id), 0);
BroadcastReceiver alive = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
// Receiver runs on main thread
// Receiver has a wake lock for ~10 seconds
EntityLog.log(context, account.name + " keep alive"); EntityLog.log(context, account.name + " keep alive");
state.thread.interrupt();
try {
// Give interrupted thread some time to acquire wake lock
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
};
String id = BuildConfig.APPLICATION_ID + ".POLL." + account.id;
PendingIntent pi = PendingIntent.getBroadcast(ServiceSynchronize.this, 0, new Intent(id), 0);
registerReceiver(alarm, new IntentFilter(id));
// Keep alive
AlarmManager am = getSystemService(AlarmManager.class);
try { try {
while (state.running) {
// Schedule keep alive alarm
EntityLog.log(this, account.name + " wait=" + account.poll_interval);
am.setAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + account.poll_interval * 60 * 1000L,
pi);
try {
wl.release();
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ex) {
EntityLog.log(this, account.name + " waited running=" + state.running);
} finally {
wl.acquire(); wl.acquire();
}
if (state.running) {
if (!istore.isConnected()) if (!istore.isConnected())
throw new StoreClosedException(istore); throw new StoreClosedException(istore);
@ -949,42 +1026,13 @@ public class ServiceSynchronize extends LifecycleService {
throw new FolderClosedException(folders.get(folder)); throw new FolderClosedException(folders.get(folder));
} else } else
synchronizeMessages(account, folder, folders.get(folder), state); synchronizeMessages(account, folder, folders.get(folder), state);
} catch (Throwable ex) {
Log.e(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, null, ex);
db.account().setAccountError(account.id, Helper.formatThrowable(ex));
sem.release();
} finally {
wl.release();
} }
// Reschedule alarm
am.setAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + account.poll_interval * 60 * 1000L,
pi);
} }
};
registerReceiver(alive, new IntentFilter(id));
// Schedule alarm
EntityLog.log(this, account.name + " wait=" + account.poll_interval);
am.setAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + account.poll_interval * 60 * 1000L,
pi);
// Wait for interrupt or exception
try {
sem.acquire();
} catch (InterruptedException ex) {
Log.w(Helper.TAG, account.name + " semaphore " + ex.toString());
} finally { } finally {
// Cleanup // Cleanup
am.cancel(pi); am.cancel(pi);
unregisterReceiver(alive); unregisterReceiver(alarm);
lbm.unregisterReceiver(processFolder); lbm.unregisterReceiver(processFolder);
} }
@ -1055,8 +1103,10 @@ public class ServiceSynchronize extends LifecycleService {
} }
} }
} }
} finally {
EntityLog.log(this, account.name + " stopped"); EntityLog.log(this, account.name + " stopped");
wl.release();
}
} }
private void processOperations(EntityFolder folder, Session isession, IMAPStore istore, IMAPFolder ifolder) throws MessagingException, JSONException, IOException { private void processOperations(EntityFolder folder, Session isession, IMAPStore istore, IMAPFolder ifolder) throws MessagingException, JSONException, IOException {
@ -1897,7 +1947,13 @@ public class ServiceSynchronize extends LifecycleService {
@Override @Override
public void run() { public void run() {
PowerManager pm = getSystemService(PowerManager.class);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
BuildConfig.APPLICATION_ID + ":start");
try { try {
wl.acquire();
DB db = DB.getInstance(ServiceSynchronize.this); DB db = DB.getInstance(ServiceSynchronize.this);
outbox = db.folder().getOutbox(); outbox = db.folder().getOutbox();
@ -1961,9 +2017,13 @@ public class ServiceSynchronize extends LifecycleService {
EntityLog.log(ServiceSynchronize.this, "Main started"); EntityLog.log(ServiceSynchronize.this, "Main started");
try { try {
Thread.sleep(1000);
wl.release();
Thread.sleep(Long.MAX_VALUE); Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Log.w(Helper.TAG, "main wait " + ex.toString()); Log.w(Helper.TAG, "main wait " + ex.toString());
} finally {
wl.acquire();
} }
// Stop monitoring accounts // Stop monitoring accounts
@ -1983,14 +2043,27 @@ public class ServiceSynchronize extends LifecycleService {
} catch (Throwable ex) { } catch (Throwable ex) {
// Fail-safe // Fail-safe
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
} finally {
wl.release();
} }
} }
}, "sync.main"); }, "sync.main");
state.thread.setPriority(THREAD_PRIORITY_BACKGROUND); // will be inherited state.thread.setPriority(THREAD_PRIORITY_BACKGROUND); // will be inherited
state.thread.start(); state.thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
} }
private void stop() { private void stop() {
PowerManager pm = getSystemService(PowerManager.class);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
BuildConfig.APPLICATION_ID + ":stop");
try {
wl.acquire();
EntityLog.log(ServiceSynchronize.this, "Main stop"); EntityLog.log(ServiceSynchronize.this, "Main stop");
state.running = false; state.running = false;
@ -2000,6 +2073,9 @@ public class ServiceSynchronize extends LifecycleService {
EntityLog.log(ServiceSynchronize.this, "Main stopped"); EntityLog.log(ServiceSynchronize.this, "Main stopped");
state = null; state = null;
} finally {
wl.release();
}
} }
private void restart() { private void restart() {
@ -2021,6 +2097,13 @@ public class ServiceSynchronize extends LifecycleService {
executor.submit(new Runnable() { executor.submit(new Runnable() {
@Override @Override
public void run() { public void run() {
PowerManager pm = getSystemService(PowerManager.class);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
BuildConfig.APPLICATION_ID + ":outbox");
try {
wl.acquire();
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
try { try {
Log.i(Helper.TAG, outbox.name + " start operations"); Log.i(Helper.TAG, outbox.name + " start operations");
@ -2035,6 +2118,9 @@ public class ServiceSynchronize extends LifecycleService {
Log.i(Helper.TAG, outbox.name + " end operations"); Log.i(Helper.TAG, outbox.name + " end operations");
db.folder().setFolderState(outbox.id, null); db.folder().setFolderState(outbox.id, null);
} }
} finally {
wl.release();
}
} }
}); });
} }

Loading…
Cancel
Save