mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
240 lines
7.6 KiB
Java
240 lines
7.6 KiB
Java
package org.briarproject.android;
|
|
|
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
|
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
|
import static java.util.logging.Level.WARNING;
|
|
import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
|
import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
|
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.logging.Logger;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import org.briarproject.R;
|
|
import org.briarproject.api.ContactId;
|
|
import org.briarproject.api.android.AndroidExecutor;
|
|
import org.briarproject.api.android.AndroidNotificationManager;
|
|
import org.briarproject.api.db.DatabaseComponent;
|
|
import org.briarproject.api.db.DatabaseConfig;
|
|
import org.briarproject.api.db.DatabaseExecutor;
|
|
import org.briarproject.api.db.DbException;
|
|
import org.briarproject.api.event.Event;
|
|
import org.briarproject.api.event.EventListener;
|
|
import org.briarproject.api.event.MessageAddedEvent;
|
|
import org.briarproject.api.lifecycle.LifecycleManager;
|
|
import org.briarproject.api.lifecycle.LifecycleManager.StartResult;
|
|
import org.briarproject.api.messaging.GroupId;
|
|
|
|
import roboguice.service.RoboService;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.ComponentName;
|
|
import android.content.Intent;
|
|
import android.content.ServiceConnection;
|
|
import android.os.Binder;
|
|
import android.os.IBinder;
|
|
import android.support.v4.app.NotificationCompat;
|
|
|
|
public class BriarService extends RoboService implements EventListener {
|
|
|
|
private static final int ONGOING_NOTIFICATION_ID = 1;
|
|
private static final int FAILURE_NOTIFICATION_ID = 2;
|
|
|
|
private static final Logger LOG =
|
|
Logger.getLogger(BriarService.class.getName());
|
|
|
|
private final AtomicBoolean created = new AtomicBoolean(false);
|
|
private final Binder binder = new BriarBinder();
|
|
|
|
@Inject private DatabaseConfig databaseConfig;
|
|
@Inject private AndroidNotificationManager notificationManager;
|
|
|
|
// Fields that are accessed from background threads must be volatile
|
|
@Inject private volatile LifecycleManager lifecycleManager;
|
|
@Inject private volatile AndroidExecutor androidExecutor;
|
|
@Inject @DatabaseExecutor private volatile Executor dbExecutor;
|
|
@Inject private volatile DatabaseComponent db;
|
|
private volatile boolean started = false;
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
super.onCreate();
|
|
LOG.info("Created");
|
|
if(created.getAndSet(true)) {
|
|
LOG.info("Already created");
|
|
stopSelf();
|
|
return;
|
|
}
|
|
if(databaseConfig.getEncryptionKey() == null) {
|
|
LOG.info("No database key");
|
|
stopSelf();
|
|
return;
|
|
}
|
|
// Show an ongoing notification that the service is running
|
|
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
|
|
b.setSmallIcon(R.drawable.ongoing_notification_icon);
|
|
b.setContentTitle(getText(R.string.ongoing_notification_title));
|
|
b.setContentText(getText(R.string.ongoing_notification_text));
|
|
b.setWhen(0); // Don't show the time
|
|
b.setOngoing(true);
|
|
Intent i = new Intent(this, DashboardActivity.class);
|
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
|
|
FLAG_ACTIVITY_SINGLE_TOP);
|
|
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
|
startForeground(ONGOING_NOTIFICATION_ID, b.build());
|
|
// Start the services in a background thread
|
|
new Thread() {
|
|
@Override
|
|
public void run() {
|
|
StartResult result = lifecycleManager.startServices();
|
|
if(result == SUCCESS) {
|
|
db.addListener(BriarService.this);
|
|
started = true;
|
|
} else if(result == ALREADY_RUNNING) {
|
|
LOG.info("Already running");
|
|
stopSelf();
|
|
} else {
|
|
if(LOG.isLoggable(WARNING))
|
|
LOG.warning("Startup failed: " + result);
|
|
showStartupFailureNotification();
|
|
stopSelf();
|
|
}
|
|
}
|
|
}.start();
|
|
}
|
|
|
|
private void showStartupFailureNotification() {
|
|
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
|
|
b.setSmallIcon(android.R.drawable.stat_notify_error);
|
|
b.setContentTitle(getText(R.string.startup_failed_notification_title));
|
|
b.setContentText(getText(R.string.startup_failed_notification_text));
|
|
Intent i = new Intent(this, DashboardActivity.class);
|
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
|
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
|
Object o = getSystemService(NOTIFICATION_SERVICE);
|
|
NotificationManager nm = (NotificationManager) o;
|
|
nm.notify(FAILURE_NOTIFICATION_ID, b.build());
|
|
// Bring the dashboard to the front to clear all other activities
|
|
i = new Intent(this, DashboardActivity.class);
|
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
|
i.putExtra("briar.STARTUP_FAILED", true);
|
|
startActivity(i);
|
|
}
|
|
|
|
@Override
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
return START_NOT_STICKY; // Don't restart automatically if killed
|
|
}
|
|
|
|
@Override
|
|
public IBinder onBind(Intent intent) {
|
|
return binder;
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
super.onDestroy();
|
|
LOG.info("Destroyed");
|
|
stopForeground(true);
|
|
notificationManager.clearNotifications();
|
|
// Stop the services in a background thread
|
|
new Thread() {
|
|
@Override
|
|
public void run() {
|
|
if(started) {
|
|
db.removeListener(BriarService.this);
|
|
lifecycleManager.stopServices();
|
|
}
|
|
androidExecutor.shutdown();
|
|
}
|
|
}.start();
|
|
}
|
|
|
|
@Override
|
|
public void onLowMemory() {
|
|
super.onLowMemory();
|
|
LOG.warning("Memory is low");
|
|
// FIXME: Work out what to do about it
|
|
}
|
|
|
|
public void eventOccurred(Event e) {
|
|
if(e instanceof MessageAddedEvent) {
|
|
MessageAddedEvent m = (MessageAddedEvent) e;
|
|
GroupId g = m.getGroup().getId();
|
|
ContactId c = m.getContactId();
|
|
if(c != null) showMessageNotification(g, c);
|
|
}
|
|
}
|
|
|
|
private void showMessageNotification(final GroupId g, final ContactId c) {
|
|
dbExecutor.execute(new Runnable() {
|
|
public void run() {
|
|
try {
|
|
lifecycleManager.waitForDatabase();
|
|
if(g.equals(db.getInboxGroupId(c)))
|
|
notificationManager.showPrivateMessageNotification(c);
|
|
else notificationManager.showGroupPostNotification(g);
|
|
} catch(DbException e) {
|
|
if(LOG.isLoggable(WARNING))
|
|
LOG.log(WARNING, e.toString(), e);
|
|
} catch(InterruptedException e) {
|
|
LOG.info("Interruped while waiting for database");
|
|
Thread.currentThread().interrupt();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/** Waits for the database to be opened before returning. */
|
|
public void waitForDatabase() throws InterruptedException {
|
|
lifecycleManager.waitForDatabase();
|
|
}
|
|
|
|
/** Waits for all services to start before returning. */
|
|
public void waitForStartup() throws InterruptedException {
|
|
lifecycleManager.waitForStartup();
|
|
}
|
|
|
|
/** Waits for all services to stop before returning. */
|
|
public void waitForShutdown() throws InterruptedException {
|
|
lifecycleManager.waitForShutdown();
|
|
}
|
|
|
|
/** Starts the shutdown process. */
|
|
public void shutdown() {
|
|
stopSelf(); // This will call onDestroy()
|
|
}
|
|
|
|
public class BriarBinder extends Binder {
|
|
|
|
/** Returns the bound service. */
|
|
public BriarService getService() {
|
|
return BriarService.this;
|
|
}
|
|
}
|
|
|
|
public static class BriarServiceConnection implements ServiceConnection {
|
|
|
|
private final CountDownLatch binderLatch = new CountDownLatch(1);
|
|
|
|
private volatile IBinder binder = null;
|
|
|
|
public void onServiceConnected(ComponentName name, IBinder binder) {
|
|
this.binder = binder;
|
|
binderLatch.countDown();
|
|
}
|
|
|
|
public void onServiceDisconnected(ComponentName name) {}
|
|
|
|
/** Waits for the service to connect and returns its binder. */
|
|
public IBinder waitForBinder() throws InterruptedException {
|
|
binderLatch.await();
|
|
return binder;
|
|
}
|
|
}
|
|
}
|