Merge pull request #1383 from deltachat/async-jobs

use async core
This commit is contained in:
bjoern
2020-05-28 15:22:33 +02:00
committed by GitHub
11 changed files with 236 additions and 406 deletions
+1 -1
View File
@@ -90,7 +90,7 @@ android {
defaultConfig {
versionCode 582
versionName "1.8.1"
versionName "1.9.2"
applicationId "com.b44t.messenger"
multiDexEnabled true
+100 -154
View File
@@ -173,14 +173,6 @@ static uint32_t* jintArray2uint32Pointer(JNIEnv* env, jintArray ja, int* ret_icn
******************************************************************************/
typedef struct dc_jnicontext_t {
JavaVM* jvm; // JNIEnv cannot be shared between threads, so we share the JavaVM object
jclass cls;
jobject obj;
jmethodID methodId;
} dc_jnicontext_t;
static dc_context_t* get_dc_context(JNIEnv *env, jobject obj)
{
static jfieldID fid = 0;
@@ -195,48 +187,18 @@ static dc_context_t* get_dc_context(JNIEnv *env, jobject obj)
}
static uintptr_t s_context_callback_(dc_context_t* context, int event, uintptr_t data1, uintptr_t data2)
JNIEXPORT jlong Java_com_b44t_messenger_DcContext_createContextCPtr(JNIEnv *env, jobject obj, jstring osname, jstring dbfile)
{
jlong l;
JNIEnv* env;
dc_jnicontext_t* jnicontext = dc_get_userdata(context);
if (jnicontext==NULL || jnicontext->jvm==NULL || jnicontext->cls==NULL || jnicontext->obj==NULL || jnicontext->methodId==NULL) {
return 0; /* may happen on startup */
}
(*jnicontext->jvm)->GetEnv(jnicontext->jvm, (void**)&env, JNI_VERSION_1_6); // as this function may be called from _any_ thread, we cannot use a static pointer to JNIEnv
if (env==NULL) {
return 0; /* may happen on startup */
}
l = (*env)->CallLongMethod(env, jnicontext->obj, jnicontext->methodId, (jint)event, (jlong)data1, (jlong)data2);
return (uintptr_t)l;
}
JNIEXPORT jlong Java_com_b44t_messenger_DcContext_createContextCPtr(JNIEnv *env, jobject obj, jstring osname)
{
jclass cls = (*env)->GetObjectClass(env, obj);
dc_jnicontext_t* jnicontext = calloc(1, sizeof(dc_jnicontext_t));
if (cls==NULL || jnicontext==NULL) {
return 0;
}
(*env)->GetJavaVM(env, &jnicontext->jvm);
jnicontext->cls = (*env)->NewGlobalRef(env, cls);
jnicontext->obj = (*env)->NewGlobalRef(env, obj);
jnicontext->methodId = (*env)->GetMethodID(env, jnicontext->cls, "handleEvent","(IJJ)J" /*signature as "(param)ret" with I=int, J=long*/);
CHAR_REF(osname);
jlong contextCPtr = (jlong)dc_context_new(s_context_callback_, jnicontext, osnamePtr);
CHAR_REF(dbfile)
jlong contextCPtr = (jlong)dc_context_new(osnamePtr, dbfilePtr, NULL);
CHAR_UNREF(dbfile)
CHAR_UNREF(osname);
return contextCPtr;
}
JNIEXPORT void Java_com_b44t_messenger_DcContact_unrefContextCPtr(JNIEnv *env, jobject obj)
JNIEXPORT void Java_com_b44t_messenger_DcContext_unrefContextCPtr(JNIEnv *env, jobject obj)
{
dc_context_unref(get_dc_context(env, obj));
}
@@ -244,21 +206,6 @@ JNIEXPORT void Java_com_b44t_messenger_DcContact_unrefContextCPtr(JNIEnv *env, j
/* DcContext - open/configure/connect/fetch */
JNIEXPORT jint Java_com_b44t_messenger_DcContext_open(JNIEnv *env, jobject obj, jstring dbfile)
{
CHAR_REF(dbfile);
jint ret = dc_open(get_dc_context(env, obj), dbfilePtr, NULL);
CHAR_UNREF(dbfile)
return ret;
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_close(JNIEnv *env, jobject obj)
{
dc_close(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_setStockTranslation(JNIEnv *env, jobject obj, jint stock_id, jstring translation)
{
CHAR_REF(translation);
@@ -303,93 +250,21 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_isConfigured(JNIEnv *env, jobje
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performImapJobs(JNIEnv *env, jobject obj)
JNIEXPORT void Java_com_b44t_messenger_DcContext_startIo(JNIEnv *env, jobject obj)
{
dc_perform_imap_jobs(get_dc_context(env, obj));
dc_start_io(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performImapIdle(JNIEnv *env, jobject obj)
JNIEXPORT void Java_com_b44t_messenger_DcContext_stopIo(JNIEnv *env, jobject obj)
{
dc_perform_imap_idle(get_dc_context(env, obj));
dc_stop_io(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performImapFetch(JNIEnv *env, jobject obj)
JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_isIoRunning(JNIEnv *env, jobject obj)
{
dc_perform_imap_fetch(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_interruptImapIdle(JNIEnv *env, jobject obj)
{
dc_interrupt_imap_idle(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performSentboxJobs(JNIEnv *env, jobject obj)
{
dc_perform_sentbox_jobs(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performSentboxFetch(JNIEnv *env, jobject obj)
{
dc_perform_sentbox_fetch(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performSentboxIdle(JNIEnv *env, jobject obj)
{
dc_perform_sentbox_idle(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_interruptSentboxIdle(JNIEnv *env, jobject obj)
{
dc_interrupt_sentbox_idle(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performMvboxJobs(JNIEnv *env, jobject obj)
{
dc_perform_mvbox_jobs(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performMvboxFetch(JNIEnv *env, jobject obj)
{
dc_perform_mvbox_fetch(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performMvboxIdle(JNIEnv *env, jobject obj)
{
dc_perform_mvbox_idle(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_interruptMvboxIdle(JNIEnv *env, jobject obj)
{
dc_interrupt_mvbox_idle(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performSmtpJobs(JNIEnv *env, jobject obj)
{
dc_perform_smtp_jobs(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_performSmtpIdle(JNIEnv *env, jobject obj)
{
dc_perform_smtp_idle(get_dc_context(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_interruptSmtpIdle(JNIEnv *env, jobject obj)
{
dc_interrupt_smtp_idle(get_dc_context(env, obj));
return dc_is_io_running(get_dc_context(env, obj)) != 0;
}
@@ -398,6 +273,11 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_maybeNetwork(JNIEnv *env, jobje
dc_maybe_network(get_dc_context(env, obj));
}
JNIEXPORT jlong Java_com_b44t_messenger_DcContext_getEventEmitterCPtr(JNIEnv *env, jobject obj)
{
return (jlong)dc_get_event_emitter(get_dc_context(env, obj));
}
/* DcContext - handle contacts */
@@ -890,6 +770,89 @@ JNIEXPORT jlong Java_com_b44t_messenger_DcContext_getProviderFromEmailCPtr(JNIEn
}
/*******************************************************************************
* DcEventEmitter
******************************************************************************/
static dc_event_emitter_t* get_dc_event_emitter(JNIEnv *env, jobject obj)
{
static jfieldID fid = 0;
if (fid==0) {
jclass cls = (*env)->GetObjectClass(env, obj);
fid = (*env)->GetFieldID(env, cls, "eventEmitterCPtr", "J" /*Signature, J=long*/);
}
if (fid) {
return (dc_event_emitter_t*)(*env)->GetLongField(env, obj, fid);
}
return NULL;
}
JNIEXPORT void Java_com_b44t_messenger_DcEventEmitter_unrefEventEmitterCPtr(JNIEnv *env, jobject obj)
{
dc_event_emitter_unref(get_dc_event_emitter(env, obj));
}
JNIEXPORT jlong Java_com_b44t_messenger_DcEventEmitter_getNextEventCPtr(JNIEnv *env, jobject obj)
{
return (jlong)dc_get_next_event(get_dc_event_emitter(env, obj));
}
/*******************************************************************************
* DcEvent
******************************************************************************/
static dc_event_t* get_dc_event(JNIEnv *env, jobject obj)
{
static jfieldID fid = 0;
if (fid==0) {
jclass cls = (*env)->GetObjectClass(env, obj);
fid = (*env)->GetFieldID(env, cls, "eventCPtr", "J" /*Signature, J=long*/);
}
if (fid) {
return (dc_event_t*)(*env)->GetLongField(env, obj, fid);
}
return NULL;
}
JNIEXPORT void Java_com_b44t_messenger_DcEvent_unrefEventCPtr(JNIEnv *env, jobject obj)
{
dc_event_unref(get_dc_event(env, obj));
}
JNIEXPORT jint Java_com_b44t_messenger_DcEvent_getId(JNIEnv *env, jobject obj)
{
return dc_event_get_id(get_dc_event(env, obj));
}
JNIEXPORT jint Java_com_b44t_messenger_DcEvent_getData1Int(JNIEnv *env, jobject obj)
{
return dc_event_get_data1_int(get_dc_event(env, obj));
}
JNIEXPORT jint Java_com_b44t_messenger_DcEvent_getData2Int(JNIEnv *env, jobject obj)
{
return dc_event_get_data2_int(get_dc_event(env, obj));
}
JNIEXPORT jstring Java_com_b44t_messenger_DcEvent_getData2Str(JNIEnv *env, jobject obj)
{
char* temp = dc_event_get_data2_str(get_dc_event(env, obj));
jstring ret = JSTRING_NEW(temp);
dc_str_unref(temp);
return ret;
}
/*******************************************************************************
* DcArray
******************************************************************************/
@@ -1653,29 +1616,12 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcProvider_getOverviewPage(JNIEnv *env
return ret;
}
/*******************************************************************************
* Tools
******************************************************************************/
JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_data1IsString(JNIEnv *env, jclass cls, jint event)
{
return DC_EVENT_DATA1_IS_STRING(event);
}
JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_data2IsString(JNIEnv *env, jclass cls, jint event)
{
return DC_EVENT_DATA2_IS_STRING(event);
}
JNIEXPORT jstring Java_com_b44t_messenger_DcContext_dataToString(JNIEnv *env, jclass cls, jlong data)
{
/* the callback may return a long that represents a pointer to a C-String; this function creates a Java-string from such values. */
if (data==0) {
return NULL;
}
const char* cstring = (const char*)data;
return JSTRING_NEW(cstring);
}
+20 -33
View File
@@ -72,44 +72,37 @@ public class DcContext {
public final static int DC_EMPTY_MVBOX = 0x01;
public final static int DC_EMPTY_INBOX = 0x02;
public DcContext(String osName) {
handleEvent(0,0,0); // call handleEvent() to make sure it is not optimized away and JNI won't find it
contextCPtr = createContextCPtr(osName);
public DcContext(String osName, String dbfile) {
contextCPtr = createContextCPtr(osName, dbfile);
}
public boolean isOk() {
return contextCPtr != 0;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
unrefContextCPtr();
contextCPtr = 0;
unref();
}
public native int open (String dbfile);
public native void close ();
public void unref() {
if (contextCPtr != 0) {
unrefContextCPtr();
contextCPtr = 0;
}
}
public DcEventEmitter getEventEmitter () { return new DcEventEmitter(getEventEmitterCPtr()); }
public native void setStockTranslation (int stockId, String translation);
public native String getBlobdir ();
public native void configure ();
public native void stopOngoingProcess ();
public native int isConfigured ();
public native void performImapJobs ();
public native void performImapFetch ();
public native void performImapIdle ();
public native void interruptImapIdle ();
public native void performMvboxJobs ();
public native void performMvboxFetch ();
public native void performMvboxIdle ();
public native void interruptMvboxIdle ();
public native void performSentboxJobs ();
public native void performSentboxFetch ();
public native void performSentboxIdle ();
public native void interruptSentboxIdle ();
public native void performSmtpJobs ();
public native void performSmtpIdle ();
public native void interruptSmtpIdle ();
public native void startIo ();
public native void stopIo ();
public native boolean isIoRunning ();
public native void maybeNetwork ();
public native void setConfig (String key, String value);
@@ -189,20 +182,14 @@ public class DcContext {
*/
public native boolean setLocation (float latitude, float longitude, float accuracy);
// event handling - you should @Override this function in derived classes
public long handleEvent(int event, long data1, long data2) {
return 0;
}
// helper to get/return strings from/to handleEvent()
public native static boolean data1IsString(int event);
public native static boolean data2IsString(int event);
public native static String dataToString (long data);
// working with raw c-data
private long contextCPtr; // CAVE: the name is referenced in the JNI
private native long createContextCPtr(String osName);
private native long createContextCPtr(String osName, String dbfile);
private native void unrefContextCPtr ();
private native long getEventEmitterCPtr();
public native long createMsgCPtr (int viewtype);
private native long getChatlistCPtr (int listflags, String query, int queryId);
private native long getChatCPtr (int chat_id);
+23
View File
@@ -0,0 +1,23 @@
package com.b44t.messenger;
public class DcEvent {
public DcEvent(long eventCPtr) {
this.eventCPtr = eventCPtr;
}
@Override protected void finalize() throws Throwable {
super.finalize();
unrefEventCPtr();
eventCPtr = 0;
}
public native int getId ();
public native int getData1Int ();
public native int getData2Int ();
public native String getData2Str ();
// working with raw c-data
private long eventCPtr; // CAVE: the name is referenced in the JNI
private native void unrefEventCPtr();
}
@@ -0,0 +1,24 @@
package com.b44t.messenger;
public class DcEventEmitter {
public DcEventEmitter(long eventEmitterCPtr) {
this.eventEmitterCPtr = eventEmitterCPtr;
}
@Override protected void finalize() throws Throwable {
super.finalize();
unrefEventEmitterCPtr();
eventEmitterCPtr = 0;
}
public DcEvent getNextEvent () {
long eventCPtr = getNextEventCPtr();
return eventCPtr == 0 ? null : new DcEvent(eventCPtr);
}
// working with raw c-data
private long eventEmitterCPtr; // CAVE: the name is referenced in the JNI
private native long getNextEventCPtr ();
private native void unrefEventEmitterCPtr();
}
@@ -511,8 +511,10 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve
// calling configure() results in
// receiving multiple DC_EVENT_CONFIGURE_PROGRESS events
DcHelper.getContext(this).captureNextError();
DcHelper.getContext(this).configure();
ApplicationDcContext dcContext = DcHelper.getContext(this);
dcContext.stopIo();
dcContext.captureNextError();
dcContext.configure();
}
private void setConfig(@IdRes int viewId, String configTarget, boolean doTrim) {
@@ -534,6 +536,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve
ApplicationDcContext dcContext = DcHelper.getContext(this);
long progress = (Long)data1;
if (progress==0/*error/aborted*/) {
dcContext.maybeStartIo(); // start-io is also needed on errors to make previous config work in case of changes
dcContext.endCaptureNextError();
progressDialog.dismiss();
if (dcContext.hasCapturedError()) {
@@ -555,6 +558,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve
progressDialog.setMessage(getResources().getString(R.string.one_moment)+String.format(" %d%%", percent));
}
else if (progress==1000/*done*/) {
dcContext.maybeStartIo();
dcContext.endCaptureNextError();
progressDialog.dismiss();
Intent conversationList = new Intent(getApplicationContext(), ConversationListActivity.class);
@@ -147,6 +147,7 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen
// calling configure() results in
// receiving multiple DC_EVENT_CONFIGURE_PROGRESS events
dcContext.stopIo();
dcContext.configure();
}
@@ -192,12 +193,14 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen
progressUpdate((int)progress);
}
else if (progress==1000/*done*/) {
dcContext.maybeStartIo();
progressSuccess(false);
}
}
else if (manualConfigure && eventId==DcContext.DC_EVENT_CONFIGURE_PROGRESS) {
long progress = (Long)data1;
if (progress==1000/*done*/) {
dcContext.maybeStartIo();
finish(); // remove ourself from the activity stack (finishAffinity is available in API 16, we're targeting API 14)
}
}
@@ -65,8 +65,8 @@ public class AccountManager {
private @Nullable Account maybeGetAccount(File file) {
try {
if (!file.isDirectory() && file.getName().endsWith(".db")) {
DcContext testContext = new DcContext(null);
if (testContext.open(file.getAbsolutePath()) != 0) {
DcContext testContext = new DcContext(null, file.getAbsolutePath());
if (testContext.isOk()) {
Account ret = new Account();
ret.dbName = file.getName();
ret.displayname = testContext.getConfig("displayname");
@@ -98,8 +98,9 @@ public class AccountManager {
// create an empty DcContext object - this will be set up then, starting with
// getSelectedAccount()
ApplicationContext appContext = (ApplicationContext)context.getApplicationContext();
appContext.dcContext.stopThreads();
appContext.dcContext.close();
appContext.dcContext.notificationCenter.removeAllNotifiations();
appContext.dcContext.stopIo();
appContext.dcContext.unref();
appContext.dcContext = new ApplicationDcContext(context);
}
@@ -243,10 +244,10 @@ public class AccountManager {
if (deleteDbName!=null) {
// used to delete the previous account, however, as a resilience check, make sure,
// we do not delete already configured accounts (just in case sth. changes the flow of activities)
DcContext testContext = new DcContext(null);
if (testContext.open(new File(activity.getFilesDir(), deleteDbName).getAbsolutePath()) != 0) {
DcContext testContext = new DcContext(null, new File(activity.getFilesDir(), deleteDbName).getAbsolutePath());
if (testContext.isOk()) {
if (testContext.isConfigured() == 0) {
testContext.close();
testContext.unref();
AccountManager.getInstance().deleteAccount(activity, deleteDbName);
PreferenceManager.getDefaultSharedPreferences(activity)
.edit().putString("prev_account_db_name", "").apply();
@@ -9,7 +9,6 @@ import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.PowerManager;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
@@ -20,10 +19,11 @@ import android.webkit.MimeTypeMap;
import android.widget.Toast;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcChatlist;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcEventCenter;
import com.b44t.messenger.DcEventEmitter;
import com.b44t.messenger.DcLot;
import com.b44t.messenger.DcMsg;
@@ -38,7 +38,6 @@ import org.thoughtcrime.securesms.util.Util;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class ApplicationDcContext extends DcContext {
@@ -56,12 +55,9 @@ public class ApplicationDcContext extends DcContext {
public NotificationCenter notificationCenter;
public ApplicationDcContext(Context context) {
super("Android "+BuildConfig.VERSION_NAME);
super("Android "+BuildConfig.VERSION_NAME, AccountManager.getInstance().getSelectedAccount(context).getAbsolutePath());
this.context = context;
File dbfile = AccountManager.getInstance().getSelectedAccount(context);
open(dbfile.getAbsolutePath());
// migration, can be removed after some versions (added 5/2020)
// (this will convert only for one account, but that is fine, multi-account is experimental anyway)
try {
@@ -103,27 +99,26 @@ public class ApplicationDcContext extends DcContext {
}
// /migration
try {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
imapWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "imapWakeLock");
imapWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting
mvboxWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mvboxWakeLock");
mvboxWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting
sentboxWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "sentboxWakeLock");
sentboxWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting
smtpWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "smtpWakeLock");
smtpWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting
} catch (Exception e) {
Log.e(TAG, "Cannot create wakeLocks");
}
new Thread(() -> {
DcEventEmitter emitter = getEventEmitter();
while (true) {
DcEvent event = emitter.getNextEvent();
if (event==null) {
break;
}
handleEvent(event);
}
Log.i(TAG, "shutting down event handler");
}, "eventThread").start();
notificationCenter = new NotificationCenter(this);
startThreads(0);
maybeStartIo();
}
public void maybeStartIo() {
if (isConfigured()!=0) {
startIo();
}
}
public void setStockTranslations() {
@@ -313,166 +308,6 @@ public class ApplicationDcContext extends DcContext {
chat.getVisibility(), verified, chat.isSendingLocations(), chat.isMuted(), summary);
}
/***********************************************************************************************
* Working Threads
**********************************************************************************************/
private final Object threadsCritical = new Object();
private final Object incLoopsCritical= new Object();
public Thread imapThread = null;
private PowerManager.WakeLock imapWakeLock = null;
private int inboxLoops = 0;
public Thread mvboxThread = null;
private PowerManager.WakeLock mvboxWakeLock = null;
private int mvboxLoops = 0;
public Thread sentboxThread = null;
private PowerManager.WakeLock sentboxWakeLock = null;
public Thread smtpThread = null;
private PowerManager.WakeLock smtpWakeLock = null;
private int smtpLoops = 0;
public final static int INTERRUPT_IDLE = 0x01; // interrupt idle if the thread is already running
public boolean run = true;
public void startThreads(int flags) {
synchronized (threadsCritical) {
if (imapThread == null || !imapThread.isAlive()) {
imapThread = new Thread(() -> {
Log.i(TAG, "###################### IMAP-Thread started. ######################");
while (run) {
imapWakeLock.acquire();
performImapJobs();
performImapFetch();
imapWakeLock.release();
synchronized (incLoopsCritical) {
inboxLoops++;
}
performImapIdle();
}
Log.i(TAG, "!!!!!!!!!!!! IMAP-Thread stopped");
}, "imapThread");
imapThread.setPriority(Thread.NORM_PRIORITY);
imapThread.start();
} else {
if ((flags & INTERRUPT_IDLE) != 0) {
interruptImapIdle();
}
}
if (mvboxThread == null || !mvboxThread.isAlive()) {
mvboxThread = new Thread(() -> {
Log.i(TAG, "###################### MVBOX-Thread started. ######################");
while (run) {
mvboxWakeLock.acquire();
performMvboxJobs();
performMvboxFetch();
mvboxWakeLock.release();
synchronized (incLoopsCritical) {
mvboxLoops++;
}
performMvboxIdle();
}
Log.i(TAG, "!!!!!!!!!!!! MVBOX-Thread stopped");
}, "mvboxThread");
mvboxThread.setPriority(Thread.NORM_PRIORITY);
mvboxThread.start();
} else {
if ((flags & INTERRUPT_IDLE) != 0) {
interruptMvboxIdle();
}
}
if (sentboxThread == null || !sentboxThread.isAlive()) {
sentboxThread = new Thread(() -> {
Log.i(TAG, "###################### SENTBOX-Thread started. ######################");
while (run) {
sentboxWakeLock.acquire();
performSentboxJobs();
performSentboxFetch();
sentboxWakeLock.release();
performSentboxIdle();
}
Log.i(TAG, "!!!!!!!!!!!! SENTBOX-Thread stopped");
}, "sentboxThread");
sentboxThread.setPriority(Thread.NORM_PRIORITY-1);
sentboxThread.start();
} else {
if ((flags & INTERRUPT_IDLE) != 0) {
interruptSentboxIdle();
}
}
if (smtpThread == null || !smtpThread.isAlive()) {
smtpThread = new Thread(() -> {
Log.i(TAG, "###################### SMTP-Thread started. ######################");
while (run) {
smtpWakeLock.acquire();
performSmtpJobs();
smtpWakeLock.release();
synchronized (incLoopsCritical) {
smtpLoops++;
}
performSmtpIdle();
}
Log.i(TAG, "!!!!!!!!!!!! SMTP-Thread stopped");
}, "smtpThread");
smtpThread.setPriority(Thread.MAX_PRIORITY);
smtpThread.start();
}
}
}
public void waitForThreadsExecutedOnce() {
while(true) {
synchronized (incLoopsCritical) {
if(inboxLoops>0 && mvboxLoops>0 && smtpLoops>0) {
break;
}
}
Util.sleep(500);
}
}
public void stopThreads() {
notificationCenter.removeAllNotifiations();
run = false;
synchronized (threadsCritical) {
while (true) {
// in theory, interrupting once outside the loop should be sufficient,
// but there are some corner cases, see https://github.com/deltachat/deltachat-core-rust/issues/925
Log.i(TAG, "!!!!!!!!!!!! Stopping threads ...");
if (imapThread!=null && imapThread.isAlive()) { interruptImapIdle(); }
if (mvboxThread!=null && mvboxThread.isAlive()) { interruptMvboxIdle(); }
if (sentboxThread!=null && sentboxThread.isAlive()) { interruptSentboxIdle(); }
if (smtpThread!=null && smtpThread.isAlive()) { interruptSmtpIdle(); }
Util.sleep(300);
if ( (imapThread==null || !imapThread.isAlive())
&& (mvboxThread==null || !mvboxThread.isAlive())
&& (sentboxThread==null || !sentboxThread.isAlive())
&& (smtpThread==null || !smtpThread.isAlive())) {
break;
}
}
}
Log.i(TAG, "!!!!!!!!!!!! threads stopped");
}
/***********************************************************************************************
* Tools
**********************************************************************************************/
@@ -557,41 +392,41 @@ public class ApplicationDcContext extends DcContext {
});
}
@Override
public long handleEvent(final int event, long data1, long data2) {
switch (event) {
public long handleEvent(DcEvent event) {
int id = event.getId();
switch (id) {
case DC_EVENT_INFO:
Log.i(TAG, dataToString(data2));
Log.i(TAG, event.getData2Str());
break;
case DC_EVENT_WARNING:
Log.w(TAG, dataToString(data2));
Log.w(TAG, event.getData2Str());
break;
case DC_EVENT_ERROR:
handleError(event, true, dataToString(data2));
handleError(id, true, event.getData2Str());
break;
case DC_EVENT_ERROR_NETWORK:
handleError(event, data1 != 0, dataToString(data2));
handleError(id, event.getData1Int() != 0, event.getData2Str());
break;
case DC_EVENT_ERROR_SELF_NOT_IN_GROUP:
handleError(event, true, dataToString(data2));
handleError(id, true, event.getData2Str());
break;
case DC_EVENT_INCOMING_MSG:
notificationCenter.addNotification((int) data1, (int) data2);
notificationCenter.addNotification(event.getData1Int(), event.getData2Int());
if (eventCenter != null) {
eventCenter.sendToObservers(event, data1, data2); // Other parts of the code are also interested in this event
eventCenter.sendToObservers(id, (long)event.getData1Int(), (long)event.getData2Int());
}
break;
default: {
final Object data1obj = data1IsString(event) ? dataToString(data1) : data1;
final Object data2obj = data2IsString(event) ? dataToString(data2) : data2;
final Object data1obj = (long)event.getData1Int();
final Object data2obj = data2IsString(id) ? event.getData2Str() : (long)event.getData2Int();
if (eventCenter != null) {
eventCenter.sendToObservers(event, data1obj, data2obj);
eventCenter.sendToObservers(id, data1obj, data2obj);
}
}
break;
@@ -7,6 +7,8 @@ import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import org.thoughtcrime.securesms.util.Util;
public class FetchWorker extends Worker {
private @NonNull Context context;
@@ -17,19 +19,24 @@ public class FetchWorker extends Worker {
this.context = context;
}
// doWork() is called in a background thread;
// once we return, Worker is considered to have finished and will be destroyed,
// this does not necessarily mean, that the app is killed, we may or may not keep running,
// therefore we do not stopIo() here.
@Override
public @NonNull Result doWork() {
// MAYBE TODO:
// - when no threads are running: fetch-inbox, maybe fetch-mvbox, do smtp-jobs.
// fetch-sendbox is not needed as these messages shall not be notified.
// - when threads are running: interrupt-all-idle
Log.i("DeltaChat", "-------------------- FetchWorker.doWork() started --------------------");
ApplicationDcContext dcContext = DcHelper.getContext(context);
dcContext.startThreads(ApplicationDcContext.INTERRUPT_IDLE);
dcContext.waitForThreadsExecutedOnce();
Log.i("DeltaChat", "-------------------- FetchWorker.doWork() done --------------------");
dcContext.maybeStartIo();
return Result.success(); // when returning, the os may terminate the app again
// as we do not know when startIo() has done it's work or if is even doable in one step,
// we go the easy way and just wait for some amount of time.
// the core has to handle interrupts at any point anyway,
// and work also maybe continued when doWork() returns.
// however, we should not wait too long here to avoid getting bad battery ratings.
Util.sleep(60 * 1000);
Log.i("DeltaChat", "-------------------- FetchWorker.doWork() done --------------------");
return Result.success();
}
}
@@ -37,7 +37,7 @@ public class ForegroundDetector implements Application.ActivityLifecycleCallback
public void onActivityStarted(Activity activity) {
refs++;
application.dcContext.startThreads(0);
application.dcContext.maybeStartIo();
}