├── AndroidManifest.xml
├── README
├── README.md
├── lib
└── gson-2.1-appsonfire.jar
├── proguard.cfg
├── project.properties
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-ldpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
└── values
│ └── strings.xml
└── src
└── com
└── appsonfire
└── p2p
├── Ack.java
├── ClientConnectionCallback.java
├── Message.java
├── NetworkListener.java
├── NetworkService.java
├── NetworkServiceCallback.java
├── SeqIdGenerator.java
├── ServerReadyCallback.java
├── bluetooth
├── BluetoothClient.java
├── BluetoothServer.java
├── BluetoothService.java
├── BluetoothSocketHandler.java
├── P2PBluetooth.java
└── wifi
│ ├── P2PWifi.java
│ ├── WifiClient.java
│ ├── WifiDiscoveryClient.java
│ ├── WifiDiscoveryClientListener.java
│ ├── WifiDiscoveryServer.java
│ ├── WifiDiscoveryServerListener.java
│ ├── WifiServer.java
│ └── WifiService.java
└── util
└── Logger.java
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | #P2P4Android
2 | ## Peer to peer messaging framework for Android.
3 |
4 | ```java
5 | P2PBluetooth p2p = new P2PBluetooth();
6 | p2p.startBluetoothServer(this, new NetworkServiceCallback() {
7 |
8 | @Override
9 | public void onSuccess(NetworkService networkService) {
10 | getOkeyApplication().setNetworkService(networkService);
11 | networkService.registerNetworkListener(ActivityServer.this);
12 | runOnUiThread(new Runnable() {
13 | @Override
14 | public void run() {
15 | progressDialog.dismiss();
16 | makeToast(ActivityServer.this, getString(R.string.bt_server_ready));
17 | Button button = (Button) findViewById(R.id.start_bt_game_button);
18 | button.setEnabled(true);
19 | }
20 | });
21 | }
22 |
23 | @Override
24 | public void onFailure(int reason) {
25 | runOnUiThread(new Runnable() {
26 | @Override
27 | public void run() {
28 | progressDialog.dismiss();
29 | makeToast(ActivityServer.this, getString(R.string.bt_failed_to_start_server));
30 | //ActivityDashboard'a geri don.
31 | finish();
32 | }
33 | });
34 | }
35 | });
36 | ```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #P2P4Android
2 | ## Peer to peer messaging framework for Android.
3 |
4 | * This is an android library project.
5 | * Sends and receives text messages.
6 | * Messages are decoded/encoded with json.
7 | * [Okey Mini](https://play.google.com/store/apps/details?id=com.appsonfire.okey) multiplayer bluetooth game uses this library.
8 |
9 | Bluetooth server start example:
10 | ```java
11 | P2PBluetooth p2p = new P2PBluetooth();
12 | p2p.startBluetoothServer(this, new NetworkServiceCallback() {
13 |
14 | @Override
15 | public void onSuccess(NetworkService networkService) {
16 | //Register a network listener for receiving messages from peers.
17 | networkService.registerNetworkListener(this);
18 |
19 | //You can send messages to peers through networkService.
20 | }
21 |
22 | @Override
23 | public void onFailure(int reason) {
24 | /*
25 | * For a list of failure reasons see
26 | * https://github.com/ilkinulas/P2P4Android/blob/master/src/com/appsonfire/p2p/NetworkServiceCallback.java
27 | */
28 | }
29 | });
30 | ```
31 |
32 | Bluetooth client start example:
33 | ```java
34 | P2PBluetooth p2p = new P2PBluetooth();
35 | p2p.startBluetoothClient(this, new NetworkServiceCallback() {
36 |
37 | @Override
38 | public void onSuccess(NetworkService networkService) {
39 | //Register a network listener for receiving messages from peers.
40 | networkService.registerNetworkListener(this);
41 |
42 | //You can send messages to peers through networkService.
43 | }
44 |
45 | @Override
46 | public void onFailure(int reason) {
47 | /*
48 | * For a list of failure reasons see
49 | * https://github.com/ilkinulas/P2P4Android/blob/master/src/com/appsonfire/p2p/NetworkServiceCallback.java
50 | */
51 | }
52 | });
53 | ```
54 |
55 | * Create your own messages by extending com.appsonfire.p2p.Message.
56 | * Messages are converted to JSON string before being sent to peers.
57 | * Received text messages (in JSON format) are converted to Message instances and delivered to application via NetworkListener interface.
58 |
59 | ```java
60 | package test;
61 |
62 | import com.appsonfire.p2p.Message;
63 |
64 | public class ConnectMessage extends Message {
65 | private String data;
66 |
67 | public ConnectMessage() {
68 | this.type = MessageType.CONNECT;
69 | }
70 |
71 | public void setData(String data) {
72 | this.data = data;
73 | }
74 |
75 | public String getData() {
76 | return this.data;
77 | }
78 | }
79 | ```
80 |
81 | You should implement NetworkListener and register it to a NetworkService.
82 |
83 | ```java
84 | package com.appsonfire.p2p;
85 | public interface NetworkListener {
86 |
87 | public void messageReceived(Message message);
88 |
89 | public void connectionLost(String endPoint);
90 |
91 | }
92 | ```
93 |
94 | P2P4Android only supports Bluetooth. WIFI support is in progress.
--------------------------------------------------------------------------------
/lib/gson-2.1-appsonfire.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilkinulas/P2P4Android/ea969d6b2ca19f2e8e40f8d752a1691deda8b159/lib/gson-2.1-appsonfire.jar
--------------------------------------------------------------------------------
/proguard.cfg:
--------------------------------------------------------------------------------
1 | -optimizationpasses 5
2 | -dontusemixedcaseclassnames
3 | -dontskipnonpubliclibraryclasses
4 | -dontpreverify
5 | -verbose
6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
7 |
8 | -keep public class * extends android.app.Activity
9 | -keep public class * extends android.app.Application
10 | -keep public class * extends android.app.Service
11 | -keep public class * extends android.content.BroadcastReceiver
12 | -keep public class * extends android.content.ContentProvider
13 | -keep public class * extends android.app.backup.BackupAgentHelper
14 | -keep public class * extends android.preference.Preference
15 | -keep public class com.android.vending.licensing.ILicensingService
16 |
17 | -keepclasseswithmembernames class * {
18 | native ;
19 | }
20 |
21 | -keepclasseswithmembers class * {
22 | public (android.content.Context, android.util.AttributeSet);
23 | }
24 |
25 | -keepclasseswithmembers class * {
26 | public (android.content.Context, android.util.AttributeSet, int);
27 | }
28 |
29 | -keepclassmembers class * extends android.app.Activity {
30 | public void *(android.view.View);
31 | }
32 |
33 | -keepclassmembers enum * {
34 | public static **[] values();
35 | public static ** valueOf(java.lang.String);
36 | }
37 |
38 | -keep class * implements android.os.Parcelable {
39 | public static final android.os.Parcelable$Creator *;
40 | }
41 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-10
12 | android.library=true
13 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilkinulas/P2P4Android/ea969d6b2ca19f2e8e40f8d752a1691deda8b159/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilkinulas/P2P4Android/ea969d6b2ca19f2e8e40f8d752a1691deda8b159/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilkinulas/P2P4Android/ea969d6b2ca19f2e8e40f8d752a1691deda8b159/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | P2P4Android
5 | Please wait
6 | Starting Server
7 | Bluetooth enabled.
8 | Searching bluetooth server...
9 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/Ack.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p;
2 |
3 |
4 | public class Ack extends Message {
5 |
6 | public Ack(Message originalMessage) {
7 | this.ack = true;
8 | this.sequenceId = originalMessage.getSequenceId();
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/ClientConnectionCallback.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p;
2 |
3 | public interface ClientConnectionCallback {
4 | public void connectionEstablished();
5 | public void connectionFailed();
6 | }
7 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/Message.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p;
2 |
3 | import java.io.IOException;
4 |
5 | import com.google.appsonfire.gson.Gson;
6 | import com.google.appsonfire.gson.GsonBuilder;
7 | import com.google.appsonfire.gson.JsonElement;
8 | import com.google.appsonfire.gson.TypeAdapter;
9 | import com.google.appsonfire.gson.internal.Streams;
10 | import com.google.appsonfire.gson.stream.JsonReader;
11 | import com.google.appsonfire.gson.stream.JsonWriter;
12 |
13 | public abstract class Message {
14 | protected String klass = getClass().getCanonicalName();
15 | protected int type;
16 | protected long sequenceId;
17 | protected int playerIndex = -1;
18 | protected String playerName;
19 | protected boolean ack;
20 | protected String sourceAddress;
21 |
22 | protected boolean updateTurn;
23 |
24 | private static Gson gson = null;
25 |
26 | static {
27 | GsonBuilder gsonBuilder = new GsonBuilder();
28 | gsonBuilder.registerTypeAdapter(Message.class, new MessageTypeAdapter().nullSafe());
29 | gson = gsonBuilder.create();
30 | }
31 |
32 | public static Message decode(String s) {
33 | return gson.fromJson(s, Message.class);
34 |
35 | }
36 |
37 | public String encode() {
38 | return gson.toJson(this);
39 | }
40 |
41 | public boolean shouldUpdateTurn() {
42 | return this.updateTurn;
43 | }
44 |
45 | public long getSequenceId() {
46 | return sequenceId;
47 | }
48 |
49 | public void setSequenceId(long sequenceId) {
50 | this.sequenceId = sequenceId;
51 | }
52 |
53 | public boolean isAck() {
54 | return ack;
55 | }
56 |
57 | public void setAck(boolean ack) {
58 | this.ack = ack;
59 | }
60 |
61 | public String getKlass() {
62 | return klass;
63 | }
64 |
65 | public int getPlayerIndex() {
66 | return playerIndex;
67 | }
68 |
69 | public String getPlayerName() {
70 | return playerName;
71 | }
72 |
73 | public void setPlayerIndex(int playerIndex) {
74 | this.playerIndex = playerIndex;
75 | }
76 |
77 | public void setPlayerName(String playerName) {
78 | this.playerName = playerName;
79 | }
80 |
81 | public String getSourceAddress() {
82 | return sourceAddress;
83 | }
84 |
85 | public void setSourceAddress(String sourceAddress) {
86 | this.sourceAddress = sourceAddress;
87 | }
88 |
89 | public int getType() {
90 | return type;
91 | }
92 |
93 | public void setType(int type) {
94 | this.type = type;
95 | }
96 |
97 | public static class MessageTypeAdapter extends TypeAdapter {
98 | private Gson gson = new Gson();
99 |
100 | @Override
101 | public Message read(JsonReader reader) throws IOException {
102 | JsonElement jsonElement = Streams.parse(reader);
103 | String type = jsonElement.getAsJsonObject().get("klass").getAsString();
104 | // Logger.d("Message type is " + type);
105 | try {
106 | String jsonString = jsonElement.getAsJsonObject().toString();
107 | // Logger.d("Json String for message is " + jsonString);
108 | return (Message) gson.fromJson(jsonString, Class.forName(type));
109 | } catch (Exception e) {
110 | e.printStackTrace();
111 | }
112 | return null;
113 | }
114 |
115 | @Override
116 | public void write(JsonWriter writer, Message message) throws IOException {
117 | writer.value(gson.toJson(message));
118 | }
119 |
120 | }
121 |
122 | @Override
123 | public String toString() {
124 | StringBuilder builder = new StringBuilder();
125 | builder.append("Message [klass=");
126 | builder.append(klass);
127 | builder.append(", type=");
128 | builder.append(type);
129 | builder.append(", sequenceId=");
130 | builder.append(sequenceId);
131 | builder.append(", playerIndex=");
132 | builder.append(playerIndex);
133 | builder.append(", playerName=");
134 | builder.append(playerName);
135 | builder.append(", ack=");
136 | builder.append(ack);
137 | builder.append(", sourceAddress=");
138 | builder.append(sourceAddress);
139 | builder.append("]");
140 | return builder.toString();
141 | }
142 |
143 |
144 |
145 |
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/NetworkListener.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p;
2 |
3 | public interface NetworkListener {
4 |
5 | public void messageReceived(Message message);
6 |
7 | public void connectionLost(String endPoint);
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/NetworkService.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p;
2 |
3 |
4 | //TODO complete javadoc documentation..
5 | public interface NetworkService {
6 | public void registerNetworkListener(NetworkListener listener);
7 |
8 | public void startServer(ServerReadyCallback callback);
9 |
10 | public void startClient(String serverAddress, ClientConnectionCallback callback);
11 |
12 | public boolean isServer();
13 |
14 | public void sendMessageToServer(Message message);
15 |
16 | public void sendMessageToServer(Message message, long timeout) throws InterruptedException;
17 |
18 | public void sendMessageToClient(String destination, Message message);
19 |
20 | public void sendMessageToClient(String destination, Message message, long timeout) throws InterruptedException;
21 |
22 | public void sendMessageToClients(Message message);
23 |
24 | public void sendMessageToClients(Message message, String exclude);
25 |
26 | public void sendMessageToClients(Message message, long timeout) throws InterruptedException;
27 |
28 | public void sendMessageToClients(Message message, long timeout, String exclude) throws InterruptedException;
29 |
30 | public void sendAck(Message message);
31 |
32 | public void terminate();
33 |
34 | public String getMyNetworkAddress();
35 | }
36 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/NetworkServiceCallback.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p;
2 |
3 | public interface NetworkServiceCallback {
4 |
5 | public static final int REASON_BLUETOOTH_IS_NOT_AVAILABLE = 1;
6 |
7 | public static final int REASON_BLUETOOTH_DISCOVERY_STARTED = 2;
8 |
9 | public static final int REASON_BLUETOOTH_DISCOVERY_FINISHED = 3;
10 |
11 | public static final int REASON_BLUETOOTH_IS_NOT_ENABLED = 4;
12 |
13 | public static final int REASON_BLUETOOTH_IS_ENABLED = 5;
14 |
15 | public static final int REASON_BLUETOOTH_DISCOVERABLE_MODE_IS_ON = 6;
16 |
17 | public static final int REASON_BLUETOOTH_NOT_DISCOVERABLE = 7;
18 |
19 | public static final int REASON_BLUETOOTH_CAN_NOT_CREATE_SERVER_SOCKET = 8;
20 |
21 | public static final int REASON_BLUETOOTH_CAN_NOT_CONNECT_TO_SERVER = 9;
22 |
23 | public static final int REASON_BLUETOOTH_CAN_NOT_FIND_SERVER = 10;
24 |
25 | public static final int REASON_CANCELED_BY_USER = 11;
26 |
27 | public void onSuccess(NetworkService networkService);
28 |
29 | public void onFailure(int reason);
30 | }
31 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/SeqIdGenerator.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p;
2 |
3 | import java.util.concurrent.atomic.AtomicLong;
4 |
5 | public class SeqIdGenerator {
6 | private static final AtomicLong seqId = new AtomicLong(1);
7 |
8 | public static final long nextSeqId() {
9 | return seqId.getAndIncrement();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/ServerReadyCallback.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p;
2 |
3 | public interface ServerReadyCallback {
4 | public void acceptingConnections();
5 | public void initFailure();
6 | }
7 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/BluetoothClient.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth;
2 |
3 | import java.io.IOException;
4 | import java.util.UUID;
5 |
6 | import android.bluetooth.BluetoothAdapter;
7 | import android.bluetooth.BluetoothDevice;
8 | import android.bluetooth.BluetoothSocket;
9 |
10 | import com.appsonfire.p2p.ClientConnectionCallback;
11 | import com.appsonfire.p2p.Message;
12 | import com.appsonfire.p2p.NetworkListener;
13 | import com.appsonfire.p2p.SeqIdGenerator;
14 | import com.appsonfire.p2p.util.Logger;
15 |
16 | public class BluetoothClient {
17 |
18 | private BluetoothAdapter bluetoothAdapter;
19 | private final String serverAddress;
20 | private NetworkListener networkListener;
21 | private BluetoothSocketHandler socketHandler;
22 | private final ClientConnectionCallback connectionEstablishedCallback;
23 |
24 | public BluetoothClient(String serverAddress, ClientConnectionCallback connectionEstablishedCallback) {
25 | this.serverAddress = serverAddress;
26 | this.connectionEstablishedCallback = connectionEstablishedCallback;
27 | this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
28 | }
29 |
30 | public void registerNetworkListener(NetworkListener listener) {
31 | this.networkListener = listener;
32 | if (this.socketHandler != null) {
33 | this.socketHandler.setNetworkListener(this.networkListener);
34 | }
35 | Logger.d(listener + " registered as NetworkListener (client)");
36 | }
37 |
38 | public void start() {
39 | BluetoothSocket socket = connect();
40 | if (socket == null) {
41 | if (connectionEstablishedCallback != null) {
42 | connectionEstablishedCallback.connectionFailed();
43 | }
44 | return;
45 | }
46 |
47 | this.socketHandler = new BluetoothSocketHandler(socket);
48 | this.socketHandler.setNetworkListener(this.networkListener);
49 | this.socketHandler.start();
50 | this.connectionEstablishedCallback.connectionEstablished();
51 | }
52 |
53 | public void sendMessage(Message message) {
54 | if ( ! message.isAck()) {
55 | message.setSequenceId(SeqIdGenerator.nextSeqId());
56 | }
57 | Logger.d("Sending message " + message + " to server");
58 | this.socketHandler.sendMessage(message);
59 | }
60 |
61 | public void sendMessage(Message message, long timeout) throws InterruptedException {
62 | if ( ! message.isAck()) {
63 | message.setSequenceId(SeqIdGenerator.nextSeqId());
64 | }
65 | Logger.d("Sending message " + message + " to server");
66 | this.socketHandler.sendMessage(message, timeout);
67 | }
68 |
69 | private BluetoothSocket connect() {
70 | BluetoothDevice remoteDevice = bluetoothAdapter.getRemoteDevice(serverAddress);
71 | for (int i = 0; i < BluetoothService.UUIDs.length; i++) {
72 | BluetoothSocket bluetoothSocket = null;
73 | UUID uuid = BluetoothService.UUIDs[i];
74 | try {
75 | bluetoothSocket = remoteDevice.createRfcommSocketToServiceRecord(uuid);
76 | } catch (IOException e) {
77 | Logger.w("IOException caught while executing createRfcommSocketToServiceRecord");
78 | doSleep(3000L);
79 | continue;
80 | }
81 | try {
82 | bluetoothSocket.connect();
83 | } catch (IOException e) {
84 | Logger.w("IOException caught while connecting bluetooth socket. UUID = " + uuid , e);
85 | doSleep(500L);
86 | continue;
87 | }
88 | return bluetoothSocket;
89 | }
90 | Logger.w("Can not connect to server@" + this.serverAddress);
91 | return null;
92 | }
93 |
94 | private static void doSleep(long duration) {
95 | try {
96 | Thread.sleep(duration);
97 | } catch (InterruptedException interruptedException) {
98 | Logger.e("Sleep interrupted. " , interruptedException);
99 | }
100 | }
101 |
102 | public void terminate() {
103 | Logger.i("Terminating BluetoothClient.");
104 | if (this.socketHandler != null ) {
105 | this.socketHandler.terminate();
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/BluetoothServer.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth;
2 |
3 | import java.io.IOException;
4 | import java.util.Collection;
5 | import java.util.Map;
6 | import java.util.Map.Entry;
7 | import java.util.Set;
8 | import java.util.UUID;
9 | import java.util.concurrent.ConcurrentHashMap;
10 |
11 | import android.bluetooth.BluetoothAdapter;
12 | import android.bluetooth.BluetoothServerSocket;
13 | import android.bluetooth.BluetoothSocket;
14 |
15 | import com.appsonfire.p2p.Message;
16 | import com.appsonfire.p2p.NetworkListener;
17 | import com.appsonfire.p2p.SeqIdGenerator;
18 | import com.appsonfire.p2p.ServerReadyCallback;
19 | import com.appsonfire.p2p.util.Logger;
20 |
21 | public class BluetoothServer extends Thread {
22 |
23 | private boolean running = true;
24 | private BluetoothAdapter bluetoothAdapter;
25 | private NetworkListener networkListener;
26 | private Map clientSocketHandlers = new ConcurrentHashMap();
27 | private ServerReadyCallback serverReadyCallback;
28 | private BluetoothServerSocket serverSocket;
29 |
30 | public BluetoothServer(ServerReadyCallback callback) {
31 | this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
32 | this.serverReadyCallback = callback;
33 | Logger.d("New Bluetooth Server instance created.");
34 | }
35 |
36 | public void registerNetworkListener(NetworkListener listener) {
37 | this.networkListener = listener;
38 | Collection handlers = this.clientSocketHandlers.values();
39 | for (BluetoothSocketHandler handler : handlers) {
40 | handler.setNetworkListener(this.networkListener);
41 | }
42 | Logger.d(listener + " registered as NetworkListener (server)");
43 | }
44 |
45 |
46 | @Override
47 | public void run() {
48 | boolean notifyAcceptingConnections = true;
49 | try {
50 | Logger.i("Bluetooth Server started.");
51 | for (UUID uuid : BluetoothService.UUIDs) {
52 | if (! running) {
53 | break;
54 | }
55 | serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(BluetoothService.BLUETOOTH_SERVICE_NAME, uuid);
56 | if (notifyAcceptingConnections) {
57 | //before accepting first client connection notify serverReadyCallback.
58 | serverReadyCallback.acceptingConnections();
59 | notifyAcceptingConnections = false;
60 | }
61 | Logger.d("Server socket created UUID " + uuid);
62 | BluetoothSocket bluetoothSocket = serverSocket.accept();
63 | serverSocket.close();
64 |
65 | BluetoothSocketHandler socketHandler = new BluetoothSocketHandler(bluetoothSocket);
66 |
67 | String destination = bluetoothSocket.getRemoteDevice().getAddress();
68 | this.clientSocketHandlers.put(destination, socketHandler);
69 | Logger.d("Client address " + destination + " saved.");
70 | if (this.networkListener != null) {
71 | socketHandler.setNetworkListener(this.networkListener);
72 | }
73 |
74 | socketHandler.start();
75 | }
76 | Logger.w("MAX number of clients reached. MAX = " + BluetoothService.UUIDs.length);
77 | } catch (Exception e) {
78 | Logger.e("BluetoothServer failed to listen for incomming connections.", e);
79 | serverReadyCallback.initFailure();
80 | }
81 | Logger.i("BluetoothServer exiting...");
82 | }
83 |
84 |
85 | /**
86 | * Waits for an acknowledgement. If ack is not received until timeout miliseconds calling thread is interrupted.
87 | * @param destination
88 | * @param message
89 | * @param timeout in miliseconds
90 | */
91 | public void sendMessage(String destination, Message message, long timeout) throws InterruptedException {
92 | BluetoothSocketHandler clientHandler = this.clientSocketHandlers.get(destination);
93 | if (! message.isAck()) {
94 | message.setSequenceId(SeqIdGenerator.nextSeqId());
95 | }
96 | if (clientHandler == null) {
97 | Logger.w("Client socket handler for destination " + destination + " is null");
98 | return;
99 | }
100 | Logger.d("Sending message " + message + " to " + destination);
101 | clientHandler.sendMessage(message, timeout);
102 | }
103 |
104 | public void sendMessage(String destination, Message message) {
105 | BluetoothSocketHandler clientHandler = this.clientSocketHandlers.get(destination);
106 | if (! message.isAck()) {
107 | message.setSequenceId(SeqIdGenerator.nextSeqId());
108 | }
109 | if (clientHandler == null) {
110 | Logger.w("Client socket handler for destination " + destination + " is null");
111 | return;
112 | }
113 | Logger.d("Sending message " + message + " to " + destination);
114 | clientHandler.sendMessage(message);
115 | }
116 |
117 | public void sendMessage(Message message) {
118 | sendMessage(message, null);
119 | }
120 |
121 | public void sendMessage(Message message, String exclude) {
122 | Set> entrySet = this.clientSocketHandlers.entrySet();
123 | for (Entry entry: entrySet) {
124 | String client = entry.getKey();
125 | if ( ! client.equals(exclude)) {
126 | sendMessage(client, message);
127 | }
128 | }
129 | }
130 |
131 | public void sendMessage(Message message, long timeout) throws InterruptedException{
132 | sendMessage(message, timeout, null);
133 | }
134 |
135 | public void sendMessage(Message message, long timeout, String exclude) throws InterruptedException {
136 | Set> entrySet = this.clientSocketHandlers.entrySet();
137 | for (Entry entry: entrySet) {
138 | String client = entry.getKey();
139 | if (client.equals(exclude)) {
140 | continue;
141 | }
142 | sendMessage(client, message, timeout);
143 | }
144 | }
145 |
146 | public void terminate() {
147 | Logger.i("Terminating BluetoothServer.");
148 | this.running = false;
149 | Set> entrySet = this.clientSocketHandlers.entrySet();
150 | for (Entry entry : entrySet) {
151 | entry.getValue().terminate();
152 | }
153 | this.clientSocketHandlers.clear();
154 | if (this.serverSocket != null) {
155 | try {
156 | this.serverSocket.close();
157 | } catch (IOException e) {
158 | Logger.e("Exception caught while closing server socket", e);
159 | }
160 | }
161 | }
162 |
163 | }
164 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/BluetoothService.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth;
2 |
3 | import java.util.UUID;
4 |
5 | import android.bluetooth.BluetoothAdapter;
6 |
7 | import com.appsonfire.p2p.Ack;
8 | import com.appsonfire.p2p.ClientConnectionCallback;
9 | import com.appsonfire.p2p.Message;
10 | import com.appsonfire.p2p.NetworkListener;
11 | import com.appsonfire.p2p.NetworkService;
12 | import com.appsonfire.p2p.ServerReadyCallback;
13 | import com.appsonfire.p2p.util.Logger;
14 |
15 | public class BluetoothService implements NetworkService {
16 |
17 | public static final int SERVICE_TYPE_CLIENT = 1;
18 | public static final int SERVICE_TYPE_SERVER = 2;
19 | public static final String BLUETOOTH_SERVICE_NAME = "com.appsonfire.multiplayergamefw.bluetoothservice";
20 |
21 | private int type;
22 |
23 | public static final UUID[] UUIDs = {
24 | UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c661"),
25 | UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c662"),
26 | UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c663"),
27 | UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c664"),
28 | UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c665"),
29 | UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c666"),
30 | UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c667"), };
31 |
32 | private BluetoothClient bluetoothClient = null;
33 | private BluetoothServer bluetoothServer = null;
34 | private boolean bluetoothServerStarted;
35 |
36 | public boolean isBluetoothServerStarted() {
37 | return this.bluetoothServerStarted;
38 | }
39 |
40 | @Override
41 | public void registerNetworkListener(NetworkListener listener) {
42 | if (isServer()) {
43 | if (this.bluetoothServer != null) {
44 | this.bluetoothServer.registerNetworkListener(listener);
45 | }
46 | } else {
47 | if (this.bluetoothClient != null) {
48 | this.bluetoothClient.registerNetworkListener(listener);
49 | }
50 | }
51 | }
52 |
53 | @Override
54 | public void sendMessageToServer(Message message) {
55 | bluetoothClient.sendMessage(message);
56 | }
57 |
58 | @Override
59 | public void sendMessageToServer(Message message, long timeout) throws InterruptedException {
60 | bluetoothClient.sendMessage(message, timeout);
61 | }
62 |
63 | @Override
64 | public void sendMessageToClient(String destination, Message message) {
65 | bluetoothServer.sendMessage(destination, message);
66 | }
67 |
68 | @Override
69 | public void sendMessageToClient(String destination, Message message, long timeout) throws InterruptedException {
70 | bluetoothServer.sendMessage(destination, message, timeout);
71 | }
72 |
73 |
74 | @Override
75 | public void sendMessageToClients(Message message) {
76 | bluetoothServer.sendMessage(message);
77 | }
78 |
79 | @Override
80 | public void sendMessageToClients(Message message, String exclude) {
81 | bluetoothServer.sendMessage(message, exclude);
82 | }
83 |
84 | @Override
85 | public void sendMessageToClients(Message message, long timeout) throws InterruptedException {
86 | bluetoothServer.sendMessage(message, timeout);
87 | }
88 |
89 | @Override
90 | public void sendMessageToClients(Message message, long timeout, String exclude) throws InterruptedException {
91 | bluetoothServer.sendMessage(message, timeout, exclude);
92 | }
93 |
94 | @Override
95 | public void startServer(ServerReadyCallback callback) {
96 | Logger.d("Starting bluetooth server service.");
97 | this.type = SERVICE_TYPE_SERVER;
98 | if (this.bluetoothServer != null) {
99 | this.bluetoothServer.terminate();
100 | this.bluetoothServer.interrupt();
101 | this.bluetoothServer = null;
102 | } else {
103 | Logger.d("Bluetooth server is null. Creating a new one.");
104 | }
105 | this.bluetoothServer = new BluetoothServer(callback);
106 | this.bluetoothServer.start();
107 | this.bluetoothServerStarted = true;
108 | }
109 |
110 | @Override
111 | public void startClient(String serverAddress, ClientConnectionCallback callback) {
112 | Logger.d("Starting bluetooth client service.");
113 | this.type = SERVICE_TYPE_CLIENT;
114 | if (this.bluetoothClient != null ) {
115 | this.bluetoothClient.terminate();
116 | }
117 | this.bluetoothClient = new BluetoothClient(serverAddress, callback);
118 | this.bluetoothClient.start();
119 | }
120 |
121 | @Override
122 | public boolean isServer() {
123 | return this.type == SERVICE_TYPE_SERVER;
124 | }
125 |
126 | @Override
127 | public void sendAck(Message message) {
128 | Logger.d("Sending ACK for " + message);
129 | Ack ackMessage = new Ack(message);
130 | if (isServer()) {
131 | bluetoothServer.sendMessage(message.getSourceAddress(), ackMessage);
132 | } else {
133 | bluetoothClient.sendMessage(ackMessage);
134 | }
135 | }
136 |
137 | @Override
138 | public void terminate() {
139 | if (this.bluetoothClient != null) {
140 | this.bluetoothClient.terminate();
141 | this.bluetoothClient = null;
142 | }
143 | if (this.bluetoothServer != null) {
144 | this.bluetoothServer.terminate();
145 | this.bluetoothServer = null;
146 | }
147 | this.bluetoothServerStarted = false;
148 | }
149 |
150 | @Override
151 | public String getMyNetworkAddress() {
152 | return BluetoothAdapter.getDefaultAdapter().getAddress();
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/BluetoothSocketHandler.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.InputStreamReader;
7 | import java.io.OutputStream;
8 | import java.io.PrintWriter;
9 | import java.util.concurrent.TimeUnit;
10 | import java.util.concurrent.locks.Condition;
11 | import java.util.concurrent.locks.Lock;
12 | import java.util.concurrent.locks.ReentrantLock;
13 |
14 | import android.bluetooth.BluetoothAdapter;
15 | import android.bluetooth.BluetoothSocket;
16 | import android.util.Log;
17 |
18 | import com.appsonfire.p2p.Message;
19 | import com.appsonfire.p2p.NetworkListener;
20 | import com.appsonfire.p2p.util.Logger;
21 |
22 | public class BluetoothSocketHandler extends Thread {
23 |
24 |
25 | private final BluetoothSocket socket;
26 | private final String destinationAddress;
27 | private boolean running = true;
28 | private Message ackWaitingMessage = null;
29 | private Lock ackLock = new ReentrantLock(true);
30 | private Condition ackReceivedCondition = ackLock.newCondition();
31 | private NetworkListener networkListener;
32 | private BluetoothAdapter bluetoothAdapter;
33 |
34 | public BluetoothSocketHandler(BluetoothSocket socket) {
35 | super();
36 | this.socket = socket;
37 | this.destinationAddress = socket.getRemoteDevice().getAddress();
38 | this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
39 | setName("BluetoothSocketHandler_"+socket.getRemoteDevice().getAddress());
40 | }
41 |
42 |
43 | public void setNetworkListener(NetworkListener networkListener) {
44 | this.networkListener = networkListener;
45 | }
46 |
47 | @Override
48 | public void run() {
49 | super.run();
50 | Logger.d("BluetoothSocketHandler is running for client " + this.destinationAddress);
51 | try {
52 | InputStream inputStream = socket.getInputStream();
53 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
54 |
55 | while (running) {
56 | String message = reader.readLine();
57 | Message decodedMessage = Message.decode(message);
58 | if (decodedMessage.isAck()) {
59 | notifyAckWaitingThreads(decodedMessage);
60 | } else {
61 | if (networkListener != null) {
62 | networkListener.messageReceived(decodedMessage);
63 | }
64 | }
65 | }
66 | Logger.i("BluetoothSocketHandler exiting...");
67 | } catch (IOException e) {
68 | Logger.e("Exception caught while listening client messages. " + this.destinationAddress, e);
69 | if (networkListener != null) {
70 | networkListener.connectionLost(this.destinationAddress);
71 | }
72 | }
73 | //TODO remove this socket handler from client socket handler list
74 | //clientSocketHandlers.remove(this.destinationAddress);
75 | }
76 |
77 | private void notifyAckWaitingThreads(Message ack) {
78 | if (this.ackWaitingMessage == null) {
79 | return;
80 | }
81 | if (ack.getSequenceId() == this.ackWaitingMessage.getSequenceId()) {
82 | this.ackWaitingMessage = null;
83 | try {
84 | ackLock.lock();
85 | ackReceivedCondition.signalAll();
86 | } finally {
87 | ackLock.unlock();
88 | }
89 | }
90 |
91 | }
92 |
93 | public void sendMessage(Message message, long timeout) throws InterruptedException {
94 | this.sendMessage(message);
95 | this.waitForAcknowledgement(message, timeout);
96 | }
97 |
98 | public void sendMessage(Message message) {
99 | OutputStream outStream;
100 | try {
101 | outStream = this.socket.getOutputStream();
102 | PrintWriter writer = new PrintWriter(outStream);
103 | message.setSourceAddress(this.bluetoothAdapter.getAddress());
104 | writer.println(message.encode());
105 | writer.flush();
106 | } catch (IOException e) {
107 | Log.w("Failed to send message " + message + " to destination " + destinationAddress, e);
108 | }
109 | }
110 |
111 | public void terminate() {
112 | //TODO remove this socket handler from client socket handlers list
113 | //clientSocketHandlers.remove(this.destinationAddress);
114 | this.running = false;
115 | try {
116 | this.socket.close();
117 | } catch (IOException e) {
118 | Log.w("Exception caught while destroying BluetoothSocketHandler.", e);
119 | }
120 | this.interrupt();
121 | }
122 |
123 | public void waitForAcknowledgement(Message message, long timeout) throws InterruptedException {
124 | this.ackWaitingMessage = message;
125 | try {
126 | ackLock.lock();
127 | boolean ackReceived = this.ackReceivedCondition.await(timeout, TimeUnit.MILLISECONDS);
128 | if ( ! ackReceived) {
129 | throw new InterruptedException("Ack is not received for " + message + " in " + timeout + " ms.");
130 | }
131 | } finally {
132 | ackLock.unlock();
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/P2PBluetooth.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth;
2 |
3 |
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.bluetooth.BluetoothDevice;
6 | import android.content.BroadcastReceiver;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.IntentFilter;
10 | import android.os.Parcelable;
11 |
12 | import com.appsonfire.p2p.ClientConnectionCallback;
13 | import com.appsonfire.p2p.NetworkServiceCallback;
14 | import com.appsonfire.p2p.ServerReadyCallback;
15 | import com.appsonfire.p2p.util.Logger;
16 |
17 | public class P2PBluetooth {
18 |
19 | private boolean clientConnectionSuccess = false;
20 | private String savedBluetoothName;
21 | private static final String MAGIC_BT_NAME_PREFIX = "=p2p=";
22 |
23 | private BroadcastReceiver bluetoothStateBroadcastReceiver;
24 | private BroadcastReceiver bluetoothClientStateBroadcastReceiver;
25 | private BroadcastReceiver bluetoothScanmodeBroadcastReceiver;
26 |
27 | private BluetoothService bluetoothService;
28 |
29 | /**
30 | * Enables bluetooth and makes the device discoverable.
31 | * @param context android context.
32 | * @param callback Callback is used to receive async updates about server start
33 | */
34 | public void startBluetoothServer(final Context context, final NetworkServiceCallback callback) {
35 | final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
36 | if (bluetoothAdapter == null) {
37 | callback.onFailure(NetworkServiceCallback.REASON_BLUETOOTH_IS_NOT_AVAILABLE);
38 | return;
39 | }
40 |
41 | if (bluetoothAdapter.isEnabled()) {
42 | prepareServerBluetoothAdapter(context, callback, bluetoothAdapter);
43 | } else {
44 | bluetoothStateBroadcastReceiver = new BroadcastReceiver() {
45 |
46 | @Override
47 | public void onReceive(Context context, Intent intent) {
48 | int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
49 | switch (newState) {
50 | case BluetoothAdapter.STATE_TURNING_ON:
51 | Logger.i("Turning on bluetooth adapter. STATE_TURNING_ON ");
52 | break;
53 | case BluetoothAdapter.STATE_ON:
54 | Logger.i("Bluetooth adapter is enabled. STATE_ON ");
55 | doUnregisterReceiver(context, this);
56 | prepareServerBluetoothAdapter(context, callback, bluetoothAdapter);
57 | break;
58 | case BluetoothAdapter.STATE_TURNING_OFF:
59 | case BluetoothAdapter.STATE_OFF:
60 | doUnregisterReceiver(context, this);
61 | callback.onFailure(NetworkServiceCallback.REASON_BLUETOOTH_IS_NOT_ENABLED);
62 | break;
63 | default:
64 | Logger.w("Unexpected BluetoothAdapter state received. STATE : " + newState);
65 | }
66 | }
67 | };
68 | context.registerReceiver(bluetoothStateBroadcastReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
69 | bluetoothAdapter.enable();
70 | }
71 | }
72 |
73 | private void prepareServerBluetoothAdapter(Context context, final NetworkServiceCallback callback, final BluetoothAdapter bluetoothAdapter) {
74 | changeBluetoothAdapterName(bluetoothAdapter);
75 |
76 | if (bluetoothAdapter.getScanMode() == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
77 | startBluetoothServerService(callback);
78 | return;
79 | }
80 |
81 | bluetoothScanmodeBroadcastReceiver = new BroadcastReceiver() {
82 | @Override
83 | public void onReceive(Context context, Intent intent) {
84 | String action = intent.getAction();
85 | Logger.i("Scan mode broadcast receiver received action " + action);
86 | int scanMode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.ERROR);
87 | switch (scanMode) {
88 | case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
89 | Logger.i("Bluetooth scan mode is CONNECTABLE & DISCOVERABLE");
90 | doUnregisterReceiver(context, this);
91 | startBluetoothServerService(callback);
92 | break;
93 | default:
94 | Logger.w("Unexpected scan mode received : " + scanMode);
95 | doUnregisterReceiver(context, this);
96 | callback.onFailure(NetworkServiceCallback.REASON_BLUETOOTH_NOT_DISCOVERABLE);
97 | break;
98 | }
99 | }
100 | };
101 |
102 | context.registerReceiver(bluetoothScanmodeBroadcastReceiver, new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED));
103 | Intent i = new Intent();
104 | i.setAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
105 | i.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 600);
106 | context.startActivity(i);
107 |
108 | cancelDiscoverableActivityWorkaround(context, callback);
109 | }
110 |
111 | /**
112 | * When the activity started by ACTION_REQUEST_DISCOVERABLE is canceled we can not receive an activity result.
113 | * (Activity is not started with startActivityForResult)
114 | * This method spawns a thread that checks every second if the bluetooth service is started. If the service is not started
115 | * for 10 seconds then we assume that user has canceled activity.
116 | * @param context
117 | * @param callback
118 | */
119 | private void cancelDiscoverableActivityWorkaround(final Context context, final NetworkServiceCallback callback) {
120 | new Thread(new Runnable() {
121 | @Override
122 | public void run() {
123 | for (int i = 0; i < 10; i++) {
124 | if (bluetoothService != null && bluetoothService.isBluetoothServerStarted()) {
125 | Logger.d("cancelDiscoverableActivityWorkaround : Bluetooth service started.");
126 | return;
127 | }
128 | try {
129 | Thread.sleep(1000);
130 | } catch (InterruptedException e) {
131 | Logger.e("cancelDiscoverableActivityWorkaround : interrupted", e);
132 | }
133 | }
134 | Logger.d("cancelDiscoverableActivityWorkaround : BT is not discovarable");
135 | doUnregisterReceiver(context, bluetoothScanmodeBroadcastReceiver);
136 | callback.onFailure(NetworkServiceCallback.REASON_BLUETOOTH_NOT_DISCOVERABLE);
137 | }
138 | }).start();
139 | }
140 |
141 | private void startBluetoothServerService(final NetworkServiceCallback callback) {
142 | if (this.bluetoothService != null) {
143 | this.bluetoothService.terminate();
144 | }
145 | this.bluetoothService = new BluetoothService();
146 | this.bluetoothService.startServer(new ServerReadyCallback() {
147 | @Override
148 | public void initFailure() {
149 | callback.onFailure(NetworkServiceCallback.REASON_BLUETOOTH_CAN_NOT_CREATE_SERVER_SOCKET);
150 | }
151 |
152 | @Override
153 | public void acceptingConnections() {
154 | callback.onSuccess(bluetoothService);
155 | }
156 | });
157 | }
158 |
159 | /**
160 | * changes bluetooth adapter name. Name is replaced with "MAGIC_PREFIX + Name"
161 | * Name is restored while disabling bluetooth adapter.
162 | * This magic prefix is used by clients to identify the bluetooth server.
163 | * @param bluetoothAdapter
164 | */
165 | private void changeBluetoothAdapterName(final BluetoothAdapter bluetoothAdapter) {
166 | Logger.d("Changing bluetooth adater name...");
167 | String btName = bluetoothAdapter.getName();
168 | if (btName == null) {
169 | Logger.w("Failed to read bluetooth adapter name. bluetoothAdapter.getName() returns null.");
170 | return;
171 | }
172 |
173 | if (btName.startsWith(MAGIC_BT_NAME_PREFIX)) {
174 | Logger.d("No need to change bluetooth adapter name. Name : " + btName);
175 | } else {
176 | savedBluetoothName = btName;
177 | bluetoothAdapter.setName(MAGIC_BT_NAME_PREFIX + btName);
178 | Logger.d("Bluetooth name has been changed. New name is " + bluetoothAdapter.getName());
179 | }
180 | }
181 |
182 | public void revertBluetoothAdapterName() {
183 | BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
184 | if (adapter == null) {
185 | return;
186 | }
187 |
188 | if (this.savedBluetoothName == null) {
189 | String name = adapter.getName();
190 | if (name != null && name.startsWith(MAGIC_BT_NAME_PREFIX)) {
191 | name = name.substring(MAGIC_BT_NAME_PREFIX.length(), name.length());
192 | if (adapter.setName(name)) {
193 | Logger.i("Bluetooth adapter name is changed to " + name);
194 | }
195 | }
196 | } else {
197 | if (adapter.setName(this.savedBluetoothName)) {
198 | Logger.i("Bluetooth adapter name is set to initial value : " + this.savedBluetoothName);
199 | }
200 | this.savedBluetoothName = null;
201 | }
202 | }
203 |
204 | public void startBluetoothClient(Context context, final NetworkServiceCallback callback) {
205 | final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
206 | if (bluetoothAdapter == null) {
207 | callback.onFailure(NetworkServiceCallback.REASON_BLUETOOTH_IS_NOT_AVAILABLE);
208 | return;
209 | }
210 |
211 | if (bluetoothAdapter.isEnabled()) {
212 | startBluetoothDiscovery(context, callback, bluetoothAdapter);
213 | } else {
214 | bluetoothClientStateBroadcastReceiver = new BroadcastReceiver() {
215 | @Override
216 | public void onReceive(Context context, Intent intent) {
217 | int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
218 | switch (newState) {
219 | case BluetoothAdapter.STATE_TURNING_ON:
220 | Logger.i("Turning on bluetooth adapter. STATE_TURNING_ON ");
221 | break;
222 | case BluetoothAdapter.STATE_ON:
223 | doUnregisterReceiver(context, this);
224 | startBluetoothDiscovery(context, callback, bluetoothAdapter);
225 | break;
226 | case BluetoothAdapter.STATE_TURNING_OFF:
227 | case BluetoothAdapter.STATE_OFF:
228 | doUnregisterReceiver(context, this);
229 | callback.onFailure(NetworkServiceCallback.REASON_BLUETOOTH_IS_NOT_ENABLED);
230 | break;
231 | default:
232 | Logger.w("Unexpected BluetoothAdapter state received. STATE : " + newState);
233 | }
234 | }
235 | };
236 |
237 | context.registerReceiver(bluetoothClientStateBroadcastReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
238 | bluetoothAdapter.enable();
239 | }
240 | }
241 |
242 | private void startBluetoothDiscovery(Context context, final NetworkServiceCallback callback,
243 | final BluetoothAdapter bluetoothAdapter) {
244 | IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
245 | filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
246 |
247 | context.registerReceiver(new BroadcastReceiver() {
248 |
249 | @Override
250 | public void onReceive(Context context, Intent intent) {
251 | String action = intent.getAction();
252 | if (BluetoothDevice.ACTION_FOUND.equals(action)) {
253 | Parcelable btParcel = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
254 | BluetoothDevice btDevice = (BluetoothDevice) btParcel;
255 | String btDeviceName = btDevice.getName();
256 | Logger.d("BT Device found. " + btDevice + " name : " + btDeviceName);
257 | if (btDeviceName != null && btDeviceName.startsWith(MAGIC_BT_NAME_PREFIX)) {
258 | bluetoothAdapter.cancelDiscovery();
259 | doUnregisterReceiver(context, this);
260 | String btServerAddress = btDevice.getAddress();
261 | if (bluetoothService != null) {
262 | bluetoothService.terminate();
263 | }
264 | bluetoothService = new BluetoothService();
265 | bluetoothService.startClient(btServerAddress, new ClientConnectionCallback() {
266 | @Override
267 | public void connectionEstablished() {
268 | clientConnectionSuccess = true;
269 | callback.onSuccess(bluetoothService);
270 | }
271 |
272 | @Override
273 | public void connectionFailed() {
274 | callback.onFailure(NetworkServiceCallback.REASON_BLUETOOTH_CAN_NOT_CONNECT_TO_SERVER);
275 | }
276 | });
277 | }
278 | } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
279 | Logger.d("Bluetooth device search completed. ");
280 | doUnregisterReceiver(context, this);
281 | bluetoothAdapter.cancelDiscovery();
282 | if ( ! clientConnectionSuccess) {
283 | callback.onFailure(NetworkServiceCallback.REASON_BLUETOOTH_CAN_NOT_FIND_SERVER);
284 | }
285 | }
286 | }
287 | }, filter);
288 | bluetoothAdapter.startDiscovery();
289 | }
290 |
291 | public boolean stopBluetoothService(Context context) {
292 | doUnregisterReceiver(context, bluetoothScanmodeBroadcastReceiver);
293 | doUnregisterReceiver(context, bluetoothStateBroadcastReceiver);
294 | doUnregisterReceiver(context, bluetoothClientStateBroadcastReceiver);
295 | revertBluetoothAdapterName();
296 | if (this.bluetoothService!=null) {
297 | this.bluetoothService.terminate();
298 | }
299 | disableBluetoothAdapter();
300 | return true;
301 |
302 | }
303 |
304 | public void disableBluetoothAdapter() {
305 | BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
306 | Logger.i("Disabling bluetooth adapter...");
307 | if (adapter != null ) {
308 | if (adapter.isEnabled()) {
309 | adapter.disable();
310 | }
311 | }
312 | }
313 |
314 | private void doUnregisterReceiver(Context context, BroadcastReceiver receiver) {
315 | if (context != null) {
316 | try {
317 | context.unregisterReceiver(receiver);
318 | } catch (Exception e) {
319 | Logger.w("Failed to unregister " + receiver, e);
320 | }
321 | }
322 | }
323 | }
324 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/wifi/P2PWifi.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth.wifi;
2 |
3 | import android.content.Context;
4 |
5 | import com.appsonfire.p2p.NetworkServiceCallback;
6 |
7 | public class P2PWifi {
8 |
9 | public void startWifiServer(Context context, final NetworkServiceCallback callback) {
10 |
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/wifi/WifiClient.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth.wifi;
2 |
3 | public class WifiClient {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/wifi/WifiDiscoveryClient.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth.wifi;
2 |
3 | import java.net.DatagramPacket;
4 | import java.net.DatagramSocket;
5 | import java.net.InetAddress;
6 |
7 | import android.content.Context;
8 | import android.net.DhcpInfo;
9 | import android.net.wifi.WifiManager;
10 |
11 | public class WifiDiscoveryClient {
12 |
13 | private DatagramPacket requestDatagramPacket;
14 | private byte[] responseData = new byte[1024];
15 | private DatagramPacket responseDatagramPacket = new DatagramPacket(this.responseData, this.responseData.length);
16 |
17 | public void discoverServer(Context context, WifiDiscoveryClientListener listener) {
18 | startAsyncDiscovery(context, listener);
19 | }
20 |
21 | private void startAsyncDiscovery(final Context context, final WifiDiscoveryClientListener listener) {
22 |
23 | new Thread(new Runnable() {
24 |
25 | @Override
26 | public void run() {
27 | try {
28 | DatagramSocket socket = new DatagramSocket(WifiDiscoveryServer.DISCOVERY_PORT);
29 | socket.setBroadcast(true);
30 | InetAddress broadcastAddress = InetAddress.getByAddress(getBroadcastIPAddressRaw(context));
31 |
32 | final byte[] out = WifiDiscoveryServer.REQUEST_ID;
33 | requestDatagramPacket = new DatagramPacket(out, out.length, broadcastAddress, WifiDiscoveryServer.DISCOVERY_PORT);
34 |
35 | socket.send(requestDatagramPacket);
36 |
37 | socket.receive(responseDatagramPacket);
38 | final byte[] discoveryResponseData = new byte[responseDatagramPacket.getLength()];
39 | System.arraycopy(responseDatagramPacket.getData(),responseDatagramPacket.getOffset(), discoveryResponseData, 0, responseDatagramPacket.getLength());
40 | listener.onDiscover(String.valueOf(discoveryResponseData));
41 | } catch (Exception e) {
42 | listener.onException(e);
43 | }
44 | }
45 | }).start();
46 | }
47 |
48 | public static byte[] getBroadcastIPAddressRaw(final Context context) {
49 | // TODO what if wifi manager is null...
50 | WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
51 | DhcpInfo dhcp = wifiManager.getDhcpInfo();
52 | int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
53 | final byte[] broadcastIP = new byte[4];
54 | for (int k = 0; k < 4; k++) {
55 | broadcastIP[k] = (byte) ((broadcast >> k * 8) & 0xFF);
56 | }
57 | return broadcastIP;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/wifi/WifiDiscoveryClientListener.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth.wifi;
2 |
3 |
4 | public interface WifiDiscoveryClientListener {
5 |
6 | public void onException(Exception e);
7 |
8 | public void onDiscover(String valueOf);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/wifi/WifiDiscoveryServer.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth.wifi;
2 |
3 | import java.io.IOException;
4 | import java.net.DatagramPacket;
5 | import java.net.DatagramSocket;
6 | import java.net.SocketException;
7 |
8 | import com.appsonfire.p2p.util.Logger;
9 |
10 | public class WifiDiscoveryServer extends Thread {
11 |
12 | public static final int DISCOVERY_PORT = 11211;
13 | public static final byte[] REQUEST_ID = "com.appsonfire.p2p".getBytes();
14 |
15 | private boolean running = true;
16 |
17 | private DatagramSocket datagramSocket;
18 | private final byte[] requestData = new byte[1024];
19 | private final DatagramPacket datagramPacket = new DatagramPacket(this.requestData, this.requestData.length);
20 |
21 | private final WifiDiscoveryServerListener listener;
22 |
23 | private final String ipAddress;
24 |
25 | public WifiDiscoveryServer(String ipAddress, WifiDiscoveryServerListener listener) {
26 | this.ipAddress = ipAddress;
27 | this.listener = listener;
28 | }
29 |
30 | @Override
31 | public void run() {
32 | super.run();
33 | try {
34 | datagramSocket = new DatagramSocket(DISCOVERY_PORT);
35 | } catch (SocketException e) {
36 | Logger.e("Failed to start wifi discovery server", e);
37 | if (listener != null) {
38 | listener.onException(e);
39 | }
40 | return;
41 | }
42 | while (running && !Thread.interrupted()) {
43 | try {
44 | this.datagramSocket.receive(this.datagramPacket);
45 | byte[] data = this.datagramPacket.getData();
46 | int aStart = 0;
47 | int bStart = this.datagramPacket.getOffset();
48 | boolean isDiscoveryMessage = true;
49 | for (int i = aStart, j = bStart; i < REQUEST_ID.length; i++, j++) {
50 | if (REQUEST_ID[i] != data[j]) {
51 | isDiscoveryMessage = false;
52 | break;
53 | }
54 | }
55 | if (isDiscoveryMessage) {
56 | byte[] response = this.ipAddress.getBytes();
57 | this.datagramSocket.send(new DatagramPacket(response, response.length, datagramPacket.getAddress(), datagramPacket.getPort()));
58 | listener.onDiscoveryMessageReceived();
59 | }
60 | } catch (IOException e) {
61 | if (listener != null) {
62 | Logger.e("Failed to reveice datagram packet", e);
63 | listener.onException(e);
64 | }
65 | }
66 | }
67 | }
68 |
69 | public void terminate() {
70 | running = false;
71 | if (this.datagramSocket != null) {
72 | this.datagramSocket.close();
73 | }
74 | interrupt();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/wifi/WifiDiscoveryServerListener.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth.wifi;
2 |
3 |
4 | public interface WifiDiscoveryServerListener {
5 |
6 | public void onException(Exception e);
7 |
8 | public void onDiscoveryMessageReceived();
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/wifi/WifiServer.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth.wifi;
2 |
3 | public class WifiServer extends Thread {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/bluetooth/wifi/WifiService.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.bluetooth.wifi;
2 |
3 | import com.appsonfire.p2p.ClientConnectionCallback;
4 | import com.appsonfire.p2p.Message;
5 | import com.appsonfire.p2p.NetworkListener;
6 | import com.appsonfire.p2p.NetworkService;
7 | import com.appsonfire.p2p.ServerReadyCallback;
8 |
9 | public class WifiService implements NetworkService {
10 |
11 | @Override
12 | public void registerNetworkListener(NetworkListener listener) {
13 | // TODO Auto-generated method stub
14 |
15 | }
16 |
17 | @Override
18 | public void startServer(ServerReadyCallback callback) {
19 | // TODO Auto-generated method stub
20 |
21 | }
22 |
23 | @Override
24 | public void startClient(String serverAddress, ClientConnectionCallback callback) {
25 | // TODO Auto-generated method stub
26 |
27 | }
28 |
29 | @Override
30 | public boolean isServer() {
31 | // TODO Auto-generated method stub
32 | return false;
33 | }
34 |
35 | @Override
36 | public void sendMessageToServer(Message message) {
37 | // TODO Auto-generated method stub
38 |
39 | }
40 |
41 | @Override
42 | public void sendMessageToServer(Message message, long timeout) throws InterruptedException {
43 | // TODO Auto-generated method stub
44 |
45 | }
46 |
47 | @Override
48 | public void sendMessageToClient(String destination, Message message) {
49 | // TODO Auto-generated method stub
50 |
51 | }
52 |
53 | @Override
54 | public void sendMessageToClient(String destination, Message message, long timeout) throws InterruptedException {
55 | // TODO Auto-generated method stub
56 |
57 | }
58 |
59 | @Override
60 | public void sendMessageToClients(Message message) {
61 | // TODO Auto-generated method stub
62 |
63 | }
64 |
65 | @Override
66 | public void sendMessageToClients(Message message, String exclude) {
67 | // TODO Auto-generated method stub
68 |
69 | }
70 |
71 | @Override
72 | public void sendMessageToClients(Message message, long timeout) throws InterruptedException {
73 | // TODO Auto-generated method stub
74 |
75 | }
76 |
77 | @Override
78 | public void sendMessageToClients(Message message, long timeout, String exclude) throws InterruptedException {
79 | // TODO Auto-generated method stub
80 |
81 | }
82 |
83 | @Override
84 | public void sendAck(Message message) {
85 | // TODO Auto-generated method stub
86 |
87 | }
88 |
89 | @Override
90 | public void terminate() {
91 | // TODO Auto-generated method stub
92 |
93 | }
94 |
95 | @Override
96 | public String getMyNetworkAddress() {
97 | // TODO Auto-generated method stub
98 | return null;
99 | }
100 |
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/com/appsonfire/p2p/util/Logger.java:
--------------------------------------------------------------------------------
1 | package com.appsonfire.p2p.util;
2 |
3 | import android.util.Log;
4 |
5 | public class Logger {
6 | public static final String TAG = "p2p4Android";
7 | public static int logLevel = Log.INFO;
8 |
9 |
10 | public static final void d(String s) {
11 | if (Log.DEBUG >= logLevel) {
12 | Log.d(TAG, s);
13 | }
14 | }
15 |
16 | public static final void i(String s) {
17 | if (Log.INFO >= logLevel) {
18 | Log.i(TAG, s);
19 | }
20 | }
21 |
22 | public static final void w(String s) {
23 | if (Log.WARN >= logLevel) {
24 | Log.w(TAG, s);
25 | }
26 | }
27 |
28 | public static final void w(String s, Exception e) {
29 | if (Log.WARN >= logLevel) {
30 | Log.w(TAG, s, e);
31 | }
32 | }
33 |
34 | public static final void e(String s) {
35 | if (Log.ERROR >= logLevel) {
36 | Log.e(TAG, s);
37 | }
38 | }
39 |
40 | public static final void e(String s, Exception e) {
41 | if (Log.ERROR >= logLevel) {
42 | Log.e(TAG, s, e);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------