diff --git a/bramble-android/src/main/AndroidManifest.xml b/bramble-android/src/main/AndroidManifest.xml index 3bcd2608e..9a4bd9810 100644 --- a/bramble-android/src/main/AndroidManifest.xml +++ b/bramble-android/src/main/AndroidManifest.xml @@ -16,6 +16,8 @@ android:label="@string/app_name" android:supportsRtl="true"> + + diff --git a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java index c8494e6fa..e0087bed5 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java @@ -6,7 +6,7 @@ import org.briarproject.bramble.plugin.tor.CircumventionModule; import org.briarproject.bramble.reporting.ReportingModule; import org.briarproject.bramble.socks.SocksModule; import org.briarproject.bramble.system.AndroidSystemModule; -import org.briarproject.bramble.system.DefaultTaskSchedulerModule; +import org.briarproject.bramble.system.AndroidTaskSchedulerModule; import dagger.Module; @@ -14,8 +14,8 @@ import dagger.Module; AndroidBatteryModule.class, AndroidNetworkModule.class, AndroidSystemModule.class, + AndroidTaskSchedulerModule.class, CircumventionModule.class, - DefaultTaskSchedulerModule.class, ReportingModule.class, SocksModule.class }) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAppComponent.java b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAppComponent.java new file mode 100644 index 000000000..038f65a58 --- /dev/null +++ b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAppComponent.java @@ -0,0 +1,8 @@ +package org.briarproject.bramble; + +import org.briarproject.bramble.api.system.AlarmListener; + +public interface BrambleAppComponent { + + AlarmListener alarmListener(); +} diff --git a/bramble-android/src/main/java/org/briarproject/bramble/BrambleApplication.java b/bramble-android/src/main/java/org/briarproject/bramble/BrambleApplication.java new file mode 100644 index 000000000..e9c8ef991 --- /dev/null +++ b/bramble-android/src/main/java/org/briarproject/bramble/BrambleApplication.java @@ -0,0 +1,6 @@ +package org.briarproject.bramble; + +public interface BrambleApplication { + + BrambleAppComponent getBrambleAppComponent(); +} diff --git a/bramble-android/src/main/java/org/briarproject/bramble/api/system/AlarmListener.java b/bramble-android/src/main/java/org/briarproject/bramble/api/system/AlarmListener.java new file mode 100644 index 000000000..e1dab1d57 --- /dev/null +++ b/bramble-android/src/main/java/org/briarproject/bramble/api/system/AlarmListener.java @@ -0,0 +1,11 @@ +package org.briarproject.bramble.api.system; + +import android.content.Intent; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +@NotNullByDefault +public interface AlarmListener { + + void onAlarm(Intent intent); +} diff --git a/bramble-android/src/main/java/org/briarproject/bramble/system/AlarmConstants.java b/bramble-android/src/main/java/org/briarproject/bramble/system/AlarmConstants.java new file mode 100644 index 000000000..b86be5fc6 --- /dev/null +++ b/bramble-android/src/main/java/org/briarproject/bramble/system/AlarmConstants.java @@ -0,0 +1,18 @@ +package org.briarproject.bramble.system; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +@NotNullByDefault +interface AlarmConstants { + + /** + * Request code for the broadcast intent attached to the periodic alarm. + */ + int REQUEST_ALARM = 1; + + /** + * Key for storing the process ID in the extras of the periodic alarm's + * intent. This allows us to ignore alarms scheduled by dead processes. + */ + String EXTRA_PID = "org.briarproject.bramble.EXTRA_PID"; +} diff --git a/bramble-android/src/main/java/org/briarproject/bramble/system/AlarmReceiver.java b/bramble-android/src/main/java/org/briarproject/bramble/system/AlarmReceiver.java new file mode 100644 index 000000000..c3741a816 --- /dev/null +++ b/bramble-android/src/main/java/org/briarproject/bramble/system/AlarmReceiver.java @@ -0,0 +1,17 @@ +package org.briarproject.bramble.system; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import org.briarproject.bramble.BrambleApplication; + +public class AlarmReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context ctx, Intent intent) { + BrambleApplication app = + (BrambleApplication) ctx.getApplicationContext(); + app.getBrambleAppComponent().alarmListener().onAlarm(intent); + } +} diff --git a/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskScheduler.java b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskScheduler.java new file mode 100644 index 000000000..ae430bcda --- /dev/null +++ b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskScheduler.java @@ -0,0 +1,204 @@ +package org.briarproject.bramble.system; + +import android.annotation.TargetApi; +import android.app.AlarmManager; +import android.app.Application; +import android.app.PendingIntent; +import android.content.Intent; +import android.os.Process; +import android.os.SystemClock; + +import org.briarproject.bramble.api.lifecycle.Service; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.system.AlarmListener; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.bramble.api.system.TaskScheduler; + +import java.util.ArrayList; +import java.util.List; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; +import static android.app.AlarmManager.INTERVAL_FIFTEEN_MINUTES; +import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; +import static android.content.Context.ALARM_SERVICE; +import static android.os.Build.VERSION.SDK_INT; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.logging.Level.INFO; +import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.system.AlarmConstants.EXTRA_PID; +import static org.briarproject.bramble.system.AlarmConstants.REQUEST_ALARM; + +@ThreadSafe +@NotNullByDefault +class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener { + + private static final Logger LOG = + getLogger(AndroidTaskScheduler.class.getName()); + + private static final long TICK_MS = SECONDS.toMillis(10); + private static final long ALARM_MS = INTERVAL_FIFTEEN_MINUTES; + + private final Application app; + private final Clock clock; + private final ScheduledExecutorService scheduledExecutorService; + private final AlarmManager alarmManager; + + private final Object lock = new Object(); + @GuardedBy("lock") + private final Queue tasks = new PriorityQueue<>(); + + AndroidTaskScheduler(Application app, Clock clock, + ScheduledExecutorService scheduledExecutorService) { + this.app = app; + this.clock = clock; + this.scheduledExecutorService = scheduledExecutorService; + alarmManager = (AlarmManager) + requireNonNull(app.getSystemService(ALARM_SERVICE)); + } + + @Override + public void startService() { + scheduledExecutorService.scheduleAtFixedRate(this::runDueTasks, + TICK_MS, TICK_MS, MILLISECONDS); + scheduleAlarm(); + } + + @Override + public void stopService() { + cancelAlarm(); + } + + @Override + public Future schedule(Runnable task, long delay, TimeUnit unit) { + long now = clock.currentTimeMillis(); + long dueMillis = now + MILLISECONDS.convert(delay, unit); + ScheduledTask s = new ScheduledTask(task, dueMillis); + if (dueMillis <= now) { + scheduledExecutorService.execute(s); + } else { + synchronized (lock) { + tasks.add(s); + } + } + return s; + } + + @Override + public Future scheduleAtFixedRate(Runnable task, long delay, + long interval, TimeUnit unit) { + Runnable wrapped = () -> { + scheduleAtFixedRate(task, interval, interval, unit); + task.run(); + }; + return schedule(wrapped, delay, unit); + } + + @Override + public Future scheduleWithFixedDelay(Runnable task, long delay, + long interval, TimeUnit unit) { + Runnable wrapped = () -> { + task.run(); + scheduleWithFixedDelay(task, interval, interval, unit); + }; + return schedule(wrapped, delay, unit); + } + + @Override + public void onAlarm(Intent intent) { + int extraPid = intent.getIntExtra(EXTRA_PID, -1); + int currentPid = Process.myPid(); + if (extraPid == currentPid) { + LOG.info("Alarm"); + rescheduleAlarm(); + runDueTasks(); + } else { + LOG.info("Ignoring alarm with PID " + extraPid + + ", current PID is " + currentPid); + } + } + + private void runDueTasks() { + long now = clock.currentTimeMillis(); + List due = new ArrayList<>(); + synchronized (lock) { + while (true) { + ScheduledTask s = tasks.peek(); + if (s == null || s.dueMillis > now) break; + due.add(tasks.remove()); + } + } + if (LOG.isLoggable(INFO)) { + LOG.info("Running " + due.size() + " due tasks"); + } + for (ScheduledTask s : due) { + if (LOG.isLoggable(INFO)) { + LOG.info("Task is " + (now - s.dueMillis) + " ms overdue"); + } + s.run(); + } + } + + private void scheduleAlarm() { + if (SDK_INT >= 23) scheduleIdleAlarm(); + else scheduleInexactRepeatingAlarm(); + } + + private void rescheduleAlarm() { + if (SDK_INT >= 23) scheduleIdleAlarm(); + } + + private void cancelAlarm() { + alarmManager.cancel(getAlarmPendingIntent()); + } + + private void scheduleInexactRepeatingAlarm() { + alarmManager.setInexactRepeating(ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + ALARM_MS, ALARM_MS, + getAlarmPendingIntent()); + } + + @TargetApi(23) + private void scheduleIdleAlarm() { + alarmManager.setAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + ALARM_MS, + getAlarmPendingIntent()); + } + + private PendingIntent getAlarmPendingIntent() { + Intent i = new Intent(app, AlarmReceiver.class); + i.putExtra(EXTRA_PID, android.os.Process.myPid()); + return PendingIntent.getBroadcast(app, REQUEST_ALARM, i, + FLAG_CANCEL_CURRENT); + } + + private static class ScheduledTask extends FutureTask + implements Comparable { + + private final long dueMillis; + + public ScheduledTask(Runnable runnable, long dueMillis) { + super(runnable, null); + this.dueMillis = dueMillis; + } + + @Override + public int compareTo(ScheduledTask s) { + //noinspection UseCompareMethod + if (dueMillis < s.dueMillis) return -1; + if (dueMillis > s.dueMillis) return 1; + return 0; + } + } +} diff --git a/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskSchedulerModule.java b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskSchedulerModule.java new file mode 100644 index 000000000..f99ab7382 --- /dev/null +++ b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskSchedulerModule.java @@ -0,0 +1,59 @@ +package org.briarproject.bramble.system; + +import android.app.Application; + +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.api.system.AlarmListener; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.bramble.api.system.TaskScheduler; + +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +@Module +public class AndroidTaskSchedulerModule { + + public static class EagerSingletons { + @Inject + AndroidTaskScheduler scheduler; + } + + private final ScheduledExecutorService scheduledExecutorService; + + public AndroidTaskSchedulerModule() { + // Discard tasks that are submitted during shutdown + RejectedExecutionHandler policy = + new ScheduledThreadPoolExecutor.DiscardPolicy(); + scheduledExecutorService = new ScheduledThreadPoolExecutor(1, policy); + } + + @Provides + @Singleton + AndroidTaskScheduler provideAndroidTaskScheduler( + LifecycleManager lifecycleManager, Application app, Clock clock) { + lifecycleManager.registerForShutdown(scheduledExecutorService); + AndroidTaskScheduler scheduler = + new AndroidTaskScheduler(app, clock, scheduledExecutorService); + lifecycleManager.registerService(scheduler); + return scheduler; + } + + @Provides + @Singleton + AlarmListener provideAlarmListener(AndroidTaskScheduler scheduler) { + return scheduler; + } + + @Provides + @Singleton + TaskScheduler provideTaskScheduler(AndroidTaskScheduler scheduler) { + return scheduler; + } +} diff --git a/bramble-android/src/main/java/org/briarproject/bramble/util/RenewableWakeLock.java b/bramble-android/src/main/java/org/briarproject/bramble/util/RenewableWakeLock.java index 1bfa2eb1c..68ed14f72 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/util/RenewableWakeLock.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/util/RenewableWakeLock.java @@ -5,7 +5,7 @@ import android.os.PowerManager; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.system.TaskScheduler; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; @@ -39,7 +39,7 @@ public class RenewableWakeLock { @Nullable private PowerManager.WakeLock wakeLock; // Locking: lock @Nullable - private ScheduledFuture future; // Locking: lock + private Future future; // Locking: lock public RenewableWakeLock(PowerManager powerManager, TaskScheduler scheduler, int levelAndFlags, String tag, diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/system/TaskScheduler.java b/bramble-api/src/main/java/org/briarproject/bramble/api/system/TaskScheduler.java index 2ecd471f1..b2e627ba2 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/system/TaskScheduler.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/system/TaskScheduler.java @@ -2,8 +2,8 @@ package org.briarproject.bramble.api.system; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** @@ -18,17 +18,17 @@ public interface TaskScheduler { /** * See {@link ScheduledExecutorService#schedule(Runnable, long, TimeUnit)}. */ - ScheduledFuture schedule(Runnable task, long delay, TimeUnit unit); + Future schedule(Runnable task, long delay, TimeUnit unit); /** * See {@link ScheduledExecutorService#scheduleAtFixedRate(Runnable, long, long, TimeUnit)}. */ - ScheduledFuture scheduleAtFixedRate(Runnable task, long delay, - long interval, TimeUnit unit); + Future scheduleAtFixedRate(Runnable task, long delay, long interval, + TimeUnit unit); /** * See {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)}. */ - ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay, + Future scheduleWithFixedDelay(Runnable task, long delay, long interval, TimeUnit unit); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PollerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PollerImpl.java index 86f84f899..db5a878a9 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PollerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PollerImpl.java @@ -189,7 +189,7 @@ class PollerImpl implements Poller, EventListener { // it will abort safely when it finds it's been replaced if (scheduled != null) scheduled.future.cancel(false); PollTask task = new PollTask(p, due, randomiseNext); - Future future = scheduler.schedule(() -> + Future future = scheduler.schedule(() -> ioExecutor.execute(task), delay, MILLISECONDS); tasks.put(t, new ScheduledPollTask(task, future)); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/system/TaskSchedulerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/system/TaskSchedulerImpl.java index 3758f2b77..3d8e7d9bd 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/system/TaskSchedulerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/system/TaskSchedulerImpl.java @@ -3,8 +3,8 @@ package org.briarproject.bramble.system; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.system.TaskScheduler; +import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.annotation.concurrent.ThreadSafe; @@ -24,19 +24,18 @@ class TaskSchedulerImpl implements TaskScheduler { } @Override - public ScheduledFuture schedule(Runnable task, long delay, - TimeUnit unit) { + public Future schedule(Runnable task, long delay, TimeUnit unit) { return delegate.schedule(task, delay, unit); } @Override - public ScheduledFuture scheduleAtFixedRate(Runnable task, long delay, + public Future scheduleAtFixedRate(Runnable task, long delay, long interval, TimeUnit unit) { return delegate.scheduleAtFixedRate(task, delay, interval, unit); } @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay, + public Future scheduleWithFixedDelay(Runnable task, long delay, long interval, TimeUnit unit) { return delegate.scheduleWithFixedDelay(task, delay, interval, unit); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java index 01f3ba070..b255e196f 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java @@ -198,7 +198,7 @@ class TransportKeyManagerImpl implements TransportKeyManager { private void scheduleKeyUpdate(long now) { long delay = timePeriodLength - now % timePeriodLength; - scheduler.schedule((Runnable) this::updateKeys, delay, MILLISECONDS); + scheduler.schedule(this::updateKeys, delay, MILLISECONDS); } private void updateKeys() { diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/PollerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/PollerImplTest.java index 130110486..04cd467bd 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/PollerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/PollerImplTest.java @@ -31,7 +31,7 @@ import org.junit.Test; import java.security.SecureRandom; import java.util.List; import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.Future; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -55,8 +55,7 @@ public class PollerImplTest extends BrambleMockTestCase { private final TransportPropertyManager transportPropertyManager = context.mock(TransportPropertyManager.class); private final Clock clock = context.mock(Clock.class); - private final ScheduledFuture future = - context.mock(ScheduledFuture.class); + private final Future future = context.mock(Future.class); private final SecureRandom random; private final Executor ioExecutor = new ImmediateExecutor(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java index 5a9092582..0c4de76ee 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java @@ -2,6 +2,7 @@ package org.briarproject.briar.android; import org.briarproject.bramble.BrambleAndroidEagerSingletons; import org.briarproject.bramble.BrambleAndroidModule; +import org.briarproject.bramble.BrambleAppComponent; import org.briarproject.bramble.BrambleCoreEagerSingletons; import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.account.BriarAccountModule; @@ -73,7 +74,7 @@ import dagger.Component; }) public interface AndroidComponent extends BrambleCoreEagerSingletons, BrambleAndroidEagerSingletons, - BriarCoreEagerSingletons, AndroidEagerSingletons { + BriarCoreEagerSingletons, AndroidEagerSingletons, BrambleAppComponent { // Exposed objects @CryptoExecutor diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java index 0ba14e5ec..09946b3a7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java @@ -3,6 +3,7 @@ package org.briarproject.briar.android; import android.app.Activity; import android.content.SharedPreferences; +import org.briarproject.bramble.BrambleApplication; import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import java.util.Collection; @@ -12,7 +13,7 @@ import java.util.logging.LogRecord; * This exists so that the Application object will not necessarily be cast * directly to the Briar application object. */ -public interface BriarApplication { +public interface BriarApplication extends BrambleApplication { Class ENTRY_ACTIVITY = NavDrawerActivity.class; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java index 493a542a7..0ae3159ee 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java @@ -18,6 +18,7 @@ import org.acra.ACRA; import org.acra.ReportingInteractionMode; import org.acra.annotation.ReportsCrashes; import org.briarproject.bramble.BrambleAndroidEagerSingletons; +import org.briarproject.bramble.BrambleAppComponent; import org.briarproject.bramble.BrambleCoreEagerSingletons; import org.briarproject.briar.BriarCoreEagerSingletons; import org.briarproject.briar.BuildConfig; @@ -170,6 +171,11 @@ public class BriarApplicationImpl extends Application StrictMode.setVmPolicy(vmPolicy.build()); } + @Override + public BrambleAppComponent getBrambleAppComponent() { + return applicationComponent; + } + @Override public Collection getRecentLogRecords() { return logHandler.getRecentLogRecords(); diff --git a/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java b/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java index 862521667..7535d5cc4 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java @@ -8,6 +8,7 @@ import com.vanniktech.emoji.EmojiManager; import com.vanniktech.emoji.google.GoogleEmojiProvider; import org.briarproject.bramble.BrambleAndroidEagerSingletons; +import org.briarproject.bramble.BrambleAppComponent; import org.briarproject.bramble.BrambleCoreEagerSingletons; import org.briarproject.briar.BriarCoreEagerSingletons; @@ -53,6 +54,11 @@ public class TestBriarApplication extends Application EmojiManager.install(new GoogleEmojiProvider()); } + @Override + public BrambleAppComponent getBrambleAppComponent() { + return applicationComponent; + } + @Override public Collection getRecentLogRecords() { return emptyList();