diff --git a/ebclibrary/src/main/java/org/mpisws/encounters/EncounterBasedCommunication.java b/ebclibrary/src/main/java/org/mpisws/encounters/EncounterBasedCommunication.java index c091be9fd82c67c02c3238794af15b392eb151fd..d0bbe0deedc307c5aa09dd03d41650d117e4ccc9 100644 --- a/ebclibrary/src/main/java/org/mpisws/encounters/EncounterBasedCommunication.java +++ b/ebclibrary/src/main/java/org/mpisws/encounters/EncounterBasedCommunication.java @@ -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; diff --git a/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/EncounterFormationService.java b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/EncounterFormationService.java index 75e8c6382b0e45edbd3edaa84616dd7cd1f94c1d..ff8c821eed5c75ab18567cb7671142e577512503 100644 --- a/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/EncounterFormationService.java +++ b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/EncounterFormationService.java @@ -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; } diff --git a/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/SDDR_Native.java b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/SDDR_Native.java index 07c51c2afce9057989fb093b27148048274dd3e3..21448a473f71387c3d177b4e158891753d03acdc 100644 --- a/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/SDDR_Native.java +++ b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/SDDR_Native.java @@ -29,5 +29,5 @@ public class SDDR_Native { static public native int c_sendToClient(String data); static public long c_RadioPtr; - static protected ArrayList c_EncounterMsgs = new ArrayList<>(); + public static ArrayList c_EncounterMsgs = new ArrayList<>(); } \ No newline at end of file diff --git a/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorAdvertiser.java b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorAdvertiser.java new file mode 100644 index 0000000000000000000000000000000000000000..0a06d7c76ea9c00ff519d1cb7351d330747d16cc --- /dev/null +++ b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorAdvertiser.java @@ -0,0 +1,144 @@ +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"); + } + } +} diff --git a/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorEncounterConfirmationAndEpochLinking.java b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorEncounterConfirmationAndEpochLinking.java new file mode 100644 index 0000000000000000000000000000000000000000..022bce22c7aaa7fee9e080f150affd324836bdef --- /dev/null +++ b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorEncounterConfirmationAndEpochLinking.java @@ -0,0 +1,317 @@ +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 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 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 mySeenNonces = new HashSet<>(); + Set recvSeenNonces = new HashSet<>(); + List> 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 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 confirmPubKeys = ESClient.getInstance().getTopicTexts(advertsToConfirm); + + // 6) calculate the secret key and eids for all encounters for which you got a dh pub key + List> eidsToCreate = new ArrayList<>(); + List 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> 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 msgsToSend = new ArrayList<>(); + List toPostEntries = encountersBridge.getEntriesToPostForLinking(); + List entriesToPostOn = new ArrayList<>(toPostEntries.size()); + List 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 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> 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> cursorAndMsgs = CommunicationHelpers.getMsgsPage(continuationCursor, context); + List 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 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); + } + }); + } +} diff --git a/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorEncounterFormationCore.java b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorEncounterFormationCore.java new file mode 100644 index 0000000000000000000000000000000000000000..6704ac44135de2a45e60ffcd6d245773d1e9b7f3 --- /dev/null +++ b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorEncounterFormationCore.java @@ -0,0 +1,151 @@ +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; + +/** + * SDDR_Core implements the core functionality of the SDDR protocol. It is started and called by the + * SDDR_Core_Service. The functionalities it provides are: + * - running discovery + * - processing and storing encounter information in persistent storage + * - running epoch changes and confirmation + */ +public class SimulatorEncounterFormationCore implements Runnable { + private static final String TAG = SimulatorEncounterFormationCore.class.getSimpleName(); + protected static final int NUM_SIMULATED_DEVICES = 10; + private static final int NUM_SIMULATED_EPOCHS = 20; + protected static List mDHPubKeys = new ArrayList<>(NUM_SIMULATED_EPOCHS); + protected static List mDHNonces = new ArrayList<>(NUM_SIMULATED_EPOCHS); + protected static List mDHFullKeys = new ArrayList<>(NUM_SIMULATED_EPOCHS); + protected static List otherDHPubKeys = new ArrayList<>(NUM_SIMULATED_DEVICES*NUM_SIMULATED_EPOCHS); + protected static List otherDHNonces = new ArrayList<>(NUM_SIMULATED_DEVICES*NUM_SIMULATED_EPOCHS); + protected static List otherDHFullKeys = new ArrayList<>(NUM_SIMULATED_DEVICES*NUM_SIMULATED_EPOCHS); + protected static int CURRENT_EPOCH = 0; + + protected static Identifier mDHPubKey; + protected static Identifier mNonce; + protected static Identifier mDHFullKey; + protected static Map> ongoingEncounters = new HashMap<>(); + protected SimulatorEncounterConfirmationAndEpochLinking encounterConfirmationAndEpochLinking; + + private BluetoothManager bluetoothManager; + private BluetoothAdapter mBluetoothAdapter; + private SimulatorAdvertiser mAdvertiser; + private SimulatorScannerProcessor mScannerProcessor; + private long changeEpochTime; + protected Context mService; + + public SimulatorEncounterFormationCore(EncounterFormationService service) { + this.mService = service; + } + + private void initialize() { + Looper.prepare(); + Log.v(TAG, "Initialize SDDRCore on thread " + Thread.currentThread().getName()); + this.bluetoothManager = (BluetoothManager) mService.getSystemService(Context.BLUETOOTH_SERVICE); + mBluetoothAdapter = bluetoothManager.getAdapter(); + mAdvertiser = new SimulatorAdvertiser(); + mScannerProcessor = new SimulatorScannerProcessor(); + + mAdvertiser.initialize(mBluetoothAdapter); + mScannerProcessor.initialize(mBluetoothAdapter, this); + + changeEpochTime = System.currentTimeMillis() + CHANGE_EPOCH_TIME; + encounterConfirmationAndEpochLinking = new SimulatorEncounterConfirmationAndEpochLinking(mService); + new EncounterEntriesBridge(mService).finalizeOldEncounters(); + initializeSimulatedAdverts(); + } + + private void initializeSimulatedAdverts() { + List> topicsToCreate = new ArrayList<>(); + for (int i = 0; i < NUM_SIMULATED_EPOCHS*NUM_SIMULATED_DEVICES; i++) { + SDDR_Native.c_changeEpoch(); + otherDHPubKeys.add(new Identifier(SDDR_Native.c_getAdvertDHPubKey())); + otherDHFullKeys.add(new Identifier(SDDR_Native.c_getAdvertDHKey())); + otherDHNonces.add(new Identifier(SDDR_Native.c_getMyAdvert())); + topicsToCreate.add(new ImmutablePair<>(otherDHNonces.get(i), otherDHPubKeys.get(i))); + } + for (int i = 0; i < NUM_SIMULATED_EPOCHS; i++) { + SDDR_Native.c_changeEpoch(); + mDHPubKeys.add(new Identifier(SDDR_Native.c_getAdvertDHPubKey())); + mDHFullKeys.add(new Identifier(SDDR_Native.c_getAdvertDHKey())); + mDHNonces.add(new Identifier(SDDR_Native.c_getMyAdvert())); + + for (int j = 0; j < NUM_SIMULATED_DEVICES; j++) { + Identifier sharedSecret = new Identifier(SDDR_Native.c_computeSecretKeyWithSHA( + otherDHFullKeys.get(j+(NUM_SIMULATED_DEVICES*i)).getBytes(), + mDHNonces.get(i).getBytes(), + mDHPubKeys.get(i).getBytes())); + topicsToCreate.add(new ImmutablePair<>(sharedSecret, sharedSecret)); + } + } + Log.d(TAG, "Creating " + topicsToCreate.size() + " topics"); + ESClient.getInstance().createTopics(topicsToCreate); + } + + public void run() { + initialize(); + Log.v(TAG, "Running core on thread " + Thread.currentThread().getName()); + startAdvertisingAndUpdateAdvert(); + mScannerProcessor.startScanning(); + } + + public void stop() { + mAdvertiser.stopAdvertising(); + mScannerProcessor.stopScanning(); + } + + private void startAdvertisingAndUpdateAdvert() { + mDHPubKey = mDHPubKeys.get(CURRENT_EPOCH); + mDHFullKey = mDHFullKeys.get(CURRENT_EPOCH); + mNonce = mDHNonces.get(CURRENT_EPOCH); + mAdvertiser.setAdData(mNonce.getBytes()); + mAdvertiser.resetAdvertiser(); + } + + public void postScanProcessing() { + if (changeEpochTime < System.currentTimeMillis()) { + Log.d(TAG, "CHANGING EPOCH"); + CURRENT_EPOCH++; + assert(CURRENT_EPOCH <= NUM_SIMULATED_EPOCHS); + changeEpochTime += CHANGE_EPOCH_TIME; + startAdvertisingAndUpdateAdvert(); + + // add database entries for our new nonce pairs with all currently receiving adverts + encounterConfirmationAndEpochLinking.insertNewNoncesIntoDatabase(); + // post our nonces on the appropriate "old nonce" encounter topic (the "oldest" topic for this encounter) + encounterConfirmationAndEpochLinking.postUnpostedNoncesThatNeedLinking(); + // retrieve and link using the new nonces we had posted on our encounter topics + // TODO This isn't actually tested by the simulator because no simulated devices post link messages + encounterConfirmationAndEpochLinking.processNoncesForLinking(); + // because we're simulating, do the whole ES stuff + encounterConfirmationAndEpochLinking.confirmEncountersViaES(); + } + } + + public static Map> getOngoingEncounters() { + return ongoingEncounters; + } +} diff --git a/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorScannerProcessor.java b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorScannerProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..aaa14a1bf9140be8c5dda10118e7f59d328f330b --- /dev/null +++ b/ebclibrary/src/main/java/org/mpisws/encounters/encounterformation/simulator/SimulatorScannerProcessor.java @@ -0,0 +1,207 @@ +package org.mpisws.encounters.encounterformation.simulator; + +/** + * Created by tslilyai on 10/18/17. + * + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; +import android.util.Log; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.mpisws.encounters.encounterformation.SDDR_Native; +import org.mpisws.encounters.encounterformation.SDDR_Proto; +import org.mpisws.encounters.encounterhistory.events.EncounterEndedEvent; +import org.mpisws.encounters.encounterhistory.events.EncounterEvent; +import org.mpisws.encounters.encounterhistory.events.EncounterStartedEvent; +import org.mpisws.encounters.encounterhistory.events.RSSIEntry; +import org.mpisws.helpers.Identifier; +import org.mpisws.helpers.Utils; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER; +import static org.mpisws.encounters.encounterformation.simulator.SimulatorEncounterFormationCore.CURRENT_EPOCH; +import static org.mpisws.encounters.encounterformation.simulator.SimulatorEncounterFormationCore.NUM_SIMULATED_DEVICES; +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.encounters.encounterformation.simulator.SimulatorEncounterFormationCore.ongoingEncounters; +import static org.mpisws.encounters.encounterformation.simulator.SimulatorEncounterFormationCore.otherDHNonces; + +/** + * Scans for Bluetooth Low Energy Advertisements matching a filter and displays them to the user. + */ +public class SimulatorScannerProcessor { + private static final String TAG = SimulatorScannerProcessor.class.getSimpleName(); + private BluetoothAdapter mBluetoothAdapter; + private BluetoothLeScanner mBluetoothLeScanner; + private ScanCallback mScanCallback; + private SimulatorEncounterFormationCore core; + + public SimulatorScannerProcessor() { } + + public void initialize(BluetoothAdapter btAdapter, SimulatorEncounterFormationCore core) { + this.mBluetoothAdapter = btAdapter; + mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); + this.core = core; + } + + /** + * Start scanning for BLE Advertisements (and stop after a set period of time). + */ + public void startScanning() { + if (mScanCallback == null) { + mScanCallback = new SDDRScanCallback(); + mBluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback); + } else { + mBluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback); + } + Log.v(TAG, "Starting Scanning on thread " + Thread.currentThread().getName()); + } + + /** + * Stop scanning for BLE Advertisements. + */ + public void stopScanning() { + mBluetoothLeScanner.stopScan(mScanCallback); + } + + /** + * Filter our scans so we only discover SDDR_API devices + */ + private List buildScanFilters() { + List scanFilters = new ArrayList<>(); + return scanFilters; + } + + /** + * Return a {@link ScanSettings} object (default settings for now) + */ + private ScanSettings buildScanSettings() { + ScanSettings.Builder builder = new ScanSettings.Builder(); + builder.setScanMode(SCAN_MODE_LOW_POWER); + return builder.build(); + } + + /** + * Custom ScanCallback object. Calls the native function to process discoveries for encounters. + */ + private class SDDRScanCallback extends ScanCallback { + private void simulateReceivingResults() { + // get all "new" nonces you should've "received" + for (int i = 0; i < NUM_SIMULATED_DEVICES; i++) { + // for each new nonce, call processScanResult + byte[] newnonce = otherDHNonces.get(i+(NUM_SIMULATED_DEVICES*CURRENT_EPOCH)).getBytes(); + long pkid = SDDR_Native.c_processScanResult(-1, newnonce, System.currentTimeMillis()); + } + } + + @Override + public void onBatchScanResults(List results) { + super.onBatchScanResults(results); + Log.v(TAG, results.size() + " Batch Results Found on thread " + Thread.currentThread().getName()); + + SDDR_Native.c_preDiscovery(); + simulateReceivingResults(); + SDDR_Native.c_postDiscovery(); + processEncounters(); + core.postScanProcessing(); + } + + @Override + public void onScanResult(int callbackType, ScanResult result) { + super.onScanResult(callbackType, result); + simulateReceivingResults(); + } + + @Override + public void onScanFailed(int errorCode) { + Log.v(TAG, "Scanning failed: " + errorCode); + } + } + + private void processEncounters() { + List pkids = new ArrayList<>(); + for (Iterator iterator = SDDR_Native.c_EncounterMsgs.iterator(); iterator.hasNext(); ) { + byte[] msg = iterator.next(); + final SDDR_Proto.Event event; + + try { + event = SDDR_Proto.Event.parseFrom(msg); + } catch (InvalidProtocolBufferException e) { + e.printStackTrace(); + return; + } + + Utils.myAssert(event.hasEncounterEvent()); + final SDDR_Proto.Event.EncounterEvent subEvent = event.getEncounterEvent(); + final SDDR_Proto.Event.EncounterEvent.EventType type = subEvent.getType(); + final long time = subEvent.getTime(); + final long pkid = subEvent.getPkid(); + pkids.add(pkid); + final String address = subEvent.getAddress(); + final List rssiEventsPB = subEvent.getRssiEventsList(); + final List advertsPB = subEvent.getSharedSecretsList(); + + // Transforming the lists into the appropriate Java structures for EncounterEvent + final List rssiEvents = new LinkedList<>(); + for (SDDR_Proto.Event.EncounterEvent.RSSIEvent rssiEvent : rssiEventsPB) { + rssiEvents.add(new RSSIEntry(rssiEvent.getTime(), rssiEvent.getRssi())); + } + + Identifier receivedNonce = null; + // we don't have to track devices' adverts if we're trying to connect to them now + if (advertsPB.size() > 0) { + // TODO hack there really is only ever one nonce per encounter + receivedNonce = new Identifier(advertsPB.get(0).toByteArray()); + } + + EncounterEvent encEvent; + switch (type) { + case UnconfirmedStart: + encEvent = new EncounterStartedEvent(pkid, time, receivedNonce, mNonce, mDHPubKey, mDHFullKey); + Log.v(TAG, "[EncounterEvent] Tentative encounter started at " + time); + ongoingEncounters.put(receivedNonce.toString(), new ImmutablePair<>(pkid, time)); + break; + case End: + encEvent = new EncounterEndedEvent(pkid, time, rssiEvents); + Log.v(TAG, "[EncounterEvent] Encounter Ended at " + time); + ongoingEncounters.remove(receivedNonce.toString()); + break; + default: + encEvent = null; + } + + Log.v(TAG, "\tPKID = " + pkid + ", Address = " + address); + + if (encEvent != null) encEvent.broadcast(core.mService); + iterator.remove(); + } + EncounterEvent.insertLocationForEncounters(core.mService); + } +} diff --git a/ebclibrary/src/main/java/org/mpisws/encounters/encounterhistory/bridges/EncounterEntriesBridge.java b/ebclibrary/src/main/java/org/mpisws/encounters/encounterhistory/bridges/EncounterEntriesBridge.java index e0a61a5b5834001715ee1eccac963b276e3993ae..8dcf45c6ba8eb807fcb817ad0736a752fe289837 100644 --- a/ebclibrary/src/main/java/org/mpisws/encounters/encounterhistory/bridges/EncounterEntriesBridge.java +++ b/ebclibrary/src/main/java/org/mpisws/encounters/encounterhistory/bridges/EncounterEntriesBridge.java @@ -231,7 +231,7 @@ public class EncounterEntriesBridge extends AbstractEncountersBridge new ESBatchTester(32).simulateConfirmations(), 15000); break; - case R.id.testSQLQueries: + case R.id.testSendMessages: ebc.getSDDRClient().updateDatabaseOnAgent(); ebc.getCommunicationClient().sendMultiHopMessageToEncounters("hello world", null, null, true); break; - /* List msgs = new ArrayList<>(); - for (EncounterQueryConstraint constraint : TestConstraintQuery.getConstraints()) { - msgs.add(new EbCMsgPackage(new ESMessage("hello world"), constraint)); - } - ebc.getCommunicationClient().sendMessages(msgs); - break; - case R.id.testMessages: - msgs = new ArrayList<>(); - for (int i = 0 ; i < 10; i++) { - msgs.add(new EbCMsgPackage(new ESMessage("hello world"), new AllEncountersConstraint())); - } - ebc.getCommunicationClient().sendMessages(msgs); - ESClient.GetUnreadMsgsCallback callback = new ESClient.GetUnreadMsgsCallback() { + case R.id.testReceiveMessages: + ESClient.GetReceivedMessagesCallback callback = new ESClient.GetReceivedMessagesCallback() { @Override - public void processReceivedMessages(List msgs) { - for (ESMessage msg : msgs) { + public void processReceivedMessages(List msgs) { + for (ReceivedMessageWrapper msg : msgs) { System.out.print(msg.getTopicHandle() + ", "); + System.out.print(msg.getMsgType()); System.out.println("\n"); } } }; ebc.getCommunicationClient().getUnreadMsgs(callback); - break;*/ + break; case R.id.testEndToEndES: ebc.getSDDRClient().startFormingEncounters(); ebc.getSDDRClient().confirmAndCommunicateUsingNetwork(); break; - case R.id.testWithoutWifi: + case R.id.testWithoutConfirmation: ESClient.getInstance().setESCredentials("user", "fakeauth"); ebc.getSDDRClient().startFormingEncounters(); break; diff --git a/testapp/src/main/java/org/mpisws/testapp/tests/TestConstraintQuery.java b/testapp/src/main/java/org/mpisws/testapp/tests/TestConstraintQuery.java index 57e575726c93fc7d1ec168ab4e59958f44f377c0..d4ab8954669f2cdcedba2519c10a7c134bf69b03 100644 --- a/testapp/src/main/java/org/mpisws/testapp/tests/TestConstraintQuery.java +++ b/testapp/src/main/java/org/mpisws/testapp/tests/TestConstraintQuery.java @@ -16,7 +16,6 @@ public class TestConstraintQuery { static EncounterQueryConstraint.SpaceRegion spaceRegion = new EncounterQueryConstraint.SpaceRegion(23849230, 23849320, 1000); static long starttime = 10000; static long overlapduration = 10000; - static long encountercausaltime = 19210; public static void main(String[] args) { for (EncounterQueryConstraint constraint : getConstraints()) { @@ -25,11 +24,8 @@ public class TestConstraintQuery { } public static List getConstraints (){ - for (int i = 0; i < 3; i++) { - } - List constraints = new ArrayList<>(); - constraints.add(new SpaceTimeIntersectConstraint(starttime, starttime+1000, overlapduration, spaceRegion, encountercausaltime)); + constraints.add(new SpaceTimeIntersectConstraint(starttime, starttime+1000, overlapduration, spaceRegion, true)); constraints.add(new AllEncountersConstraint()); return constraints; } diff --git a/testapp/src/main/res/layout/activity_main.xml b/testapp/src/main/res/layout/activity_main.xml index fd056cebca00f29a55eee91103b515be034b9229..b41e7bd2aedb3f15f56c4e4383003ef5f15f5c8f 100644 --- a/testapp/src/main/res/layout/activity_main.xml +++ b/testapp/src/main/res/layout/activity_main.xml @@ -44,22 +44,22 @@ android:layout_height="wrap_content" android:text="Test ES Functions" />