Browse Source

Fixed service synchronization

main
M66B 6 years ago
parent
commit
eb3ba04e1e
1 changed files with 85 additions and 44 deletions
  1. +85
    -44
      app/src/main/java/eu/faircode/email/ServiceSynchronize.java

+ 85
- 44
app/src/main/java/eu/faircode/email/ServiceSynchronize.java View File

@ -109,6 +109,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class ServiceSynchronize extends LifecycleService { public class ServiceSynchronize extends LifecycleService {
private final Object lock = new Object(); private final Object lock = new Object();
private ServiceManager serviceManager = new ServiceManager();
private static final int NOTIFICATION_SYNCHRONIZE = 1; private static final int NOTIFICATION_SYNCHRONIZE = 1;
private static final int NOTIFICATION_UNSEEN = 2; private static final int NOTIFICATION_UNSEEN = 2;
@ -139,7 +140,7 @@ public class ServiceSynchronize extends LifecycleService {
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
// Removed because of Android VPN service // Removed because of Android VPN service
// builder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); // builder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
cm.registerNetworkCallback(builder.build(), networkCallback);
cm.registerNetworkCallback(builder.build(), serviceManager);
DB.getInstance(this).account().liveStats().observe(this, new Observer<TupleAccountStats>() { DB.getInstance(this).account().liveStats().observe(this, new Observer<TupleAccountStats>() {
private int prev_unseen = -1; private int prev_unseen = -1;
@ -168,9 +169,9 @@ public class ServiceSynchronize extends LifecycleService {
Log.i(Helper.TAG, "Service destroy"); Log.i(Helper.TAG, "Service destroy");
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
cm.unregisterNetworkCallback(networkCallback);
cm.unregisterNetworkCallback(serviceManager);
networkCallback.onLost(cm.getActiveNetwork());
serviceManager.stop();
stopForeground(true); stopForeground(true);
@ -329,11 +330,11 @@ public class ServiceSynchronize extends LifecycleService {
Log.i(Helper.TAG, account.name + " start"); Log.i(Helper.TAG, account.name + " start");
final DB db = DB.getInstance(ServiceSynchronize.this); final DB db = DB.getInstance(ServiceSynchronize.this);
db.account().setAccountState(account.id, "connecting");
int backoff = CONNECT_BACKOFF_START; int backoff = CONNECT_BACKOFF_START;
while (state.running) { while (state.running) {
IMAPStore istore = null; IMAPStore istore = null;
final Semaphore semaphore = new Semaphore(0, true);
try { try {
Properties props = MessageHelper.getSessionProperties(); Properties props = MessageHelper.getSessionProperties();
props.setProperty("mail.imaps.peek", "true"); props.setProperty("mail.imaps.peek", "true");
@ -506,6 +507,8 @@ public class ServiceSynchronize extends LifecycleService {
synchronized (state) { synchronized (state) {
state.notifyAll(); state.notifyAll();
} }
semaphore.release();
} }
@Override @Override
@ -525,6 +528,8 @@ public class ServiceSynchronize extends LifecycleService {
synchronized (state) { synchronized (state) {
state.notifyAll(); state.notifyAll();
} }
semaphore.release();
} }
private BroadcastReceiver processReceiver = new BroadcastReceiver() { private BroadcastReceiver processReceiver = new BroadcastReceiver() {
@ -585,6 +590,7 @@ public class ServiceSynchronize extends LifecycleService {
// Initiate connection // Initiate connection
Log.i(Helper.TAG, account.name + " connect"); Log.i(Helper.TAG, account.name + " connect");
db.account().setAccountState(account.id, "connecting");
istore.connect(account.host, account.port, account.user, account.password); istore.connect(account.host, account.port, account.user, account.password);
backoff = CONNECT_BACKOFF_START; backoff = CONNECT_BACKOFF_START;
@ -598,7 +604,7 @@ public class ServiceSynchronize extends LifecycleService {
} }
Log.i(Helper.TAG, account.name + " waited"); Log.i(Helper.TAG, account.name + " waited");
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Log.w(Helper.TAG, account.name + " " + ex.toString());
Log.w(Helper.TAG, account.name + " wait " + ex.toString());
} }
if (state.running) { if (state.running) {
Log.i(Helper.TAG, account.name + " NOOP"); Log.i(Helper.TAG, account.name + " NOOP");
@ -625,7 +631,16 @@ public class ServiceSynchronize extends LifecycleService {
Log.w(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex)); Log.w(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
} }
} }
Log.i(Helper.TAG, account.name + " closed");
Log.i(Helper.TAG, account.name + " waiting for close");
boolean acquired = false;
while (!acquired)
try {
semaphore.acquire();
acquired = true;
} catch (InterruptedException ex) {
Log.e(Helper.TAG, account.name + " acquire " + ex.getMessage());
}
Log.i(Helper.TAG, account.name + " reported closed");
} }
if (state.running) { if (state.running) {
@ -636,7 +651,7 @@ public class ServiceSynchronize extends LifecycleService {
if (backoff < CONNECT_BACKOFF_MAX) if (backoff < CONNECT_BACKOFF_MAX)
backoff *= 2; backoff *= 2;
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Log.w(Helper.TAG, account.name + " " + ex.toString());
Log.w(Helper.TAG, account.name + " backoff " + ex.getMessage());
} }
} }
} }
@ -644,13 +659,16 @@ public class ServiceSynchronize extends LifecycleService {
db.account().setAccountState(account.id, null); db.account().setAccountState(account.id, null);
for (Thread t : threads) { for (Thread t : threads) {
try {
Log.i(Helper.TAG, "Joining " + t.getName());
t.join();
Log.i(Helper.TAG, "Joined " + t.getName());
} catch (InterruptedException ex) {
Log.w(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
}
boolean joined = false;
while (!joined)
try {
Log.i(Helper.TAG, "Joining " + t.getName());
t.join();
joined = true;
Log.i(Helper.TAG, "Joined " + t.getName());
} catch (InterruptedException ex) {
Log.w(Helper.TAG, t.getName() + " join " + ex.toString());
}
} }
threads.clear(); threads.clear();
executor.shutdown(); executor.shutdown();
@ -762,7 +780,7 @@ public class ServiceSynchronize extends LifecycleService {
try { try {
Thread.sleep(NOOP_INTERVAL); Thread.sleep(NOOP_INTERVAL);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Log.w(Helper.TAG, folder.name + " " + ex.toString());
Log.w(Helper.TAG, folder.name + " noop " + ex.getMessage());
} }
open = ifolder.isOpen(); open = ifolder.isOpen();
if (open) if (open)
@ -1358,7 +1376,7 @@ public class ServiceSynchronize extends LifecycleService {
}); });
} }
ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
private class ServiceManager extends ConnectivityManager.NetworkCallback {
ServiceState state = new ServiceState(); ServiceState state = new ServiceState();
private Thread main; private Thread main;
private EntityFolder outbox = null; private EntityFolder outbox = null;
@ -1424,14 +1442,18 @@ public class ServiceSynchronize extends LifecycleService {
} }
// Stop monitoring accounts // Stop monitoring accounts
for (Thread t : threads)
try {
Log.i(Helper.TAG, "Joining " + t.getName());
t.join();
Log.i(Helper.TAG, "Joined " + t.getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
for (Thread t : threads) {
boolean joined = false;
while (!joined)
try {
Log.i(Helper.TAG, "Joining " + t.getName());
t.join();
joined = true;
Log.i(Helper.TAG, "Joined " + t.getName());
} catch (InterruptedException ex) {
Log.w(Helper.TAG, t.getName() + " join " + ex.toString());
}
}
threads.clear(); threads.clear();
executor.shutdown(); executor.shutdown();
@ -1452,23 +1474,37 @@ public class ServiceSynchronize extends LifecycleService {
public void onLost(Network network) { public void onLost(Network network) {
Log.i(Helper.TAG, "Lost " + network); Log.i(Helper.TAG, "Lost " + network);
// TODO: run in thread
if (main != null)
new Thread(new Runnable() {
@Override
public void run() {
stop();
}
}).start();
}
synchronized (state) {
state.running = false;
state.notifyAll();
}
public void stop() {
if (main != null) {
synchronized (state) {
state.running = false;
state.notifyAll();
}
try {
main.interrupt(); // backoff
Log.i(Helper.TAG, "Joining " + main.getName());
main.join();
Log.i(Helper.TAG, "Joined " + main.getName());
} catch (InterruptedException ex) {
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
}
main.interrupt(); // stop backoff
boolean joined = false;
while (!joined)
try {
Log.i(Helper.TAG, "Joining " + main.getName());
main.join();
joined = true;
Log.i(Helper.TAG, "Joined " + main.getName());
} catch (InterruptedException ex) {
Log.e(Helper.TAG, main.getName() + " join " + ex.toString());
}
main = null;
main = null;
}
} }
private BroadcastReceiver outboxReceiver = new BroadcastReceiver() { private BroadcastReceiver outboxReceiver = new BroadcastReceiver() {
@ -1500,7 +1536,7 @@ public class ServiceSynchronize extends LifecycleService {
} }
} }
}; };
};
}
public static void start(Context context) { public static void start(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
@ -1544,6 +1580,8 @@ public class ServiceSynchronize extends LifecycleService {
public void quit() { public void quit() {
Log.i(Helper.TAG, "Service quit"); Log.i(Helper.TAG, "Service quit");
serviceManager.stop();
Log.i(Helper.TAG, "Service quited");
stopSelf(); stopSelf();
} }
@ -1578,11 +1616,14 @@ public class ServiceSynchronize extends LifecycleService {
if (exists) { if (exists) {
Log.i(Helper.TAG, "Service stopping"); Log.i(Helper.TAG, "Service stopping");
try {
semaphore.acquire();
} catch (InterruptedException ex) {
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
}
boolean acquired = false;
while (!acquired)
try {
semaphore.acquire();
acquired = true;
} catch (InterruptedException ex) {
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
}
context.getApplicationContext().unbindService(connection); context.getApplicationContext().unbindService(connection);
} }


Loading…
Cancel
Save