From 4f32b994fabedea13c252f5e3094b3bca743d2d4 Mon Sep 17 00:00:00 2001 From: adbenitez Date: Mon, 15 Dec 2025 16:29:13 +0100 Subject: [PATCH 1/4] do accounts migration in background move accounts migration to background thread and wait for it in foreground to avoid ANR in background in ApplicationContext --- .../securesms/ApplicationContext.java | 112 ++++++++++++++++-- .../securesms/ConversationActivity.java | 3 +- .../components/reminder/DozeReminder.java | 2 +- .../securesms/connect/AccountManager.java | 6 +- .../securesms/connect/DcEventCenter.java | 2 +- .../securesms/connect/DcHelper.java | 6 +- .../notifications/NotificationCenter.java | 12 +- .../service/FetchForegroundService.java | 6 +- 8 files changed, 120 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 78f7107bd..39c35684e 100644 --- a/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -55,10 +55,13 @@ import chat.delta.rpc.RpcException; public class ApplicationContext extends MultiDexApplication { private static final String TAG = ApplicationContext.class.getSimpleName(); + private static final Object initLock = new Object(); + private static volatile boolean isInitialized = false; + + private static DcAccounts dcAccounts; + private Rpc rpc; + private DcContext dcContext; - public static DcAccounts dcAccounts; - public Rpc rpc; - public DcContext dcContext; public DcLocationManager dcLocationManager; public DcEventCenter eventCenter; public NotificationCenter notificationCenter; @@ -73,6 +76,71 @@ public class ApplicationContext extends MultiDexApplication { return (ApplicationContext)context.getApplicationContext(); } + /** + * Get DcAccounts instance, waiting for initialization if necessary. + * This method is thread-safe and will block until initialization is complete. + */ + public static DcAccounts getDcAccounts() { + synchronized (initLock) { + while (!isInitialized) { + try { + initLock.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted while waiting for DcAccounts initialization", e); + } + } + return dcAccounts; + } + } + + /** + * Get Rpc instance, waiting for initialization if necessary. + * This method is thread-safe and will block until initialization is complete. + */ + public Rpc getRpc() { + synchronized (initLock) { + while (!isInitialized) { + try { + initLock.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted while waiting for Rpc initialization", e); + } + } + return rpc; + } + } + + /** + * Get DcContext instance, waiting for initialization if necessary. + * This method is thread-safe and will block until initialization is complete. + */ + public DcContext getDcContext() { + synchronized (initLock) { + while (!isInitialized) { + try { + initLock.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted while waiting for DcContext initialization", e); + } + } + return dcContext; + } + } + + /** + * Set DcContext instance. This should only be called by AccountManager when switching accounts, + * which only happens after initial initialization is complete. + * This method is thread-safe but does NOT trigger initialization or notify waiting threads. + */ + public void setDcContext(DcContext dcContext) { + synchronized (initLock) { + this.dcContext = dcContext; + } + } + @Override public void onCreate() { super.onCreate(); @@ -88,17 +156,17 @@ public class ApplicationContext extends MultiDexApplication { System.loadLibrary("native-utils"); + // Initialize DcAccounts in background to avoid ANR during SQL migrations + Util.runOnBackground(() -> { + synchronized (initLock) { + try { + dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath()); Log.i(TAG, "DcAccounts created"); rpc = new Rpc(new FFITransport(dcAccounts.getJsonrpcInstance())); Log.i(TAG, "Rpc created"); AccountManager.getInstance().migrateToDcAccounts(this); - // October-2025 migration: delete deprecated "permanent channel" id - NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); - notificationManager.deleteNotificationChannel("dc_foreground_notification_ch"); - // end October-2025 migration - int[] allAccounts = dcAccounts.getAll(); Log.i(TAG, "Number of profiles: " + allAccounts.length); for (int accountId : allAccounts) { @@ -130,6 +198,14 @@ public class ApplicationContext extends MultiDexApplication { dcContext = dcAccounts.getSelectedAccount(); notificationCenter = new NotificationCenter(this); eventCenter = new DcEventCenter(this); + + // Mark as initialized before starting threads that depend on it + isInitialized = true; + initLock.notifyAll(); + Log.i(TAG, "DcAccounts initialization complete"); + + dcLocationManager = new DcLocationManager(this); // depends on dcContext + new Thread(() -> { Log.i(TAG, "Starting event loop"); DcEventEmitter emitter = dcAccounts.getEventEmitter(); @@ -149,6 +225,21 @@ public class ApplicationContext extends MultiDexApplication { dcAccounts.startIo(); + } catch (Exception e) { + Log.e(TAG, "Fatal error during DcAccounts initialization", e); + // Mark as initialized even on error to avoid deadlock + isInitialized = true; + initLock.notifyAll(); + throw new RuntimeException("Failed to initialize DcAccounts", e); + } + } + }); + + // October-2025 migration: delete deprecated "permanent channel" id + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); + notificationManager.deleteNotificationChannel("dc_foreground_notification_ch"); + // end October-2025 migration + new ForegroundDetector(ApplicationContext.getInstance(this)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { @@ -158,7 +249,7 @@ public class ApplicationContext extends MultiDexApplication { @Override public void onAvailable(@NonNull android.net.Network network) { Log.i("DeltaChat", "++++++++++++++++++ NetworkCallback.onAvailable() #" + debugOnAvailableCount++); - dcAccounts.maybeNetwork(); + getDcAccounts().maybeNetwork(); } @Override @@ -187,7 +278,6 @@ public class ApplicationContext extends MultiDexApplication { initializeJobManager(); InChatSounds.getInstance(this); - dcLocationManager = new DcLocationManager(this); DynamicTheme.setDefaultDayNightMode(this); IntentFilter filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); @@ -236,6 +326,8 @@ public class ApplicationContext extends MultiDexApplication { "WebxdcGarbageCollectionWorker", ExistingPeriodicWorkPolicy.KEEP, webxdcGarbageCollectionRequest); + + Log.i("DeltaChat", "+++++++++++ ApplicationContext.onCreate() finished ++++++++++"); } public JobManager getJobManager() { diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java index 737bfcfa4..e4b1f9f89 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java @@ -955,8 +955,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity int accountId = getIntent().getIntExtra(ACCOUNT_ID_EXTRA, dcContext.getAccountId()); if (accountId != dcContext.getAccountId()) { AccountManager.getInstance().switchAccount(context, accountId); - dcContext = context.dcContext; - fragment.dcContext = context.dcContext; + fragment.dcContext = dcContext = context.getDcContext(); initializeBackground(); } chatId = getIntent().getIntExtra(CHAT_ID_EXTRA, -1); diff --git a/src/main/java/org/thoughtcrime/securesms/components/reminder/DozeReminder.java b/src/main/java/org/thoughtcrime/securesms/components/reminder/DozeReminder.java index 611dc8a6b..f0c2a57ad 100644 --- a/src/main/java/org/thoughtcrime/securesms/components/reminder/DozeReminder.java +++ b/src/main/java/org/thoughtcrime/securesms/components/reminder/DozeReminder.java @@ -110,7 +110,7 @@ public class DozeReminder { } private static boolean isPushAvailableAndSufficient() { - return ApplicationContext.dcAccounts.isAllChatmail() + return ApplicationContext.getDcAccounts().isAllChatmail() && FcmReceiveService.getToken() != null; } diff --git a/src/main/java/org/thoughtcrime/securesms/connect/AccountManager.java b/src/main/java/org/thoughtcrime/securesms/connect/AccountManager.java index 12d98b51c..7c5268dcb 100644 --- a/src/main/java/org/thoughtcrime/securesms/connect/AccountManager.java +++ b/src/main/java/org/thoughtcrime/securesms/connect/AccountManager.java @@ -31,7 +31,7 @@ public class AccountManager { private void resetDcContext(Context context) { ApplicationContext appContext = (ApplicationContext)context.getApplicationContext(); - appContext.dcContext = ApplicationContext.dcAccounts.getSelectedAccount(); + appContext.setDcContext(ApplicationContext.getDcAccounts().getSelectedAccount()); DcHelper.setStockTranslations(context); DirectShareUtil.resetAllShortcuts(appContext); } @@ -55,7 +55,7 @@ public class AccountManager { for (File file : files) { // old accounts have the pattern "messenger*.db" if (!file.isDirectory() && file.getName().startsWith("messenger") && file.getName().endsWith(".db")) { - int accountId = ApplicationContext.dcAccounts.migrateAccount(file.getAbsolutePath()); + int accountId = ApplicationContext.getDcAccounts().migrateAccount(file.getAbsolutePath()); if (accountId != 0) { String selName = PreferenceManager.getDefaultSharedPreferences(context) .getString("curr_account_db_name", "messenger.db"); @@ -70,7 +70,7 @@ public class AccountManager { } if (selectAccountId != 0) { - ApplicationContext.dcAccounts.selectAccount(selectAccountId); + ApplicationContext.getDcAccounts().selectAccount(selectAccountId); } } catch (Exception e) { Log.e(TAG, "Error in migrateToDcAccounts()", e); diff --git a/src/main/java/org/thoughtcrime/securesms/connect/DcEventCenter.java b/src/main/java/org/thoughtcrime/securesms/connect/DcEventCenter.java index 4262dbd6c..560f67888 100644 --- a/src/main/java/org/thoughtcrime/securesms/connect/DcEventCenter.java +++ b/src/main/java/org/thoughtcrime/securesms/connect/DcEventCenter.java @@ -220,7 +220,7 @@ public class DcEventCenter { break; } - if (accountId != context.dcContext.getAccountId()) { + if (accountId != context.getDcContext().getAccountId()) { return 0; } diff --git a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java index 2f0b6163e..a524a764d 100644 --- a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java +++ b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java @@ -64,15 +64,15 @@ public class DcHelper { public static final String CONFIG_PRIVATE_TAG = "private_tag"; public static DcContext getContext(@NonNull Context context) { - return ApplicationContext.getInstance(context).dcContext; + return ApplicationContext.getInstance(context).getDcContext(); } public static Rpc getRpc(@NonNull Context context) { - return ApplicationContext.getInstance(context).rpc; + return ApplicationContext.getInstance(context).getRpc(); } public static DcAccounts getAccounts(@NonNull Context context) { - return ApplicationContext.getInstance(context).dcAccounts; + return ApplicationContext.getInstance(context).getDcAccounts(); } public static DcEventCenter getEventCenter(@NonNull Context context) { diff --git a/src/main/java/org/thoughtcrime/securesms/notifications/NotificationCenter.java b/src/main/java/org/thoughtcrime/securesms/notifications/NotificationCenter.java index 11ef6a130..7bdd7b18d 100644 --- a/src/main/java/org/thoughtcrime/securesms/notifications/NotificationCenter.java +++ b/src/main/java/org/thoughtcrime/securesms/notifications/NotificationCenter.java @@ -426,7 +426,7 @@ public class NotificationCenter { public void notifyCall(int accId, int callId, String payload) { Util.runOnAnyBackgroundThread(() -> { NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - DcContext dcContext = context.dcAccounts.getAccount(accId); + DcContext dcContext = context.getDcAccounts().getAccount(accId); int chatId = dcContext.getMsg(callId).getChatId(); DcChat dcChat = dcContext.getChat(chatId); String name = dcChat.getName(); @@ -478,7 +478,7 @@ public class NotificationCenter { public void notifyMessage(int accountId, int chatId, int msgId) { Util.runOnAnyBackgroundThread(() -> { - DcContext dcContext = context.dcAccounts.getAccount(accountId); + DcContext dcContext = context.getDcAccounts().getAccount(accountId); DcChat dcChat = dcContext.getChat(chatId); DcMsg dcMsg = dcContext.getMsg(msgId); @@ -508,7 +508,7 @@ public class NotificationCenter { public void notifyReaction(int accountId, int contactId, int msgId, String reaction) { Util.runOnAnyBackgroundThread(() -> { - DcContext dcContext = context.dcAccounts.getAccount(accountId); + DcContext dcContext = context.getDcAccounts().getAccount(accountId); DcMsg dcMsg = dcContext.getMsg(msgId); NotificationPrivacyPreference privacy = Prefs.getNotificationPrivacy(context); @@ -530,7 +530,7 @@ public class NotificationCenter { return; // showing "New Message" is wrong, just do nothing. } - DcContext dcContext = context.dcAccounts.getAccount(accountId); + DcContext dcContext = context.getDcAccounts().getAccount(accountId); DcMsg dcMsg = dcContext.getMsg(msgId); DcMsg parentMsg; if(dcMsg.getType() == DcMsg.DC_MSG_WEBXDC) { @@ -554,7 +554,7 @@ public class NotificationCenter { @WorkerThread private void maybeAddNotification(int accountId, DcChat dcChat, int msgId, String shortLine, String tickerLine, boolean playInChatSound, boolean isMention) { - DcContext dcContext = context.dcAccounts.getAccount(accountId); + DcContext dcContext = context.getDcAccounts().getAccount(accountId); int chatId = dcChat.getId(); ChatData chatData = new ChatData(accountId, chatId); isMention = isMention && dcContext.isMentionsEnabled(); @@ -602,7 +602,7 @@ public class NotificationCenter { } String accountTag = dcContext.getConfig(CONFIG_PRIVATE_TAG); - if (accountTag.isEmpty() && context.dcAccounts.getAll().length > 1) { + if (accountTag.isEmpty() && context.getDcAccounts().getAll().length > 1) { accountTag = dcContext.getName(); } diff --git a/src/main/java/org/thoughtcrime/securesms/service/FetchForegroundService.java b/src/main/java/org/thoughtcrime/securesms/service/FetchForegroundService.java index 29b71c2c7..02ef10f17 100644 --- a/src/main/java/org/thoughtcrime/securesms/service/FetchForegroundService.java +++ b/src/main/java/org/thoughtcrime/securesms/service/FetchForegroundService.java @@ -45,7 +45,7 @@ public final class FetchForegroundService extends Service { // we need to handle the message within 20s, and the time window may be even shorter than 20s, // so, use 10s to be safe. fetchingSynchronously = true; - if (ApplicationContext.dcAccounts.backgroundFetch(10)) { + if (ApplicationContext.getDcAccounts().backgroundFetch(10)) { // The background fetch was successful, but we need to wait until all events were processed. // After all events were processed, we will get DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE, // and stop() will be called. @@ -92,7 +92,7 @@ public final class FetchForegroundService extends Service { Util.runOnAnyBackgroundThread(() -> { Log.i(TAG, "Starting fetch"); - if (!ApplicationContext.dcAccounts.backgroundFetch(300)) { // as startForeground() was called, there is time + if (!ApplicationContext.getDcAccounts().backgroundFetch(300)) { // as startForeground() was called, there is time FetchForegroundService.stop(this); } // else we stop FetchForegroundService on DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE }); @@ -111,7 +111,7 @@ public final class FetchForegroundService extends Service { @Override public void onTimeout(int startId, int fgsType) { - ApplicationContext.dcAccounts.stopBackgroundFetch(); + ApplicationContext.getDcAccounts().stopBackgroundFetch(); stopSelf(); } From af7d915d14537c7580ca6779812f55b77244e34a Mon Sep 17 00:00:00 2001 From: adbenitez Date: Mon, 15 Dec 2025 17:20:33 +0100 Subject: [PATCH 2/4] fix gplay flavor --- .../securesms/notifications/FcmReceiveService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gplay/java/org/thoughtcrime/securesms/notifications/FcmReceiveService.java b/src/gplay/java/org/thoughtcrime/securesms/notifications/FcmReceiveService.java index 9affbc1ed..6ba0ee1cf 100644 --- a/src/gplay/java/org/thoughtcrime/securesms/notifications/FcmReceiveService.java +++ b/src/gplay/java/org/thoughtcrime/securesms/notifications/FcmReceiveService.java @@ -62,7 +62,7 @@ public class FcmReceiveService extends FirebaseMessagingService { prefixedToken = addPrefix(rawToken); Log.i(TAG, "FCM token: " + prefixedToken); - ApplicationContext.dcAccounts.setPushDeviceToken(prefixedToken); + ApplicationContext.getDcAccounts().setPushDeviceToken(prefixedToken); triedRegistering = true; }); } @@ -104,6 +104,6 @@ public class FcmReceiveService extends FirebaseMessagingService { public void onNewToken(@NonNull String rawToken) { prefixedToken = addPrefix(rawToken); Log.i(TAG, "new FCM token: " + prefixedToken); - ApplicationContext.dcAccounts.setPushDeviceToken(prefixedToken); + ApplicationContext.getDcAccounts().setPushDeviceToken(prefixedToken); } } From 09f66caadcb190db7cf0ad27c7ea06697571ffab Mon Sep 17 00:00:00 2001 From: adbenitez Date: Mon, 15 Dec 2025 17:23:49 +0100 Subject: [PATCH 3/4] indent code --- .../securesms/ApplicationContext.java | 114 +++++++++--------- 1 file changed, 56 insertions(+), 58 deletions(-) diff --git a/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 39c35684e..d2d776b67 100644 --- a/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -160,71 +160,69 @@ public class ApplicationContext extends MultiDexApplication { Util.runOnBackground(() -> { synchronized (initLock) { try { + dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath()); + Log.i(TAG, "DcAccounts created"); + rpc = new Rpc(new FFITransport(dcAccounts.getJsonrpcInstance())); + Log.i(TAG, "Rpc created"); + AccountManager.getInstance().migrateToDcAccounts(this); - dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath()); - Log.i(TAG, "DcAccounts created"); - rpc = new Rpc(new FFITransport(dcAccounts.getJsonrpcInstance())); - Log.i(TAG, "Rpc created"); - AccountManager.getInstance().migrateToDcAccounts(this); + int[] allAccounts = dcAccounts.getAll(); + Log.i(TAG, "Number of profiles: " + allAccounts.length); + for (int accountId : allAccounts) { + DcContext ac = dcAccounts.getAccount(accountId); + if (!ac.isOpen()) { + try { + DatabaseSecret secret = DatabaseSecretProvider.getOrCreateDatabaseSecret(this, accountId); + boolean res = ac.open(secret.asString()); + if (res) Log.i(TAG, "Successfully opened account " + accountId + ", path: " + ac.getBlobdir()); + else Log.e(TAG, "Error opening account " + accountId + ", path: " + ac.getBlobdir()); + } catch (Exception e) { + Log.e(TAG, "Failed to open account " + accountId + ", path: " + ac.getBlobdir() + ": " + e); + e.printStackTrace(); + } + } - int[] allAccounts = dcAccounts.getAll(); - Log.i(TAG, "Number of profiles: " + allAccounts.length); - for (int accountId : allAccounts) { - DcContext ac = dcAccounts.getAccount(accountId); - if (!ac.isOpen()) { - try { - DatabaseSecret secret = DatabaseSecretProvider.getOrCreateDatabaseSecret(this, accountId); - boolean res = ac.open(secret.asString()); - if (res) Log.i(TAG, "Successfully opened account " + accountId + ", path: " + ac.getBlobdir()); - else Log.e(TAG, "Error opening account " + accountId + ", path: " + ac.getBlobdir()); - } catch (Exception e) { - Log.e(TAG, "Failed to open account " + accountId + ", path: " + ac.getBlobdir() + ": " + e); - e.printStackTrace(); - } - } + // 2025.11.12: this is needed until core starts ignoring "delete_server_after" for chatmail + if (ac.isChatmail()) { + ac.setConfig("delete_server_after", null); // reset + } + } + if (allAccounts.length == 0) { + try { + rpc.addAccount(); + } catch (RpcException e) { + e.printStackTrace(); + } + } + dcContext = dcAccounts.getSelectedAccount(); + notificationCenter = new NotificationCenter(this); + eventCenter = new DcEventCenter(this); - // 2025.11.12: this is needed until core starts ignoring "delete_server_after" for chatmail - if (ac.isChatmail()) { - ac.setConfig("delete_server_after", null); // reset - } - } - if (allAccounts.length == 0) { - try { - rpc.addAccount(); - } catch (RpcException e) { - e.printStackTrace(); - } - } - dcContext = dcAccounts.getSelectedAccount(); - notificationCenter = new NotificationCenter(this); - eventCenter = new DcEventCenter(this); + // Mark as initialized before starting threads that depend on it + isInitialized = true; + initLock.notifyAll(); + Log.i(TAG, "DcAccounts initialization complete"); - // Mark as initialized before starting threads that depend on it - isInitialized = true; - initLock.notifyAll(); - Log.i(TAG, "DcAccounts initialization complete"); + dcLocationManager = new DcLocationManager(this); // depends on dcContext - dcLocationManager = new DcLocationManager(this); // depends on dcContext + new Thread(() -> { + Log.i(TAG, "Starting event loop"); + DcEventEmitter emitter = dcAccounts.getEventEmitter(); + Log.i(TAG, "DcEventEmitter obtained"); + while (true) { + DcEvent event = emitter.getNextEvent(); + if (event==null) { + break; + } + eventCenter.handleEvent(event); + } + Log.i("DeltaChat", "shutting down event handler"); + }, "eventThread").start(); - new Thread(() -> { - Log.i(TAG, "Starting event loop"); - DcEventEmitter emitter = dcAccounts.getEventEmitter(); - Log.i(TAG, "DcEventEmitter obtained"); - while (true) { - DcEvent event = emitter.getNextEvent(); - if (event==null) { - break; - } - eventCenter.handleEvent(event); - } - Log.i("DeltaChat", "shutting down event handler"); - }, "eventThread").start(); - - // set translations before starting I/O to avoid sending untranslated MDNs (issue #2288) - DcHelper.setStockTranslations(this); - - dcAccounts.startIo(); + // set translations before starting I/O to avoid sending untranslated MDNs (issue #2288) + DcHelper.setStockTranslations(this); + dcAccounts.startIo(); } catch (Exception e) { Log.e(TAG, "Fatal error during DcAccounts initialization", e); // Mark as initialized even on error to avoid deadlock From ca110c6d748d67cb5068203163a3918fe9c46cbf Mon Sep 17 00:00:00 2001 From: adbenitez Date: Mon, 15 Dec 2025 17:28:48 +0100 Subject: [PATCH 4/4] improve code --- .../securesms/ApplicationContext.java | 44 +++++++------------ 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index d2d776b67..4bbdf0fee 100644 --- a/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -76,40 +76,35 @@ public class ApplicationContext extends MultiDexApplication { return (ApplicationContext)context.getApplicationContext(); } - /** - * Get DcAccounts instance, waiting for initialization if necessary. - * This method is thread-safe and will block until initialization is complete. - */ - public static DcAccounts getDcAccounts() { + private static void ensureInitialized() { synchronized (initLock) { while (!isInitialized) { try { initLock.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw new RuntimeException("Interrupted while waiting for DcAccounts initialization", e); + throw new RuntimeException("Interrupted while waiting for initialization", e); } } - return dcAccounts; } } + /** + * Get DcAccounts instance, waiting for initialization if necessary. + * This method is thread-safe and will block until initialization is complete. + */ + public static DcAccounts getDcAccounts() { + ensureInitialized(); + return dcAccounts; + } + /** * Get Rpc instance, waiting for initialization if necessary. * This method is thread-safe and will block until initialization is complete. */ public Rpc getRpc() { - synchronized (initLock) { - while (!isInitialized) { - try { - initLock.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException("Interrupted while waiting for Rpc initialization", e); - } - } - return rpc; - } + ensureInitialized(); + return rpc; } /** @@ -117,17 +112,8 @@ public class ApplicationContext extends MultiDexApplication { * This method is thread-safe and will block until initialization is complete. */ public DcContext getDcContext() { - synchronized (initLock) { - while (!isInitialized) { - try { - initLock.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException("Interrupted while waiting for DcContext initialization", e); - } - } - return dcContext; - } + ensureInitialized(); + return dcContext; } /**