Added a service based POW engine, so it shouldn't be killed by the system, at least not that easily. (WIP)
This commit is contained in:
@ -0,0 +1,164 @@
|
||||
package ch.dissem.apps.abit.service;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ch.dissem.apps.abit.notification.NetworkNotification;
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
|
||||
import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIFICATION_ID;
|
||||
|
||||
/**
|
||||
* Define a Service that returns an IBinder for the
|
||||
* sync adapter class, allowing the sync adapter framework to call
|
||||
* onPerformSync().
|
||||
*/
|
||||
public class BitmessageService extends Service {
|
||||
public static final Logger LOG = LoggerFactory.getLogger(BitmessageService.class);
|
||||
|
||||
public static final int MSG_CREATE_IDENTITY = 10;
|
||||
public static final int MSG_SUBSCRIBE = 20;
|
||||
public static final int MSG_ADD_CONTACT = 21;
|
||||
public static final int MSG_SUBSCRIBE_AND_ADD_CONTACT = 23;
|
||||
public static final int MSG_SEND_MESSAGE = 30;
|
||||
public static final int MSG_SEND_BROADCAST = 31;
|
||||
public static final int MSG_START_NODE = 100;
|
||||
public static final int MSG_STOP_NODE = 101;
|
||||
|
||||
public static final String DATA_FIELD_IDENTITY = "identity";
|
||||
public static final String DATA_FIELD_ADDRESS = "address";
|
||||
public static final String DATA_FIELD_SUBJECT = "subject";
|
||||
public static final String DATA_FIELD_MESSAGE = "message";
|
||||
|
||||
// Object to use as a thread-safe lock
|
||||
private static final Object lock = new Object();
|
||||
|
||||
private static NetworkNotification notification = null;
|
||||
private static BitmessageContext bmc = null;
|
||||
|
||||
private static volatile boolean running = false;
|
||||
|
||||
private static Messenger messenger;
|
||||
|
||||
public static boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
synchronized (lock) {
|
||||
if (bmc == null) {
|
||||
bmc = Singleton.getBitmessageContext(this);
|
||||
notification = new NetworkNotification(this, bmc);
|
||||
messenger = new Messenger(new IncomingHandler(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return Service.START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (bmc.isRunning()) bmc.shutdown();
|
||||
running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object that allows the system to invoke
|
||||
* the sync adapter.
|
||||
*/
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return messenger.getBinder();
|
||||
}
|
||||
|
||||
private static class IncomingHandler extends Handler {
|
||||
private WeakReference<BitmessageService> service;
|
||||
|
||||
private IncomingHandler(BitmessageService service) {
|
||||
this.service = new WeakReference<>(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_CREATE_IDENTITY: {
|
||||
BitmessageAddress identity = bmc.createIdentity(false);
|
||||
if (msg.replyTo != null) {
|
||||
try {
|
||||
Message message = Message.obtain(this, MSG_CREATE_IDENTITY);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putSerializable(DATA_FIELD_IDENTITY, identity);
|
||||
message.setData(bundle);
|
||||
msg.replyTo.send(message);
|
||||
} catch (RemoteException e) {
|
||||
LOG.debug(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSG_SUBSCRIBE: {
|
||||
Serializable data = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
|
||||
if (data instanceof BitmessageAddress) {
|
||||
bmc.addSubscribtion((BitmessageAddress) data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSG_SEND_MESSAGE: {
|
||||
Serializable identity = msg.getData().getSerializable(DATA_FIELD_IDENTITY);
|
||||
Serializable address = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
|
||||
if (identity instanceof BitmessageAddress
|
||||
&& address instanceof BitmessageAddress) {
|
||||
String subject = msg.getData().getString(DATA_FIELD_SUBJECT);
|
||||
String message = msg.getData().getString(DATA_FIELD_MESSAGE);
|
||||
bmc.send((BitmessageAddress) identity, (BitmessageAddress) address,
|
||||
subject, message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSG_SEND_BROADCAST: {
|
||||
Serializable data = msg.getData().getSerializable(DATA_FIELD_IDENTITY);
|
||||
if (data instanceof BitmessageAddress) {
|
||||
String subject = msg.getData().getString(DATA_FIELD_SUBJECT);
|
||||
String message = msg.getData().getString(DATA_FIELD_MESSAGE);
|
||||
bmc.broadcast((BitmessageAddress) data, subject, message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSG_START_NODE:
|
||||
// TODO: warn user, option to restrict to WiFi
|
||||
// (I'm not quite sure this can be done here, though)
|
||||
service.get().startService(new Intent(service.get(), BitmessageService.class));
|
||||
running = true;
|
||||
service.get().startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
|
||||
bmc.startup();
|
||||
notification.show();
|
||||
break;
|
||||
case MSG_STOP_NODE:
|
||||
bmc.shutdown();
|
||||
running = false;
|
||||
service.get().stopForeground(false);
|
||||
service.get().stopSelf();
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package ch.dissem.apps.abit.service;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ch.dissem.apps.abit.notification.ProofOfWorkNotification;
|
||||
import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine;
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
|
||||
|
||||
import static ch.dissem.apps.abit.notification.ProofOfWorkNotification.ONGOING_NOTIFICATION_ID;
|
||||
|
||||
/**
|
||||
* The Proof of Work Service makes sure POW is done in a foreground process, so it shouldn't be
|
||||
* killed by the system before the nonce is found.
|
||||
*/
|
||||
public class ProofOfWorkService extends Service {
|
||||
public static final Logger LOG = LoggerFactory.getLogger(ProofOfWorkService.class);
|
||||
|
||||
// Object to use as a thread-safe lock
|
||||
private static final Object lock = new Object();
|
||||
private static ProofOfWorkEngine engine;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
synchronized (lock) {
|
||||
if (engine == null) {
|
||||
engine = new MultiThreadedPOWEngine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new PowBinder(engine, this);
|
||||
}
|
||||
|
||||
public static class PowBinder extends Binder {
|
||||
private final ProofOfWorkEngine engine;
|
||||
|
||||
private PowBinder(ProofOfWorkEngine engine, ProofOfWorkService service) {
|
||||
this.engine = new EngineWrapper(engine, service);
|
||||
}
|
||||
|
||||
public ProofOfWorkEngine getEngine() {
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
|
||||
private static class EngineWrapper implements ProofOfWorkEngine {
|
||||
private final ProofOfWorkNotification notification;
|
||||
private final ProofOfWorkEngine engine;
|
||||
private final WeakReference<ProofOfWorkService> serviceRef;
|
||||
|
||||
private EngineWrapper(ProofOfWorkEngine engine, ProofOfWorkService service) {
|
||||
this.engine = engine;
|
||||
this.serviceRef = new WeakReference<>(service);
|
||||
this.notification = new ProofOfWorkNotification(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void calculateNonce(byte[] initialHash, byte[] target, final Callback callback) {
|
||||
final ProofOfWorkService service = serviceRef.get();
|
||||
service.startService(new Intent(service, ProofOfWorkService.class));
|
||||
service.startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
|
||||
engine.calculateNonce(initialHash, target, new ProofOfWorkEngine.Callback() {
|
||||
@Override
|
||||
public void onNonceCalculated(byte[] nonce) {
|
||||
try {
|
||||
callback.onNonceCalculated(nonce);
|
||||
} finally {
|
||||
service.stopForeground(true);
|
||||
service.stopSelf();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package ch.dissem.apps.abit.service;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import ch.dissem.apps.abit.service.ProofOfWorkService.PowBinder;
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
|
||||
|
||||
import static android.content.Context.BIND_AUTO_CREATE;
|
||||
|
||||
/**
|
||||
* Proof of Work engine that uses the Proof of Work service.
|
||||
*/
|
||||
public class ServicePowEngine implements ProofOfWorkEngine, ProofOfWorkEngine.Callback {
|
||||
private final Semaphore semaphore = new Semaphore(1, true);
|
||||
private final Context ctx;
|
||||
|
||||
private byte[] initialHash, targetValue;
|
||||
private Callback callback;
|
||||
|
||||
public ServicePowEngine(Context ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
private ServiceConnection connection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
((PowBinder) service).getEngine().calculateNonce(initialHash, targetValue, ServicePowEngine.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
semaphore.release();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void calculateNonce(byte[] initialHash, byte[] targetValue, Callback callback) {
|
||||
try {
|
||||
semaphore.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
this.initialHash = initialHash;
|
||||
this.targetValue = targetValue;
|
||||
this.callback = callback;
|
||||
ctx.bindService(new Intent(ctx, ProofOfWorkService.class), connection, BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNonceCalculated(byte[] bytes) {
|
||||
callback.onNonceCalculated(bytes);
|
||||
ctx.unbindService(connection);
|
||||
}
|
||||
|
||||
}
|
@ -2,9 +2,6 @@ package ch.dissem.apps.abit.service;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ch.dissem.apps.abit.MessageListActivity;
|
||||
import ch.dissem.apps.abit.listener.MessageListener;
|
||||
import ch.dissem.apps.abit.repository.AndroidAddressRepository;
|
||||
import ch.dissem.apps.abit.repository.AndroidInventory;
|
||||
@ -15,7 +12,6 @@ import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
|
||||
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
|
||||
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||
import ch.dissem.bitmessage.ports.Security;
|
||||
import ch.dissem.bitmessage.security.sc.SpongySecurity;
|
||||
|
||||
/**
|
||||
@ -33,6 +29,7 @@ public class Singleton {
|
||||
final Context ctx = context.getApplicationContext();
|
||||
SqlHelper sqlHelper = new SqlHelper(ctx);
|
||||
bitmessageContext = new BitmessageContext.Builder()
|
||||
.proofOfWorkEngine(new ServicePowEngine(ctx))
|
||||
.security(new SpongySecurity())
|
||||
.nodeRegistry(new MemoryNodeRegistry())
|
||||
.inventory(new AndroidInventory(sqlHelper))
|
||||
|
Reference in New Issue
Block a user