Commit 629f943f authored by LIly's avatar LIly

add simulator stuff

parent 1ffc2b27
......@@ -43,11 +43,11 @@ import java.util.List;
*
**/
public class EncounterBasedCommunication {
public static final long CHANGE_EPOCH_TIME = 15*60000;
public static final long CHANGE_EPOCH_TIME = 2*60000;
public static final long SCAN_BATCH_INTERVAL = (long) (2 * 60000);
public static final int REQUEST_ENABLE_BT = 1;
public static final int REQUEST_ACCESS_FINE_LOCATION = 2;
public static final boolean USE_SIMULATOR = true;
private static final String TAG = EncounterBasedCommunication.class.getSimpleName();
private UserAccountClient userAccountClient;
......
......@@ -12,6 +12,10 @@ import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.widget.Toast;
import org.mpisws.encounters.encounterformation.simulator.SimulatorEncounterFormationCore;
import static org.mpisws.encounters.EncounterBasedCommunication.USE_SIMULATOR;
/**
* Created by tslilyai on 10/27/17.
......@@ -25,6 +29,7 @@ import android.widget.Toast;
public class EncounterFormationService extends Service {
private static final String TAG = EncounterFormationService.class.getSimpleName();
private EncounterFormationCore core;
private SimulatorEncounterFormationCore score;
private Thread thread;
public boolean was_destroyed = false;
......@@ -49,8 +54,14 @@ public class EncounterFormationService extends Service {
Log.v(TAG, "Bluetooth and location permissions enabled");
if (core == null) {
Log.v(TAG, "Starting SDDR_API Core from thread " + Thread.currentThread().getName());
core = new EncounterFormationCore(this);
thread = new Thread(core);
Log.v(TAG, "Simulated? " + USE_SIMULATOR);
if (USE_SIMULATOR) {
score = new SimulatorEncounterFormationCore(this);
thread = new Thread(score);
} else {
core = new EncounterFormationCore(this);
thread = new Thread(core);
}
thread.start();
} else {
Log.v(TAG, "SDDR_API already running");
......@@ -117,6 +128,10 @@ public class EncounterFormationService extends Service {
core.stop();
core = null;
}
if (USE_SIMULATOR && score != null) {
score.stop();
score = null;
}
was_destroyed = true;
}
......
......@@ -29,5 +29,5 @@ public class SDDR_Native {
static public native int c_sendToClient(String data);
static public long c_RadioPtr;
static protected ArrayList<byte[]> c_EncounterMsgs = new ArrayList<>();
public static ArrayList<byte[]> c_EncounterMsgs = new ArrayList<>();
}
\ No newline at end of file
package org.mpisws.encounters.encounterformation.simulator;
/**
* Created by tslilyai on 10/18/17.
*/
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.os.ParcelUuid;
import android.util.Log;
import org.mpisws.helpers.Utils;
import java.nio.ByteBuffer;
import java.util.UUID;
import static android.bluetooth.le.AdvertiseSettings.ADVERTISE_MODE_LOW_POWER;
/**
* Manages BLE Advertising.
*/
public class SimulatorAdvertiser {
public static final int TOTAL_LENGTH = 31;
public static final int SHA1_LENGTH = 20;
public static final int PUUID_LENGTH = 16;
public static final int NONCE_LENGTH_IN_ADVERT = SHA1_LENGTH - PUUID_LENGTH ;
private static final String TAG = SimulatorAdvertiser.class.getSimpleName();
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
private AdvertiseCallback mAdvertiseCallback;
private AdvertiseSettings mAdvertiseSettings;
private UUID mUUID;
private boolean connectable;
public static byte[] mPUUID = new byte[PUUID_LENGTH];
public static byte[] mAdData = new byte[NONCE_LENGTH_IN_ADVERT];
public void initialize(BluetoothAdapter btAdapter) {
mBluetoothLeAdvertiser = btAdapter.getBluetoothLeAdvertiser();
connectable = false;
}
public void setConnectable(boolean connect) {
this.connectable = connect;
}
public void setAdData(byte[] newData) {
int amountToIncludeInPUUID = PUUID_LENGTH ;
Utils.myAssert(newData.length == SHA1_LENGTH);
// copy what data can fit into the puuid slot
System.arraycopy(newData, 0, mPUUID, 0, amountToIncludeInPUUID);
System.arraycopy(newData, amountToIncludeInPUUID, mAdData, 0, SHA1_LENGTH - amountToIncludeInPUUID);
ByteBuffer bb = ByteBuffer.wrap(mPUUID);
long high = bb.getLong();
long low = bb.getLong();
mUUID = new UUID(high, low);
Log.v(TAG, "New Advert " + Utils.getHexString(mPUUID) + Utils.getHexString(mAdData));
}
public void resetAdvertiser() {
if (mAdvertiseCallback != null)
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
startAdvertising();
}
public void startAdvertising() {
if (mAdvertiseCallback == null) {
mAdvertiseCallback = new SDDRAdvertiseCallback();
mAdvertiseSettings = buildAdvertiseSettings();
}
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.startAdvertising(
mAdvertiseSettings,
buildAdvertiseData(),
mAdvertiseCallback
);
}
}
/**
* Stops BLE Advertising.
*/
public void stopAdvertising() {
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
mAdvertiseCallback = null;
}
}
/**
* Returns an AdvertiseData object which includes the Service UUID and Device Name.
*/
private AdvertiseData buildAdvertiseData() {
/**
* Note: There is a strict limit of 31 Bytes on packets sent over BLE Advertisements.
* This includes everything put into AdvertiseData including UUIDs, device info, &
* arbitrary service or manufacturer data.
* Attempting to send packets over this limit will result in a failure with error code
* AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE. Catch this error in the
* onStartFailure() method of an AdvertiseCallback implementation.
*/
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
dataBuilder.setIncludeDeviceName(false);
dataBuilder.setIncludeTxPowerLevel(false);
dataBuilder.addServiceData(new ParcelUuid(mUUID), mAdData);
return dataBuilder.build();
}
/**
* Returns an AdvertiseSetParameters object
*/
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setConnectable(connectable);
settingsBuilder.setAdvertiseMode(ADVERTISE_MODE_LOW_POWER);//ADVERTISE_MODE_LOW_LATENCY);
settingsBuilder.setTimeout(0);
return settingsBuilder.build();
}
/**
* Custom callback after Advertising succeeds or fails to start.
*/
private class SDDRAdvertiseCallback extends AdvertiseCallback {
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
if (errorCode == ADVERTISE_FAILED_DATA_TOO_LARGE) {
Log.v(TAG, "Advertising failed: Data too large!");
}
else Log.v(TAG, "Advertising failed: Unknown " + errorCode);
}
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.v(TAG, "Advertising successfully started");
}
}
}
package org.mpisws.encounters.encounterformation.simulator;
import android.content.Context;
import android.util.Log;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.mpisws.embeddedsocial.ESClient;
import org.mpisws.embeddedsocial.ESMessage;
import org.mpisws.encounters.clients.CommunicationHelpers;
import org.mpisws.encounters.encounterformation.SDDR_Native;
import org.mpisws.encounters.encounterhistory.bridges.EncounterEntriesBridge;
import org.mpisws.encounters.encounterhistory.bridges.LocationBridge;
import org.mpisws.encounters.encounterhistory.models.MEncounterEntry;
import org.mpisws.encounters.lib.Preferences;
import org.mpisws.helpers.Coder;
import org.mpisws.helpers.ESCredentials;
import org.mpisws.helpers.GlobalObjectRegistry;
import org.mpisws.helpers.Identifier;
import org.mpisws.helpers.ThreadPoolManager;
import org.mpisws.helpers.Utils;
import org.mpisws.messaging.EpochLinkMessage;
import org.mpisws.messaging.ReceivedMessageWrapper;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.mpisws.encounters.encounterformation.simulator.SimulatorEncounterFormationCore.getOngoingEncounters;
import static org.mpisws.encounters.encounterformation.simulator.SimulatorEncounterFormationCore.mDHFullKey;
import static org.mpisws.encounters.encounterformation.simulator.SimulatorEncounterFormationCore.mDHPubKey;
import static org.mpisws.encounters.encounterformation.simulator.SimulatorEncounterFormationCore.mNonce;
import static org.mpisws.messaging.ReceivedMessageWrapper.MsgTyp.EPOCH_LINK;
/**
* Created by tslilyai on 2/21/18.
*/
public class SimulatorEncounterConfirmationAndEpochLinking {
private static final String TAG = SimulatorEncounterConfirmationAndEpochLinking.class.getSimpleName();
EncounterEntriesBridge encountersBridge;
LocationBridge locationBridge;
Context context;
public SimulatorEncounterConfirmationAndEpochLinking(Context context) {
this.context = context;
encountersBridge = new EncounterEntriesBridge(context);
locationBridge = new LocationBridge(context);
}
public void confirmEncountersViaES() {
List<String> advertsToConfirm = new ArrayList<>();
// make sure no ES or DB actions are run on the main thread!
ThreadPoolManager.getInstance().runTask(() -> {
// 1) Get all advertised nonces that we have yet to use to form a secret key
List<MEncounterEntry> unconfirmedEIDs = encountersBridge.getUnconfirmedEIDs();
// 2) Remember to create a topic for every unposted nonce we advertised.
// Note that there can be repeats, so let's make this a set
// 3) Also remember the associated received oldNonce to look up to get the DH public key
Set<String> mySeenNonces = new HashSet<>();
Set<String> recvSeenNonces = new HashSet<>();
List<Pair<Identifier, Identifier>> advertsToPost = new ArrayList<>();
for (int i = 0; i < unconfirmedEIDs.size(); i++) {
MEncounterEntry eid = unconfirmedEIDs.get(i);
// remember to post this oldNonce (and also which oldNonce we're posting)
if (!eid.getPostedNonceTopic()) {
if (!mySeenNonces.contains(eid.getMyNonce().toString())) {
advertsToPost.add(new ImmutablePair(eid.getMyNonce(), eid.getMyDHPubKey()));
mySeenNonces.add(eid.getMyNonce().toString());
}
}
if (!recvSeenNonces.contains(eid.getReceivedNonce().toString())) {
advertsToConfirm.add(eid.getReceivedNonce().toString());
recvSeenNonces.add(eid.getReceivedNonce().toString());
}
}
// 4) create topics for unposted adverts and update the posted status of the successfully posted adverts
Log.d(TAG, ": Posting " + advertsToPost.size() + " broadcasted adverts");
List<String> topicHandles = ESClient.getInstance().createTopics(advertsToPost);
for (int i = 0; i < advertsToPost.size(); i++) {
if (topicHandles.get(i) != null) {
encountersBridge.updateNoncePostedAsTopic(advertsToPost.get(i).getLeft());
}
}
// 5) get dh pub key of encounters (null if doesn't exist)
Log.d(TAG, ": Confirming " + advertsToConfirm.size() + " received adverts");
List<String> confirmPubKeys = ESClient.getInstance().getTopicTexts(advertsToConfirm);
// 6) calculate the secret key and eids for all encounters for which you got a dh pub key
List<Pair<Identifier, Identifier>> eidsToCreate = new ArrayList<>();
List<Identifier> secrets = new ArrayList<>();
for (int i = 0; i < unconfirmedEIDs.size(); i++) {
secrets.add(null);
eidsToCreate.add(null);
}
boolean found = false;
int advertIndex = 0;
String otherDHPubKey = "";
String receivedNonce = "";
for (int i = 0; i < unconfirmedEIDs.size(); i++) {
MEncounterEntry unconfirmedEID = unconfirmedEIDs.get(i);
String eidRecvNonce = unconfirmedEID.getReceivedNonce().toString();
if (receivedNonce.compareTo(eidRecvNonce) != 0 || receivedNonce.compareTo("")==0) {
if (advertIndex >= advertsToConfirm.size()) {
break;
}
otherDHPubKey = confirmPubKeys.get(advertIndex);
receivedNonce = advertsToConfirm.get(advertIndex);
advertIndex++;
}
if (otherDHPubKey != null) {
found = true;
Log.d(TAG, "Computing secret with received nonce " + eidRecvNonce + " with DHPubKey " + otherDHPubKey);
secrets.set(i, computeSecretKey(unconfirmedEID.getMyDHPrivKey(), unconfirmedEID.getReceivedNonce(), otherDHPubKey));
Identifier eid = Coder.convertSharedSecretToID(secrets.get(i));
eidsToCreate.set(i, new ImmutablePair<>(eid, eid));
}
}
if (!found) return;
// 7) create the successfully determined encounter topics
topicHandles = ESClient.getInstance().createTopics(eidsToCreate);
for (int i = 0; i < unconfirmedEIDs.size(); i++) {
if (secrets.get(i) != null && topicHandles.get(i) != null) {
// put the topic handle and secret into the database
Log.d(TAG, "Adding ss, topichandle: " + secrets.get(i).toString()+ ", "+topicHandles.get(i) + " to "
+ unconfirmedEIDs.get(i).getMyNonce().toString() + ", " + unconfirmedEIDs.get(i).getReceivedNonce().toString());
encountersBridge.updateConfirmedEncounter(unconfirmedEIDs.get(i), secrets.get(i), topicHandles.get(i));
}
}
});
}
private Identifier computeSecretKey(Identifier myDHKey, Identifier advert, String dhPubKey) {
byte[] secretKey = SDDR_Native.c_computeSecretKeyWithSHA(myDHKey.getBytes(), advert.getBytes(), Utils.hexStringToByteArray(dhPubKey));
if (secretKey != null) {
Identifier secretKeyID = new Identifier(secretKey);
Log.d(TAG, "Got secret key for received advert " + advert.toString() + ": " + secretKeyID.toString());
return secretKeyID;
}
return null;
}
public void insertNewNoncesIntoDatabase() {
// insert new entries with ongoing encounters into database
Map<String, Pair<Long, Long>> ongoingEnc = getOngoingEncounters();
ongoingEnc.forEach((k, v) -> {
ThreadPoolManager.getInstance().runTask(() -> {
Log.d(TAG, "Inserting new Entry epoch change : " + v.getLeft() + " " + k + " " + mNonce);
encountersBridge.insertEntry(v.getLeft()/*pkid*/, v.getRight() /*startTime*/,
new Identifier(Utils.hexStringToByteArray(k)),
mNonce, mDHFullKey, mDHPubKey, true,
GlobalObjectRegistry.getObject(ESCredentials.class).getUserHandle());
});
});
}
public void postUnpostedNoncesThatNeedLinking() {
ThreadPoolManager.getInstance().runTask(() -> {
List<ESMessage> msgsToSend = new ArrayList<>();
List<MEncounterEntry> toPostEntries = encountersBridge.getEntriesToPostForLinking();
List<MEncounterEntry> entriesToPostOn = new ArrayList<>(toPostEntries.size());
List<Identifier> topicHandlesToGet = new ArrayList<>(toPostEntries.size());
Log.d(TAG, "Need to post " + toPostEntries.size() + " adverts to link!");
// (1) get all topic handles that we need to
for (int i = 0; i < toPostEntries.size(); i++) {
entriesToPostOn.add(null);
}
for (int i = 0; i < toPostEntries.size(); i++) {
entriesToPostOn.add(null);
MEncounterEntry entry = toPostEntries.get(i);
// find the first encounter topic associated with this encounter / received nonce
MEncounterEntry entryToPostOn = encountersBridge.getFirstEntryWithReceivedNonce(entry.getReceivedNonce());
entriesToPostOn.set(i,entryToPostOn);
// only post if confirmed
if (entryToPostOn.getEncounterID() == null || entryToPostOn.getEncounterID().getBytes() == null) {
continue;
}
if (entryToPostOn.getOtherTopicHandle() == null || entry.getOtherTopicHandle().compareTo("") == 0) {
// get the topic handle of the old encounter so we can post on it
topicHandlesToGet.add(entryToPostOn.getEncounterID());
}
}
List<String> newTopicHandles = ESClient.getInstance().getTopicHandles(topicHandlesToGet);
Log.d(TAG, "Getting topic handles of " + newTopicHandles.size() + " encounters");
// (2) update topic handle information
int index = 0; // this tracks the position in the "topicsToGet" array
for (int i = 0; i < toPostEntries.size(); i++) {
MEncounterEntry entryToPostOn = entriesToPostOn.get(i);
if (entryToPostOn.getEncounterID() == null || entryToPostOn.getEncounterID().getBytes() == null) {
continue;
}
if (entryToPostOn.getOtherTopicHandle() == null || entryToPostOn.getOtherTopicHandle().compareTo("") == 0) {
if (newTopicHandles.get(index) != "") {
// update the entry with the topic handle in the database
encountersBridge.updateTopicHandles(topicHandlesToGet.get(index), newTopicHandles.get(index));
// update the eid with the topic handle so we know where to send it
entryToPostOn.setOtherTopicHandle(newTopicHandles.get(index));
Log.d(TAG, "Set topic handle of " + entryToPostOn.toString() + " to " + newTopicHandles.get(index));
}
index++;
}
}
// (3) encode the messages to send (with its constraints) and form the message contents
for (int i = 0; i < toPostEntries.size(); i++) {
MEncounterEntry entry = toPostEntries.get(i);
MEncounterEntry entryToPostOn = entriesToPostOn.get(i);
if (entryToPostOn.getOtherTopicHandle() == null || entryToPostOn.getOtherTopicHandle().compareTo("") == 0) {
// we failed to find the topic handle
continue;
}
// we want to post both this advert and the old one
EpochLinkMessage epochLinkMessage = new EpochLinkMessage.EpochLinkMessageBuilder()
.addOldNonce(entryToPostOn.getMyNonce().toString())
.addNewNonce(entry.getMyNonce().toString())
.build();
msgsToSend.add(new ESMessage(epochLinkMessage.toSendMessageText(entryToPostOn.getSecret().getBytes()),
entryToPostOn.getEncounterID().toString(),
entryToPostOn.getOtherTopicHandle(),
true, null, true, -1));
encountersBridge.updateNoncePostedForLinking(entry.getMyNonce());
}
ESClient.getInstance().sendMsgs(msgsToSend);
});
}
public void processNoncesForLinking() {
/*
* NOTE: WE LINK *FORWARD* SO THAT THE "NEWER" PKID END TIME IS SET CORRECTLY
*/
ThreadPoolManager.getInstance().runTask(() -> {
// (1) get the nonce messages
List<Pair<Identifier, Identifier>> oldNewNoncesToLink = new ArrayList<>();
String lastReadCursor = Preferences.getInstance().getLatestReadMessageCursor();
String continuationCursor = null, firstMsgCursor = null;
boolean done = false;
int loopRound = 0, maxLoops = 100;
while(loopRound < maxLoops) {
Pair<String, List<ReceivedMessageWrapper>> cursorAndMsgs = CommunicationHelpers.getMsgsPage(continuationCursor, context);
List<ReceivedMessageWrapper> msgs = cursorAndMsgs.getRight();
continuationCursor = cursorAndMsgs.getLeft();
Log.d(TAG, "LINK: Loop round " + loopRound + " : found " + msgs.size() + " messages, continuation cursor " + continuationCursor);
// didn't find any new messages
if (msgs.size() == 0) {
break;
}
for (ReceivedMessageWrapper receivedMessageWrapper: msgs) {
if (receivedMessageWrapper.getCursor() == null) {
continue;
}
Log.d(TAG, "LINK: Msg cursor: " + receivedMessageWrapper.getCursor());
if (firstMsgCursor == null || lastReadCursor == null) {
firstMsgCursor = receivedMessageWrapper.getCursor();
Preferences.getInstance().setLatestReadMessageCursor(firstMsgCursor);
Log.d(TAG, "LINK: Setting latest read cursor to " + firstMsgCursor);
}
if (receivedMessageWrapper.getCursor().compareTo(lastReadCursor) == 0) {
// we're at the end of read messages
done = true;
break;
}
if (receivedMessageWrapper.getMsgType() != EPOCH_LINK) {
continue;
}
EpochLinkMessage epochLinkMessage = receivedMessageWrapper.getEpochLinkMessage();
String oldNonce = epochLinkMessage.getOldNonce();
String newNonce = epochLinkMessage.getNewNonce();
oldNewNoncesToLink.add(new ImmutablePair<>(
new Identifier(Utils.hexStringToByteArray(oldNonce)),
new Identifier(Utils.hexStringToByteArray(newNonce))));
}
if (done) {
break;
}
loopRound++;
}
if (oldNewNoncesToLink.size() == 0) return;
// (2) process the link nonce pairs that we got
for (Pair<Identifier, Identifier> nonces : oldNewNoncesToLink) {
Identifier newNonce = nonces.getRight();
Identifier oldNonce = nonces.getLeft();
Log.d(TAG, "Got link with new advert : " + newNonce.toString() + " " + oldNonce.toString());
// GET THE PKIDS CORRESPONDING TO THE ADVERTS
MEncounterEntry entryNew = encountersBridge.getOneEncounterEntryOfReceivedNonce(newNonce);
MEncounterEntry entryOld = encountersBridge.getOneEncounterEntryOfReceivedNonce(oldNonce);
Utils.myAssert(entryOld != null);
if (entryNew == null) {
Log.d(TAG, "New nonce not actually detected by us at any point " + newNonce);
continue;
}
// UPDATE THE ENCOUNTER TABLE ENTRIES
encountersBridge.updateEncounterEntriesFrom(entryOld, entryNew);
}
});
}
}
package org.mpisws.encounters.encounterformation.simulator;
/**
* Created by tslilyai on 10/16/17.
*/
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.os.Looper;
import android.util.Log;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.mpisws.embeddedsocial.ESClient;
import org.mpisws.encounters.encounterformation.EncounterFormationService;
import org.mpisws.encounters.encounterformation.SDDR_Native;
import org.mpisws.encounters.encounterhistory.bridges.EncounterEntriesBridge;
import org.mpisws.helpers.Identifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.mpisws.encounters.EncounterBasedCommunication.CHANGE_EPOCH_TIME;