diff --git a/README.md b/README.md index 3b94018..400f599 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,77 @@ # flashwifi -## What is this? +Share remaining data from your android phone as a hotspot and get paid in iota. -[WiFiota](https://tobywoerthle.github.io/flashWiFiSite/) +[WiFiota promotion website](https://tobywoerthle.github.io/flashWiFiSite/) -## ToDos +# How does it work? -Iota stuff: 2.5d -- [x] Address generation TOBY -- [ ] Address storage TOBY -- [x] Withdrawal and deposit for the internal wallet 0.75d TOBY -- [ ] Attach/close flash channel to tangle (deposit to root of tree) -> "api.sendTransfer" 0.75d DANIEL -- [x] UI 1.0d TOBY +## Iota setup -Flash channel: 1d WLAD -- [x] J2V8 interfaces… 1d WLAD -- [ ] overwrite flash object 0.25d WLAD -- [ ] close channel 0.25d WLAD -- [x] Create flash channel 0.75d WLAD -- [ ] integrate into repo 0.5d WLAD +The app creates a seed for every user and stores it with a password in SharedEncryptedPreferences. +There is a withdraw and deposit view which can be used to interact with the seed. -Billing protocol: 2d DANIEL -- [ ] general stuff 0.5d DANIEL -- [ ] integrate flash channel 0.5d DANIEL -- [ ] UI 1.0 d DANIEL +So far everything runs on a testnode but a live node can be set. -Network monitoring: 2d TOBY -- [ ] Client and server measure data usage TOBY -- [ ] Build sweet graph TOBY +## Discovery -WiFi direct discovery: 1d -- [ ] Negotiation protocol 0.5d DANIEL -- [ ] Password generation, open hotspot, close hotspot on timeout etc. 0.5d DANIEL +Happens with WiFi P2P. The consumer selects a peer manually so far (-> in the future automatic service discovery) +Two devices then open a wifi direct group and start the **Negotiation Protocol** + +## Negotiation Protocol + +The devices talk about each others, how much data they want to share or consume and +whether they could agree on a price and time. + +* If so the future hotspot generates a random SSID like "Iotidy-". +* And generates a random number which is used as the password +* The hotspot sends it to the consumer and now they start the Billing Protocol + +ToDo: Add full protocol + +## Billing Protocol + +The hotspot is created and the device proving the hotspot starts a new ServerSocket. +Once the consumer is connected to the hotspot he tries to connect to the gateway and +start the Billing Protocol. + +* "Hello I am Client and I am in state NOT_PAIRED" +* "Hello, Client! I am Hotspot and I am in state NOT_PAIRED" +... + +(ToDo: add full protocol) + +### Payment + +Every minute a bill is exchanged which contains information about used megabytes and durances. +A **IOTA FLASH CHANNEL** is established between the two (-> in the future more than two parties). +Every minute can be associated with a token transfer in the channel. + +* Signing a transfer signifies agreement to the the bill +* The funding of the channel results in shared risk on both parties so they have no incentive to leave earlier +* Time guards track whether timing constraints weren't fulfilled (channel not funded, no peers...) + + +### End + +Every bill can contain a "closeAfterwards" flag. Which ends the whole process with a short +exchange of data for the consumer. +The hotspot still has to attach the flash channel to the tangle. + +# Good to know +The WiFi P2P roles in a WiFi group are not fixed. So each time either the future hotspot or consumer act as server and client. + +We wrapped the iota.flash.js with additional javascript bridges and connect them to the **Jota - iota java lib**. The javascript code for the flash channel is executed within J2V8. + + +# Next big steps + +* [ ] Implement an automatic Wifiota hotspot discovery +* [ ] Implement iota.flash.js in Java +* [ ] Build a hardware prototype for Wifiota hotspot (Raspy + data plan + battery) +* [ ] Extend the protocol to multiple parties, endless roaming and backoffice functions to close conflicts after physical contacts isn't possible anymore -Frontends: 6.5d -- [ ] Initial setup view (seed generation, set password): 0.5d DANIEL - -- [ ] Settings (security, hotspot price per megabyte, client max price per megabyte, tangle attachment timeout, make new setup, change password) 1.5d TOBY -- [ ] Search hotspot: 0.5d DANIEL -- [x] Start hotspot: 0.5d DANIEL -- [x] Fund wallet: 1d TOBY -- [x] Withdraw from wallet (+QR code) 0.5 d TOBY -- [ ] Tethering status (-> fixed Notification with stop button) 1d WLAD -- [ ] UI bugs (back button) 1d -Other stuff 2.5d -- [ ] Videos: 2d WLAD + TOBY + DANIEL -- [ ] Website: 0.5d WLAD Total: 17d diff --git a/app/src/main/java/com/flashwifi/wifip2p/RoamingActivity.java b/app/src/main/java/com/flashwifi/wifip2p/RoamingActivity.java index 954ee06..35891dd 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/RoamingActivity.java +++ b/app/src/main/java/com/flashwifi/wifip2p/RoamingActivity.java @@ -104,7 +104,7 @@ public class RoamingActivity extends AppCompatActivity { apConnected.setChecked(true); mService.resetBillingState(); // when the AP is setup we can start the server - startBillingProtocol(); + startBillingProtocol(5000); } else if (message.equals("AP FAILED")) { apConnected.setChecked(false); Toast.makeText(getApplicationContext(), "Could not create Access point", Toast.LENGTH_LONG).show(); @@ -192,8 +192,15 @@ public class RoamingActivity extends AppCompatActivity { } - private void startBillingProtocol() { + private void startBillingProtocol(int milliseconds_sleep) { // setup the flash channel etc... + try { + Log.d(TAG, "startBillingProtocol: wait for some milliseconds"); + Thread.sleep(milliseconds_sleep); + Log.d(TAG, "startBillingProtocol: now let's go"); + } catch (InterruptedException e) { + e.printStackTrace(); + } if (mService.isInRoleHotspot()) { mService.startBillingServer(address); } else { diff --git a/app/src/main/java/com/flashwifi/wifip2p/accesspoint/ConnectTask.java b/app/src/main/java/com/flashwifi/wifip2p/accesspoint/ConnectTask.java index 37093b9..28a51c8 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/accesspoint/ConnectTask.java +++ b/app/src/main/java/com/flashwifi/wifip2p/accesspoint/ConnectTask.java @@ -2,6 +2,7 @@ package com.flashwifi.wifip2p.accesspoint; import android.content.Context; import android.content.Intent; +import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; @@ -58,29 +59,69 @@ public class ConnectTask extends AsyncTask { int netId = wifiManager.addNetwork(wifiConfig); boolean connected = false; + + int max_tries = 10; - while (!connected) { + while (!connected && max_tries > 0) { + max_tries--; Log.d(TAG, "doInBackground: try to find the network"); List list = wifiManager.getConfiguredNetworks(); + WifiInfo wifiInfo; for( WifiConfiguration i : list ) { if(i.SSID != null && i.SSID.equals("\"" + ssid + "\"")) { Log.d(TAG, "doInBackground: found it!!!"); wifiManager.disconnect(); wifiManager.disableNetwork(old_network_id); + wifiManager.reconnect(); boolean worked = wifiManager.enableNetwork(i.networkId, true); if (worked) { - Log.d(TAG, "doInBackground: WORKED enableNetwork"); - sendUpdateUIBroadcastWithMessage("AP SUCCESS"); + connected = true; + boolean connected_to_it = false; + String new_ssid; + Log.d(TAG, "New network added!"); + int max_seconds = 10; + boolean wrong_network = false; + while (!connected_to_it && max_seconds > 0 && !wrong_network) { + Log.d(TAG, "try to connect to hotspot"); + wifiInfo = wifiManager.getConnectionInfo(); + if (wifiInfo.getSupplicantState() == SupplicantState.COMPLETED) { + new_ssid = wifiInfo.getSSID(); + if (new_ssid.contains(ssid)) { + connected_to_it = true; + Log.d(TAG, "doInBackground: WORKED enableNetwork"); + sendUpdateUIBroadcastWithMessage("AP SUCCESS"); + } else { + Log.d(TAG, "WRONG NETWORK"); + sendUpdateUIBroadcastWithMessage("AP FAILED"); + wrong_network = true; + } + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + max_seconds--; + if (max_seconds == 0) { + Log.d(TAG, "TRIED TOO LONG TO CONNECT TO HOTSPOT -> stop"); + connected = false; + sendUpdateUIBroadcastWithMessage("AP FAILED"); + } + } + } else { Log.d(TAG, "doInBackground: Error connecting to the network"); - sendUpdateUIBroadcastWithMessage("AP FAILED"); + Log.d(TAG, "doInBackground: Let's try it again"); + //sendUpdateUIBroadcastWithMessage("AP FAILED"); } - wifiManager.reconnect(); - - connected = true; - break; } } + if (max_tries == 0) { + Log.d(TAG, "doInBackground: Error connecting to the network"); + Log.d(TAG, "doInBackground: THIS WAS THE LAST TRY"); + sendUpdateUIBroadcastWithMessage("AP FAILED"); + } + } /*Log.d(TAG, "doInBackground: now wait"); diff --git a/app/src/main/java/com/flashwifi/wifip2p/billing/BillingClient.java b/app/src/main/java/com/flashwifi/wifip2p/billing/BillingClient.java index 8431659..937fe80 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/billing/BillingClient.java +++ b/app/src/main/java/com/flashwifi/wifip2p/billing/BillingClient.java @@ -3,6 +3,8 @@ package com.flashwifi.wifip2p.billing; import android.accounts.Account; import android.content.Context; import android.content.Intent; +import android.net.wifi.WifiManager; +import android.text.format.Formatter; import android.util.Log; import com.flashwifi.wifip2p.datastore.PeerInformation; @@ -21,9 +23,14 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; +import java.util.Enumeration; + +import static android.content.Context.WIFI_SERVICE; /** * 1) This class keeps the socket connection alive. @@ -65,17 +72,24 @@ public class BillingClient { - public void start(String serverIPAddress) { + public void start(String serverIPAddress, InetAddress inetAddress) { int error_count = 0; while (state != State.CLOSED && state != State.ERROR) { try { // create client socket that connects to server - socket = new Socket(serverIPAddress, PORT); + Log.d(TAG, "try to connect to 192.168.43.1 PORT " + Integer.toString(PORT)); + socket = new Socket("192.168.43.1", PORT); socket.setSoTimeout(clientTimeoutMillis); + Log.d(TAG, "connection is established"); + + // wrap the socket socketWrapper = new SocketWrapper(socket); + Log.d(TAG, "socket wrapped"); + + Log.d(TAG, "We are in state: " + state); if (state == State.NOT_PAIRED) { // send current state @@ -87,10 +101,11 @@ public class BillingClient { // ask the hotspot to open the flash channel // get the negotiated data + // get the necessary values NegotiationOffer offer = PeerStore.getInstance().getLatestNegotiationOffer(mac); NegotiationOfferAnswer answer = PeerStore.getInstance().getLatestNegotiationOfferAnswer(mac); NegotiationFinalization finalization = PeerStore.getInstance().getLatestFinalization(mac); - // get the necessary values + // ToDo: replace magic number with setting int totalMegabytes = 100; int treeDepth = 8; @@ -179,6 +194,9 @@ public class BillingClient { sendUpdateUIBroadcastWithMessage("IOException"); e.printStackTrace(); error_count++; + } catch (Exception e) { + e.printStackTrace(); + error_count++; } finally { if (socket != null) { try { diff --git a/app/src/main/java/com/flashwifi/wifip2p/billing/BillingServer.java b/app/src/main/java/com/flashwifi/wifip2p/billing/BillingServer.java index 1e85d1d..c3a8873 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/billing/BillingServer.java +++ b/app/src/main/java/com/flashwifi/wifip2p/billing/BillingServer.java @@ -26,10 +26,12 @@ import com.google.gson.GsonBuilder; import java.io.IOException; import java.math.BigDecimal; +import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; +import java.util.Enumeration; import static android.content.Context.WIFI_SERVICE; @@ -70,6 +72,8 @@ public class BillingServer { this.context = context; this.mac = mac; + Log.d(TAG, "BillingServer: MAC of other part: " + mac); + // get the negotiated data NegotiationOffer offer = PeerStore.getInstance().getLatestNegotiationOffer(mac); NegotiationOfferAnswer answer = PeerStore.getInstance().getLatestNegotiationOfferAnswer(mac); @@ -79,7 +83,7 @@ public class BillingServer { int totalMegabytes = 100; int treeDepth = 8; this.digests = new String[]{"1234", "2345", "3456"}; - int timeoutMinutesServer = 20 * 60 * 1000; + int timeoutMinutesServer = 2 * 60 * 1000; this.hotspotRefundAddress = finalization.getHotspotRefundAddress(); this.channelRootAddress = finalization.getDepositAddressFlashChannel(); @@ -102,7 +106,8 @@ public class BillingServer { Log.d(TAG, "start: Billing server has been started"); - // ToDo: receive end of roaming broadcast + int max_errors = 1; + int errors = 0; while (state != State.CLOSED) { try { @@ -238,10 +243,21 @@ public class BillingServer { } state = State.FULLY_ATTACHED; } + } catch (SocketException e) { + e.printStackTrace(); + sendUpdateUIBroadcastWithMessage("Socket exception"); + errors++; } catch (IOException e) { e.printStackTrace(); + sendUpdateUIBroadcastWithMessage("IOException"); + errors++; } catch (InterruptedException e) { + sendUpdateUIBroadcastWithMessage("InterruptedException"); e.printStackTrace(); + errors++; + } catch (Exception e) { + e.printStackTrace(); + errors++; } finally { try { if (socketWrapper != null) { @@ -258,6 +274,11 @@ public class BillingServer { } } + if (errors >= max_errors) { + Log.d(TAG, "start: error count too high"); + state = State.ERROR; + sendUpdateUIBroadcastWithMessage("Exit"); + } } } diff --git a/app/src/main/java/com/flashwifi/wifip2p/broadcast/WiFiDirectBroadcastService.java b/app/src/main/java/com/flashwifi/wifip2p/broadcast/WiFiDirectBroadcastService.java index a75792e..85d697f 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/broadcast/WiFiDirectBroadcastService.java +++ b/app/src/main/java/com/flashwifi/wifip2p/broadcast/WiFiDirectBroadcastService.java @@ -53,6 +53,7 @@ import org.apache.commons.lang3.ArrayUtils; import java.io.IOException; import java.lang.reflect.Method; import java.math.BigInteger; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; @@ -109,6 +110,7 @@ public class WiFiDirectBroadcastService extends Service { boolean apRuns = false; ConnectTask connectTask; private boolean negotiatorRunning = false; + private String ownMacAddressStore = null; @Override public int onStartCommand(Intent intent, int flags, int startId) { @@ -237,6 +239,7 @@ public class WiFiDirectBroadcastService extends Service { byte[] myIPAddress = BigInteger.valueOf(dhcp.gateway).toByteArray(); ArrayUtils.reverse(myIPAddress); InetAddress myInetIP = null; + Inet6Address myInet6IP = null; String routerIP = null; try { myInetIP = InetAddress.getByAddress(myIPAddress); @@ -246,7 +249,7 @@ public class WiFiDirectBroadcastService extends Service { routerIP = "192.168.43.1"; } Log.d(TAG, "DHCP gateway: " + routerIP); - billingClient.start(routerIP); + billingClient.start(routerIP, myInetIP); // ToDo: handle billingServer EXIT CODES // -> close the roaming etc. } @@ -294,32 +297,38 @@ public class WiFiDirectBroadcastService extends Service { } public String getWFDMacAddress(){ + Log.d(TAG, "getWFDMacAddress: GET MAC ADRESS ========================="); + if (ownMacAddressStore != null) { + return ownMacAddressStore; + } try { List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); for (NetworkInterface ntwInterface : interfaces) { + byte[] byteMac = ntwInterface.getHardwareAddress(); + if (byteMac==null){ + return null; + } + StringBuilder strBuilder = new StringBuilder(); + for (int i=0; i0){ + strBuilder.deleteCharAt(strBuilder.length()-1); + } + + Log.d(TAG, "getWFDMacAddress: " + strBuilder.toString()); + if (ntwInterface.getName().equalsIgnoreCase("p2p0")) { - byte[] byteMac = ntwInterface.getHardwareAddress(); - if (byteMac==null){ - return null; - } - StringBuilder strBuilder = new StringBuilder(); - for (int i=0; i0){ - strBuilder.deleteCharAt(strBuilder.length()-1); - } - - return strBuilder.toString(); + this.ownMacAddressStore = strBuilder.toString(); } } } catch (Exception e) { Log.d(TAG, e.getMessage()); } - return null; + return this.ownMacAddressStore; } private void stopAllTasks() { @@ -374,6 +383,7 @@ public class WiFiDirectBroadcastService extends Service { NegotiationFinalization negFin = PeerStore.getInstance().getLatestFinalization(peer_mac_address); String ssid = negFin.getHotspotName(); String key = negFin.getHotspotPassword(); + Log.d(TAG, "YYY: " + peer_mac_address); sendStartRoamingBroadcast(peer_mac_address, ssid, key); } else if (peer_mac_address != null) { PeerStore.getInstance().unselectAll(); @@ -443,6 +453,7 @@ public class WiFiDirectBroadcastService extends Service { NegotiationFinalization negFin = PeerStore.getInstance().getLatestFinalization(peer_mac_address); String ssid = negFin.getHotspotName(); String key = negFin.getHotspotPassword(); + Log.d(TAG, "ZZZ: " + peer_mac_address); sendStartRoamingBroadcast(peer_mac_address, ssid, key); } else { Log.d(TAG, "run: could not start roaming"); @@ -585,13 +596,21 @@ public class WiFiDirectBroadcastService extends Service { NegotiationOffer offer = PeerStore.getInstance().getLatestNegotiationOffer(address); NegotiationOfferAnswer offerAnser = PeerStore.getInstance().getLatestNegotiationOfferAnswer(address); NegotiationFinalization finalization = PeerStore.getInstance().getLatestFinalization(address); - BillingOpenChannel openChannel = PeerStore.getInstance().getPeer(address).getBillingOpenChannel(); - BillingOpenChannelAnswer openChannelAnswer = PeerStore.getInstance().getPeer(address).getBillingOpenChannelAnswer(); + BillingOpenChannel openChannel = PeerStore.getInstance().getLatestBillingOpenChannel(address); + BillingOpenChannelAnswer openChannelAnswer = PeerStore.getInstance().getLatestBillingOpenChannelAnswer(address); String multisigAddress = finalization.getDepositAddressFlashChannel(); + int timeoutClientSeconds, timeoutHotspotSeconds; - int timeoutClientSeconds = openChannel.getTimeoutMinutesClient(); - int timeoutHotspotSeconds = openChannelAnswer.getTimeoutMinutesHotspot() * 60; + // ToDo: Fix this bug! + // openChannel should not be null here + if (openChannel != null) { + timeoutClientSeconds = openChannel.getTimeoutMinutesClient(); + timeoutHotspotSeconds = openChannelAnswer.getTimeoutMinutesHotspot() * 60; + } else { + timeoutClientSeconds = 3; + timeoutHotspotSeconds = 3 * 60; + } int timeout = (isInRoleHotspot()) ? timeoutHotspotSeconds : timeoutClientSeconds; int clientDeposit = finalization.getDepositClientFlashChannelInIota(); diff --git a/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerStore.java b/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerStore.java index 77db7b4..96e20f0 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerStore.java +++ b/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerStore.java @@ -1,5 +1,7 @@ package com.flashwifi.wifip2p.datastore; +import android.util.Log; + import com.flashwifi.wifip2p.protocol.BillingCloseChannel; import com.flashwifi.wifip2p.protocol.BillingCloseChannelAnswer; import com.flashwifi.wifip2p.protocol.BillingOpenChannel; @@ -22,7 +24,20 @@ public class PeerStore { private HashMap peers = new HashMap(); - public static PeerStore getInstance() { + private NegotiationOffer storedNegotiationOffer = null; + private NegotiationOfferAnswer storedNegotiationOfferAnswer = null; + private NegotiationFinalization storedNegotiationFinalization = null; + + private BillingOpenChannel storedBillingOpenChannel = null; + private BillingOpenChannelAnswer storedBillingOpenChannelAnswer = null; + + private boolean debug = true; + + private String lastMAC = null; + + + + public static synchronized PeerStore getInstance() { return ourInstance; } @@ -30,7 +45,7 @@ public class PeerStore { // ToDo: load and store stuff in SQLite or gson JSON } - public void clear() { + public synchronized void clear() { peers.clear(); } @@ -39,7 +54,7 @@ public class PeerStore { * * @return was the peer created? */ - public boolean updateOrCreate(PeerInformation peer) { + public synchronized boolean updateOrCreate(PeerInformation peer) { String macAddress = peer.getWifiP2pDevice().deviceAddress; boolean exists = peers.containsKey(macAddress); @@ -55,7 +70,7 @@ public class PeerStore { return !exists; } - public ArrayList getPeerArrayList() { + public synchronized ArrayList getPeerArrayList() { ArrayList ary = new ArrayList<>(peers.values()); Collections.sort(ary, new Comparator() { @Override @@ -70,13 +85,13 @@ public class PeerStore { /** * This sets an old flag to all the peers */ - public void makeNewGeneration() { + public synchronized void makeNewGeneration() { for (PeerInformation peer : peers.values()) { peer.incrementAge(); } } - private PeerInformation getOrCreatePeer(String address_) { + private synchronized PeerInformation getOrCreatePeer(String address_) { address_ = address_.toLowerCase(); if (peers.containsKey(address_)) { return peers.get(address_); @@ -87,74 +102,94 @@ public class PeerStore { return temp; } - public void setLatestOffer(String macAddress, NegotiationOffer offer) { + public synchronized void setLatestOffer(String macAddress, NegotiationOffer offer) { + storedNegotiationOffer = offer; getOrCreatePeer(macAddress.toLowerCase()).setLatestOffer(offer); } - public void setErrorMessage(String macAddress, String msg) { + public synchronized void setErrorMessage(String macAddress, String msg) { getOrCreatePeer(macAddress.toLowerCase()).setErrorMessage(msg); } - public void setSelected(String macAddress, boolean selected) { + public synchronized void setSelected(String macAddress, boolean selected) { getOrCreatePeer(macAddress.toLowerCase()).setSelected(selected); } - public void unselectAll() { + public synchronized void unselectAll() { for (PeerInformation p: peers.values()) { p.setSelected(false); } } - public void setLatestOfferAnswer(String macAddress, NegotiationOfferAnswer answer) { + public synchronized void setLatestOfferAnswer(String macAddress, NegotiationOfferAnswer answer) { + storedNegotiationOfferAnswer = answer; getOrCreatePeer(macAddress.toLowerCase()).setLatestOfferAnswer(answer); } - public void setLatestFinalization(String macAddress, NegotiationFinalization finalization) { + public synchronized void setLatestFinalization(String macAddress, NegotiationFinalization finalization) { + lastMAC = macAddress; + storedNegotiationFinalization = finalization; getOrCreatePeer(macAddress.toLowerCase()).setLatestFinalization(finalization); } - public NegotiationFinalization getLatestFinalization(String macAddress) { + public synchronized NegotiationFinalization getLatestFinalization(String macAddress) { if (peers.containsKey(macAddress.toLowerCase())) { return peers.get(macAddress.toLowerCase()).getLatestFinalization(); } - return null; + return storedNegotiationFinalization; } - public NegotiationOffer getLatestNegotiationOffer(String macAddress) { + public synchronized NegotiationOffer getLatestNegotiationOffer(String macAddress) { if (peers.containsKey(macAddress.toLowerCase())) { return peers.get(macAddress.toLowerCase()).getLatestNegotiationOffer(); } - return null; + return storedNegotiationOffer; } - public NegotiationOfferAnswer getLatestNegotiationOfferAnswer(String macAddress) { + public synchronized NegotiationOfferAnswer getLatestNegotiationOfferAnswer(String macAddress) { if (peers.containsKey(macAddress.toLowerCase())) { return peers.get(macAddress.toLowerCase()).getLatestOfferAnswer(); } - return null; + return storedNegotiationOfferAnswer; } - public void setIPAddress(String macAddress, InetAddress IPAddress) { + public synchronized void setIPAddress(String macAddress, InetAddress IPAddress) { getOrCreatePeer(macAddress.toLowerCase()).setIPAddress(IPAddress.getHostAddress()); } - public void setLatestBillingOpenChannel(String macAddress, BillingOpenChannel o) { + public synchronized void setLatestBillingOpenChannel(String macAddress, BillingOpenChannel o) { + storedBillingOpenChannel = o; getOrCreatePeer(macAddress.toLowerCase()).setBillingOpenChannel(o); } - public void setLatestBillingOpenChannelAnswer(String macAddress, BillingOpenChannelAnswer o) { + public synchronized void setLatestBillingOpenChannelAnswer(String macAddress, BillingOpenChannelAnswer o) { + storedBillingOpenChannelAnswer = o; getOrCreatePeer(macAddress.toLowerCase()).setBillingOpenChannelAnswer(o); } - public void setLatestBillingCloseChannel(String macAddress, BillingCloseChannel o) { + public synchronized BillingOpenChannel getLatestBillingOpenChannel(String macAddress) { + if (peers.containsKey(macAddress.toLowerCase())) { + return peers.get(macAddress.toLowerCase()).getBillingOpenChannel(); + } + return storedBillingOpenChannel; + } + + public synchronized BillingOpenChannelAnswer getLatestBillingOpenChannelAnswer(String macAddress) { + if (peers.containsKey(macAddress.toLowerCase())) { + return peers.get(macAddress.toLowerCase()).getBillingOpenChannelAnswer(); + } + return storedBillingOpenChannelAnswer; + } + + public synchronized void setLatestBillingCloseChannel(String macAddress, BillingCloseChannel o) { getOrCreatePeer(macAddress.toLowerCase()).setBillingCloseChannel(o); } - public void setLatestBillingCloseChannelAnswer(String macAddress, BillingCloseChannelAnswer o) { + public synchronized void setLatestBillingCloseChannelAnswer(String macAddress, BillingCloseChannelAnswer o) { getOrCreatePeer(macAddress.toLowerCase()).setBillingCloseChannelAnswer(o); } - public PeerInformation getPeer(String address) { + public synchronized PeerInformation getPeer(String address) { address = address.toLowerCase(); if (peers.containsKey(address)) { return peers.get(address);