Simplified error handling

pull/156/head
M66B 5 years ago
parent b5921b8390
commit dea368dd96

@ -2917,10 +2917,12 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
db.message().deleteMessage(id);
db.folder().setFolderError(message.folder, null);
db.identity().setIdentityError(message.identity, null);
if (message.identity != null) {
db.identity().setIdentityError(message.identity, null);
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel("send", message.identity.intValue());
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel("send:" + message.identity, 1);
}
} else
EntityOperation.queue(context, message, EntityOperation.DELETE);
@ -3091,7 +3093,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
if (message.identity != null) {
// Identity can be deleted
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel("send", message.identity.intValue());
nm.cancel("send:" + message.identity, 1);
}
return null;

@ -428,12 +428,12 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
} catch (IOException ex) {
if (ex.getCause() instanceof MessagingException) {
Log.w(browsable.name + " boundary server", ex);
db.folder().setFolderError(browsable.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(browsable.id, Helper.formatThrowable(ex));
} else
throw ex;
} catch (Throwable ex) {
Log.e(browsable.name + " boundary server", ex);
db.folder().setFolderError(browsable.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(browsable.id, Helper.formatThrowable(ex));
} finally {
((IMAPMessage) isub[j]).invalidateHeaders();
}

@ -49,7 +49,6 @@ import com.sun.mail.imap.IMAPStore;
import com.sun.mail.imap.protocol.FetchResponse;
import com.sun.mail.imap.protocol.IMAPProtocol;
import com.sun.mail.imap.protocol.UID;
import com.sun.mail.util.MailConnectException;
import org.json.JSONArray;
import org.json.JSONException;
@ -64,8 +63,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -82,7 +79,6 @@ import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import javax.mail.Address;
import javax.mail.AuthenticationFailedException;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Folder;
@ -91,10 +87,8 @@ import javax.mail.FolderNotFoundException;
import javax.mail.Message;
import javax.mail.MessageRemovedException;
import javax.mail.MessagingException;
import javax.mail.SendFailedException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.StoreClosedException;
import javax.mail.UIDFolder;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
@ -104,7 +98,6 @@ import javax.mail.search.MessageIDTerm;
import javax.mail.search.OrTerm;
import javax.mail.search.ReceivedDateTerm;
import javax.mail.search.SearchTerm;
import javax.net.ssl.SSLException;
import me.leolin.shortcutbadger.ShortcutBadger;
@ -253,11 +246,10 @@ class Core {
db.operation().deleteOperation(op.id);
} catch (Throwable ex) {
Log.e(folder.name, ex);
reportError(context, account, folder, ex);
db.operation().setOperationError(op.id, Helper.formatThrowable(ex));
if (message != null && !(ex instanceof IllegalArgumentException))
db.message().setMessageError(message.id, Helper.formatThrowable(ex, true));
db.message().setMessageError(message.id, Helper.formatThrowable(ex));
if (ex instanceof OutOfMemoryError ||
ex instanceof MessageRemovedException ||
@ -1053,8 +1045,7 @@ class Core {
Log.w(folder.name, ex);
} catch (Throwable ex) {
Log.e(folder.name, ex);
reportError(context, account, folder, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
}
if (uids.size() > 0) {
@ -1159,12 +1150,12 @@ class Core {
} catch (IOException ex) {
if (ex.getCause() instanceof MessagingException) {
Log.w(folder.name, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
} else
throw ex;
} catch (Throwable ex) {
Log.e(folder.name, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
} finally {
// Free memory
((IMAPMessage) isub[j]).invalidateHeaders();
@ -2117,58 +2108,17 @@ class Core {
builder.setSound(uri);
}
static void reportError(Context context, EntityAccount account, EntityFolder folder, Throwable ex) {
// FolderClosedException: can happen when no connectivity
// FolderClosedException: can happen when no connectivity
// IllegalStateException:
// - "This operation is not allowed on a closed folder"
// - can happen when syncing message
// IllegalStateException:
// - "This operation is not allowed on a closed folder"
// - can happen when syncing message
// ConnectionException
// - failed to create new store connection (connectivity)
// ConnectionException
// - failed to create new store connection (connectivity)
// MailConnectException
// - on connectivity problems when connecting to store
String title;
if (account == null)
title = Helper.localizeFolderName(context, folder.name);
else if (folder == null)
title = account.name;
else
title = account.name + "/" + Helper.localizeFolderName(context, folder.name);
String tag = "error:" + (account == null ? 0 : account.id) + ":" + (folder == null ? 0 : folder.id);
EntityLog.log(context, title + " " + Helper.formatThrowable(ex));
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (nm == null)
return;
if (ex instanceof AuthenticationFailedException || // Also: Too many simultaneous connections
ex instanceof AlertException ||
ex instanceof SendFailedException)
nm.notify(tag, 1, getNotificationError(context, "error", title, ex).build());
// connection failure: Too many simultaneous connections
if (BuildConfig.DEBUG &&
!(ex instanceof SendFailedException) &&
!(ex instanceof MailConnectException) &&
!(ex instanceof FolderClosedException) &&
!(ex instanceof IllegalStateException) &&
!(ex instanceof StoreClosedException) &&
!(ex instanceof UnknownHostException) &&
!(ex instanceof MessageRemovedException) &&
!(ex instanceof MessagingException && ex.getCause() instanceof UnknownHostException) &&
!(ex instanceof MessagingException && ex.getCause() instanceof ConnectionException) &&
!(ex instanceof MessagingException && ex.getCause() instanceof SocketException) &&
!(ex instanceof MessagingException && ex.getCause() instanceof SocketTimeoutException) &&
!(ex instanceof MessagingException && ex.getCause() instanceof SSLException) &&
!(ex instanceof MessagingException && "connection failure".equals(ex.getMessage())))
nm.notify(tag, 1, getNotificationError(context, "error", title, ex).build());
}
// MailConnectException
// - on connectivity problems when connecting to store
static NotificationCompat.Builder getNotificationError(Context context, String channel, String title, Throwable ex) {
// Build pending intent
@ -2192,7 +2142,7 @@ class Core {
.setVisibility(NotificationCompat.VISIBILITY_SECRET);
builder.setStyle(new NotificationCompat.BigTextStyle()
.bigText(Helper.formatThrowable(ex, false, "\n")));
.bigText(Helper.formatThrowable(ex, "\n")));
return builder;
}

@ -1027,7 +1027,7 @@ public class FragmentAccount extends FragmentBase {
if (!synchronize) {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel("receive", account.id.intValue());
nm.cancel("receive:" + account.id, 1);
}
return false;

@ -19,6 +19,7 @@ package eu.faircode.email;
Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@ -755,6 +756,11 @@ public class FragmentIdentity extends FragmentBase {
db.endTransaction();
}
if (!synchronize) {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel("send:" + identity.id, 1);
}
return false;
}

@ -382,33 +382,24 @@ public class Helper {
}
static String formatThrowable(Throwable ex) {
return formatThrowable(ex, false, " ");
return formatThrowable(ex, " ");
}
static String formatThrowable(Throwable ex, boolean sanitize) {
return formatThrowable(ex, sanitize, " ");
}
static String formatThrowable(Throwable ex, boolean sanitize, String separator) {
if (sanitize) {
if (ex instanceof MessageRemovedException)
return null;
if (ex instanceof IOException &&
ex.getCause() instanceof MessageRemovedException)
return null;
static String formatThrowable(Throwable ex, String separator) {
if (ex instanceof MessageRemovedException)
return null;
if (ex instanceof FolderClosedException)
return null;
if (ex instanceof IOException &&
ex.getCause() instanceof MessageRemovedException)
return null;
if (ex instanceof IllegalStateException &&
("Not connected".equals(ex.getMessage()) ||
"This operation is not allowed on a closed folder".equals(ex.getMessage())))
return null;
if (ex instanceof FolderClosedException)
return null;
//if (ex instanceof MailConnectException && ex.getCause() instanceof UnknownHostException)
// return null;
}
if (ex instanceof IllegalStateException &&
("Not connected".equals(ex.getMessage()) ||
"This operation is not allowed on a closed folder".equals(ex.getMessage())))
return null;
StringBuilder sb = new StringBuilder();
if (BuildConfig.DEBUG)

@ -51,6 +51,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.mail.Address;
import javax.mail.AuthenticationFailedException;
import javax.mail.Message;
import javax.mail.MessageRemovedException;
import javax.mail.MessagingException;
@ -150,7 +151,6 @@ public class ServiceSend extends LifecycleService {
db.operation().deleteOperation(op.id);
} catch (Throwable ex) {
Log.e(outbox.name, ex);
Core.reportError(ServiceSend.this, null, outbox, ex);
db.operation().setOperationError(op.id, Helper.formatThrowable(ex));
if (message != null)
@ -174,7 +174,7 @@ public class ServiceSend extends LifecycleService {
}
} catch (Throwable ex) {
Log.e(outbox.name, ex);
db.folder().setFolderError(outbox.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(outbox.id, Helper.formatThrowable(ex));
} finally {
db.folder().setFolderState(outbox.id, null);
db.folder().setFolderSyncState(outbox.id, null);
@ -294,6 +294,9 @@ public class ServiceSend extends LifecycleService {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean debug = prefs.getBoolean("debug", false);
if (message.identity == null)
throw new IllegalArgumentException("Identity removed");
EntityIdentity ident = db.identity().getIdentity(message.identity);
String protocol = ident.getProtocol();
@ -410,45 +413,28 @@ public class ServiceSend extends LifecycleService {
db.identity().setIdentityError(ident.id, null);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel("send", message.identity.intValue());
nm.cancel("send:" + message.identity, 1);
} catch (MessagingException ex) {
// Caused by: com.sun.mail.smtp.SMTPAddressFailedException: 554 Refused. Sending to remote addresses (relaying) is not allowed.
/*
if (ex instanceof SendFailedException) {
SendFailedException sfe = (SendFailedException) ex;
StringBuilder sb = new StringBuilder();
sb.append(sfe.getMessage());
sb.append(' ').append(getString(R.string.title_address_sent));
sb.append(' ').append(MessageHelper.formatAddresses(sfe.getValidSentAddresses()));
sb.append(' ').append(getString(R.string.title_address_unsent));
sb.append(' ').append(MessageHelper.formatAddresses(sfe.getValidUnsentAddresses()));
Log.e(ex);
sb.append(' ').append(getString(R.string.title_address_invalid));
sb.append(' ').append(MessageHelper.formatAddresses(sfe.getInvalidAddresses()));
ex = new SendFailedException(
sb.toString(),
sfe.getNextException(),
sfe.getValidSentAddresses(),
sfe.getValidUnsentAddresses(),
sfe.getInvalidAddresses());
}
*/
db.identity().setIdentityError(ident.id, Helper.formatThrowable(ex));
EntityLog.log(this, ident.name + " last attempt: " + new Date(message.last_attempt));
if (ex instanceof AuthenticationFailedException ||
ex instanceof SendFailedException) {
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify("send:" + message.identity, 1,
Core.getNotificationError(this, "error", ident.name, ex)
.build());
throw ex;
}
long now = new Date().getTime();
long delayed = now - message.last_attempt;
if (delayed > IDENTITY_ERROR_AFTER * 60 * 1000L || ex instanceof SendFailedException) {
Log.i("Reporting send error after=" + delayed);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify("send", message.identity.intValue(),
Core.getNotificationError(this, "error", ident.name, ex).build());
nm.notify("send:" + message.identity, 1,
Core.getNotificationError(this, "warning", ident.name, ex).build());
}
throw ex;

@ -65,6 +65,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import javax.mail.AuthenticationFailedException;
import javax.mail.FetchProfile;
import javax.mail.Folder;
import javax.mail.FolderClosedException;
@ -597,10 +598,16 @@ public class ServiceSynchronize extends LifecycleService {
String message = e.getMessage();
Log.w(account.name + " alert: " + message);
db.account().setAccountError(account.id, message);
if (BuildConfig.DEBUG ||
(message != null && !message.startsWith("Too many simultaneous connections")))
Core.reportError(ServiceSynchronize.this, account, null,
new Core.AlertException(message));
if (message != null && !message.startsWith("Too many simultaneous connections")) {
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify("receive:" + account.id, 1,
Core.getNotificationError(
ServiceSynchronize.this, "warning", account.name,
new Core.AlertException(message))
.build());
}
state.error(null);
} finally {
wlFolder.release();
@ -698,6 +705,14 @@ public class ServiceSynchronize extends LifecycleService {
try {
ConnectionHelper.connect(this, istore, account);
} catch (Throwable ex) {
if (ex instanceof AuthenticationFailedException) {
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify("receive:" + account.id, 1,
Core.getNotificationError(this, "error", account.name, ex)
.build());
throw ex;
}
// Report account connection error
if (account.last_connected != null && !ConnectionHelper.airplaneMode(this)) {
EntityLog.log(this, account.name + " last connected: " + new Date(account.last_connected));
@ -710,7 +725,7 @@ public class ServiceSynchronize extends LifecycleService {
SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
.format(account.last_connected)), ex);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify("receive", account.id.intValue(),
nm.notify("receive:" + account.id, 1,
Core.getNotificationError(this, "warning", account.name, warning)
.build());
}
@ -746,14 +761,14 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderReadOnly(folder.id, false);
} catch (ReadOnlyFolderException ex) {
Log.w(folder.name + " read only");
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
try {
ifolder.open(Folder.READ_ONLY);
db.folder().setFolderReadOnly(folder.id, true);
} catch (MessagingException ex1) {
Log.w(ex1);
db.folder().setFolderState(folder.id, null);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex1, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex1));
continue;
}
} catch (FolderNotFoundException ex) {
@ -763,10 +778,10 @@ public class ServiceSynchronize extends LifecycleService {
} catch (MessagingException ex) {
Log.w(ex);
db.folder().setFolderState(folder.id, null);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
continue;
} catch (Throwable ex) {
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
throw ex;
}
mapFolders.put(folder, ifolder);
@ -818,19 +833,18 @@ public class ServiceSynchronize extends LifecycleService {
} catch (IOException ex) {
if (ex.getCause() instanceof MessagingException) {
Log.w(folder.name, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
} else
throw ex;
} catch (Throwable ex) {
Log.e(folder.name, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
}
int count = ifolder.getMessageCount();
db.folder().setFolderTotal(folder.id, count < 0 ? null : count);
} catch (Throwable ex) {
Log.e(folder.name, ex);
Core.reportError(ServiceSynchronize.this, account, folder, ex);
state.error(ex);
} finally {
wlMessage.release();
@ -859,8 +873,7 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderTotal(folder.id, count < 0 ? null : count);
} catch (Throwable ex) {
Log.e(folder.name, ex);
Core.reportError(ServiceSynchronize.this, account, folder, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
state.error(ex);
} finally {
wlMessage.release();
@ -902,16 +915,15 @@ public class ServiceSynchronize extends LifecycleService {
} catch (IOException ex) {
if (ex.getCause() instanceof MessagingException) {
Log.w(folder.name, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
} else
throw ex;
} catch (Throwable ex) {
Log.e(folder.name, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
}
} catch (Throwable ex) {
Log.e(folder.name, ex);
Core.reportError(ServiceSynchronize.this, account, folder, ex);
state.error(ex);
} finally {
wlMessage.release();
@ -1008,8 +1020,7 @@ public class ServiceSynchronize extends LifecycleService {
} catch (Throwable ex) {
Log.e(folder.name, ex);
Core.reportError(ServiceSynchronize.this, account, folder, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
state.error(ex);
} finally {
if (shouldClose) {
@ -1081,7 +1092,7 @@ public class ServiceSynchronize extends LifecycleService {
db.account().setAccountWarning(account.id, capIdle ? null : getString(R.string.title_no_idle));
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel("receive", account.id.intValue());
nm.cancel("receive:" + account.id, 1);
// Schedule keep alive alarm
EntityLog.log(this, account.name + " wait=" + account.poll_interval);
@ -1121,7 +1132,6 @@ public class ServiceSynchronize extends LifecycleService {
Log.w(ex);
} catch (Throwable ex) {
Log.e(account.name, ex);
Core.reportError(this, account, null, ex);
db.account().setAccountError(account.id, Helper.formatThrowable(ex));
} finally {
// Stop watching for operations

@ -539,10 +539,6 @@
<string name="title_zoom">Text size</string>
<string name="title_select_all">Select all</string>
<string name="title_address_sent">Sent:</string>
<string name="title_address_unsent">Unsent:</string>
<string name="title_address_invalid">Invalid:</string>
<string name="title_previous">Previous</string>
<string name="title_next">Next</string>

Loading…
Cancel
Save