Commit cddc180e authored by Chengzhao Li's avatar Chengzhao Li
Browse files

- add logic for NCMCConnectivity lib

parent f261a1f5
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
package com.nclab.ncmultipeerchat;
/**
* control the whole chat
*/
public class MultiplayerController {
private static MultiplayerController ourInstance = new MultiplayerController();
public static MultiplayerController getInstance() {
return ourInstance;
}
private MultiplayerController() {
}
}
package com.nclab.ncmultipeerconnectivity;
/**
* Create a ble central role
*/
public class NCMCCentralService {
public static final int NCMCCentralService_ERROR_BLUETOOTH_OFF = 0;
public static final int NCMCCentralService_ERROR_NO_ACCESS = 1;
public static final int NCMCCentralService_ERROR_UNKNOWN = 2;
protected NCMCSession session;
public NCMCCentralServiceCallback callback;
private boolean isInited;
public NCMCCentralService(NCMCSession _session) {
this.session = _session;
this.callback = null;
this.isInited = false;
}
public void setupCentralEnvironment() {
if (!this.isInited) {
this.isInited = NCMCBluetoothLEManager.getInstance().setupCentralEnv(NCMCCentralService.this);
}
}
public boolean isInited() {
return this.isInited;
}
public void startBrowsingForPeers() {
if (this.isInited) {
NCMCBluetoothLEManager.getInstance().startBrowsing();
}
}
public void stopBrowsingForPeers() {
NCMCBluetoothLEManager.getInstance().stopBrowsing();
}
public void invitePeer(NCMCPeerID peerID) {
NCMCBluetoothLEManager.getInstance().invitePeer(peerID);
}
protected void notifyFoundPeer(NCMCPeerID peerID) {
if (this.callback != null) {
this.callback.didFoundPeer(NCMCCentralService.this, peerID);
}
}
protected void notifyLostPeer(NCMCPeerID peerID) {
if (this.callback != null) {
this.callback.didLostPeer(NCMCCentralService.this, peerID);
}
}
protected void notifyBrowsingTimeout() {
if (this.callback != null) {
this.callback.didBrowsingTimeout(NCMCCentralService.this);
}
}
protected void notifyDidNotStartBrowsingForPeers(int reason) {
if (this.callback != null) {
this.callback.didNotStartBrowsingForPeers(NCMCCentralService.this, reason);
}
}
}
package com.nclab.ncmultipeerconnectivity;
/**
* This abstract class is used to implement {@link NCMCCentralService} callbacks.
*/
public abstract class NCMCCentralServiceCallback {
public void didFoundPeer(NCMCCentralService centralService, NCMCPeerID peerID) {
}
public void didLostPeer(NCMCCentralService centralService, NCMCPeerID peerID) {
}
public void didBrowsingTimeout(NCMCCentralService centralService) {
}
public void didNotStartBrowsingForPeers(NCMCCentralService centralService, int reason) {
}
}
package com.nclab.ncmultipeerconnectivity;
/**
* device information
*/
class NCMCDeviceInfo {
public String name;
public String identifier;
public char uniqueID;
}
package com.nclab.ncmultipeerconnectivity;
import java.util.ArrayList;
import java.util.List;
/**
* data to be sent or received
*/
class NCMCMessageData {
private String deviceUUID;
private List<byte[]> dataArray;
boolean isReliable;
public NCMCMessageData(String _deviceUUID, boolean _isReliable) {
this.deviceUUID = _deviceUUID;
this.dataArray = new ArrayList<>();
this.isReliable = _isReliable;
}
public String getDeviceUUID() {
return this.deviceUUID;
}
public void clearData(){
this.dataArray.clear();
}
public void addData(byte[] array){
this.dataArray.add(array);
}
public byte[] getFullData(){
byte[] retArray;
int totalSize = 0;
for(int i=0; i < this.dataArray.size();i++){
totalSize = totalSize + this.dataArray.get(i).length;
}
int copuCounter = 0;
if(totalSize > 0) {
retArray = new byte[totalSize];
for(int ii=0; ii < this.dataArray.size();ii++){
byte[] tmpArr = this.dataArray.get(ii);
System.arraycopy(tmpArr, 0, retArray,copuCounter,tmpArr.length);
copuCounter = copuCounter + tmpArr.length;
}
}else{
retArray = new byte[]{};
}
return retArray;
}
}
package com.nclab.ncmultipeerconnectivity;
import java.util.UUID;
/**
* A peerID represents a device
*/
public class NCMCPeerID {
protected String identifier;
protected String displayName;
protected NCMCPeerID(String _displayName, String _identifier) {
this.displayName = _displayName;
this.identifier = _identifier;
}
public NCMCPeerID(String _displayName) {
this.displayName = _displayName;
this.identifier = UUID.randomUUID().toString();
}
public String getDisplayName() {
return this.displayName;
}
}
package com.nclab.ncmultipeerconnectivity;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
/**
* Used by central to save connected peripheral information
*/
class NCMCPeripheralInfo {
public BluetoothGatt bluetoothGatt;
public BluetoothGattCharacteristic readCharacteristic; // message from peripheral to central
public BluetoothGattCharacteristic writeWithResponseCharacteristic; // message from central to peripheral
public BluetoothGattCharacteristic writeWithoutResponseCharacteristic;
public String name;
public int mtu;
}
package com.nclab.ncmultipeerconnectivity;
/**
* Create a ble peripheral role
*/
public class NCMCPeripheralService {
public static final int NCMCPeripheralService_ERROR_BLUETOOTH_OFF = 0;
public static final int NCMCPeripheralService_ERROR_NO_ACCESS = 1;
public static final int NCMCPeripheralService_ERROR_UNKNOWN = 2;
public static final int NCMCPeripheralService_ERROR_NOT_SUPPORT = 3;
protected NCMCSession session;
public NCMCPeripheralServiceCallback callback;
private boolean isInited;
public NCMCPeripheralService(NCMCSession _session) {
this.session = _session;
this.callback = null;
this.isInited = false;
}
public void setupPeripheralEnvironment() {
if (!this.isInited) {
this.isInited = NCMCBluetoothLEManager.getInstance().setupPeripheralEnv(NCMCPeripheralService.this);
}
}
public boolean isInited() {
return this.isInited;
}
public void startAdvertisingPeer() {
if (isInited) {
NCMCBluetoothLEManager.getInstance().startAdvertising();
}
}
public void stopAdvertisingPeer() {
NCMCBluetoothLEManager.getInstance().stopAdvertising();
}
protected void notifyDidNotStartAdvertising(int reason) {
if (this.callback != null) {
this.callback.didNotStartAdvertising(NCMCPeripheralService.this, reason);
}
}
protected void notifyDidReceiveInvitationFromPeer(NCMCPeerID peerID) {
if (this.callback != null) {
this.callback.didReceiveInvitationFromPeer(NCMCPeripheralService.this, peerID);
}
}
}
package com.nclab.ncmultipeerconnectivity;
/**
* This abstract class is used to implement {@link NCMCPeripheralService} callbacks.
*/
public abstract class NCMCPeripheralServiceCallback {
/**
* Notify received an invitation from a remote central device
* * <p>An application must call {@link NCMCSession#sendResponseToInvitation}
* to complete the request.
* @param peripheralService the peripheral service running on this device
* @param peerID the central device that sent the invitation
*/
public void didReceiveInvitationFromPeer(NCMCPeripheralService peripheralService, NCMCPeerID peerID) {
}
public void didNotStartAdvertising(NCMCPeripheralService peripheralService, int reason) {
}
}
package com.nclab.ncmultipeerconnectivity;
import java.util.ArrayList;
import java.util.List;
/**
* Android will split long data from central to peripheral, this class is used to save those chunks and combine them.
*/
class NCMCPeripheralWriteRequestData {
private String m_deviceAddress;
private String m_UUID;
private boolean m_isCharacter;
List<byte[]> byteArray;
public NCMCPeripheralWriteRequestData(String address,String uuid, boolean isCharacter){
m_deviceAddress = address;
m_UUID = uuid;
byteArray = new ArrayList<>();
m_isCharacter = isCharacter;
}
public boolean isCharacter(){return m_isCharacter;}
public String getUUID(){return m_UUID;}
public String getDeviceAddress(){return m_deviceAddress;}
public void clearData(){
byteArray.clear();
}
public void addData(byte[] array){
byteArray.add(array);
}
public byte[] getFullData(){
byte[] retArray;
int totalSize = 0;
for(int i=0; i < byteArray.size();i++){
totalSize = totalSize + byteArray.get(i).length;
}
int copuCounter = 0;
if(totalSize > 0) {
retArray = new byte[totalSize];
for(int ii=0; ii < byteArray.size();ii++){
byte[] tmpArr = byteArray.get(ii);
System.arraycopy(tmpArr, 0, retArray,copuCounter,tmpArr.length);
copuCounter = copuCounter + tmpArr.length;
}
}else{
retArray = new byte[]{};
}
return retArray;
}
}
package com.nclab.ncmultipeerconnectivity;
import android.content.Context;
import android.util.Log;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
/**
* A session is used to control the connection between devices
* android.permission.ACCESS_COARSE_LOCATION and android.permission.ACCESS_FINE_LOCATION must be granted
*/
public class NCMCSession {
private static final String TAG = "NCMCBluetoothLEManager";
public static final int NCMCSessionStateNotConnected = 0;
public static final int NCMCSessionStateConnected = 1;
public static final int NCMCSessionSendDataReliable = 0;
public static final int NCMCSessionSendDataUnreliable = 1;
private static final char SYSMSG_PERIPHERAL_CENTRAL_REFUSE_INVITATION = 0;
private static final char SYSMSG_PERIPHERAL_CENTRAL_ACCEPT_INVITATION = 1;
private static final char SYSMSG_CENTRAL_PERIPHERAL_CONNECTION_REQUEST = 2;
private static final char SYSMSG_CENTRAL_PERIPHERAL_ASSIGN_IDENTIFIER = 3;
private static final char SYSMSG_CENTRAL_PERIPHERAL_DEVICE_CONNECTED = 4;
private static final char SYSMSG_CENTRAL_PERIPHERAL_DEVICE_DISCONNECTED = 5;
protected String serviceID;
protected char myUniqueID;
protected HashMap<String, NCMCDeviceInfo> connectedDevices;
public NCMCPeerID myPeerID;
public NCMCSessionCallback callback;
public NCMCSession(NCMCPeerID _peerID, String _serviceID, Context _context) {
this.serviceID = _serviceID;
this.myPeerID = _peerID;
this.myUniqueID = 0;
this.connectedDevices = new HashMap<>();
this.callback = null;
NCMCBluetoothLEManager.getInstance().clear();
NCMCBluetoothLEManager.getInstance().setSession(this);
NCMCBluetoothLEManager.getInstance().setContext(_context);
}
public void diconnect() {
NCMCBluetoothLEManager.getInstance().disconnect();
this.connectedDevices.clear();
}
public void sendData(byte[] data, List<NCMCPeerID> peerIDs, int mode) {
for (NCMCPeerID peerID : peerIDs) {
byte[] msg = packUserMessage(data, peerID);
if (NCMCBluetoothLEManager.getInstance().isCentral()) {
NCMCBluetoothLEManager.getInstance().sendCentralDataToPeripheral(msg, peerID.identifier, mode);
} else {
NCMCBluetoothLEManager.getInstance().sendPeripheralDataToCentral(msg, getCentralDeviceIdentifier());
}
}
}
public List<NCMCPeerID> getConnectedPeers(){
List<NCMCPeerID> peers = new LinkedList<>();
for (NCMCDeviceInfo info : this.connectedDevices.values()) {
NCMCPeerID peerID = new NCMCPeerID(info.name, info.identifier);
peers.add(peerID);
}
return peers;
}
public void sendResponseToInvitation(boolean accept, NCMCPeerID peerID){
if (accept) {
// send accept to central and wait for assign id
NCMCDeviceInfo device = new NCMCDeviceInfo();
device.identifier = this.myPeerID.identifier;
device.uniqueID = 0;
device.name = this.myPeerID.displayName;
byte[] deviceData = encodeDeviceInfo(device);
byte[] sysData = packSystemMessage(SYSMSG_PERIPHERAL_CENTRAL_ACCEPT_INVITATION, deviceData);
NCMCBluetoothLEManager.getInstance().sendPeripheralDataToCentral(sysData, peerID.identifier);
// clear and init local connected information
NCMCDeviceInfo centralDevice = new NCMCDeviceInfo();
centralDevice.identifier = peerID.identifier;
centralDevice.uniqueID = 0;
centralDevice.name = peerID.displayName;
if (this.connectedDevices != null) {
this.connectedDevices.clear();
this.connectedDevices.put(peerID.identifier, centralDevice);
}
} else {
// send refuse to central
byte[] sysData = packSystemMessage(SYSMSG_PERIPHERAL_CENTRAL_REFUSE_INVITATION, null);
NCMCBluetoothLEManager.getInstance().sendPeripheralDataToCentral(sysData, peerID.identifier);
// remove central device from local
this.connectedDevices.remove(peerID.identifier);
}
}
protected void notifyPeerStateChanged(NCMCPeerID peerID, int state) {
if (this.callback != null) {
this.callback.didChangeState(NCMCSession.this, peerID, state);
}
}
protected void notifyDidReceiveData(byte[] data, NCMCPeerID fromPeer) {
if (this.callback != null) {
this.callback.didReceiveData(NCMCSession.this, data, fromPeer);
}
}
protected void onPeripheralDisconnected(String identifier) {
NCMCDeviceInfo info = this.connectedDevices.get(identifier);
if (info != null) {
// notify this device
NCMCPeerID peerID = new NCMCPeerID(info.name, info.identifier);
notifyPeerStateChanged(peerID, NCMCSessionStateNotConnected);
// if central, notify all peripherals
if (NCMCBluetoothLEManager.getInstance().isCentral()) {
byte[] deviceData = encodeDeviceInfo(info);
byte[] sysData = packSystemMessage(SYSMSG_CENTRAL_PERIPHERAL_DEVICE_DISCONNECTED, deviceData);
for (NCMCDeviceInfo peripheralInfo : this.connectedDevices.values()) {
if (peripheralInfo.uniqueID != 0) {
NCMCBluetoothLEManager.getInstance().sendCentralDataToPeripheral(sysData, peripheralInfo.identifier, NCMCSessionSendDataReliable);
}
}
}
this.connectedDevices.remove(identifier);
}
}
protected void onCentralDisconnected() {
for (NCMCDeviceInfo info : this.connectedDevices.values()) {
if (!info.identifier.equalsIgnoreCase(myPeerID.identifier)) {
NCMCPeerID peerID = new NCMCPeerID(info.name, info.identifier);
notifyPeerStateChanged(peerID, NCMCSessionStateNotConnected);
}
}
}
protected void sendCentralConnectionRequestToPeer(NCMCPeerID peerID) {
NCMCDeviceInfo centralDevice = new NCMCDeviceInfo();
centralDevice.identifier = this.myPeerID.identifier;
centralDevice.uniqueID = 0;
centralDevice.name = this.myPeerID.displayName;
byte[] centralDeviceData = encodeDeviceInfo(centralDevice);
byte[] sysData = packSystemMessage(SYSMSG_CENTRAL_PERIPHERAL_CONNECTION_REQUEST, centralDeviceData);
NCMCBluetoothLEManager.getInstance().sendCentralDataToPeripheral(sysData, peerID.identifier, NCMCSessionSendDataReliable);
}
protected void setSelfAsCentral() {
if (this.connectedDevices != null) {
this.connectedDevices.clear();
} else {
this.connectedDevices = new HashMap<>();
}
this.myUniqueID = 0;
}
private NCMCDeviceInfo getDeviceInfoByUniqueID(char uniqueID) {
for (String key : this.connectedDevices.keySet()) {
if (this.connectedDevices.get(key).uniqueID == uniqueID) {
return this.connectedDevices.get(key);
}
}
return null;
}
protected String getCentralDeviceIdentifier() {
NCMCDeviceInfo centralDevice = getDeviceInfoByUniqueID((char)0);
if (centralDevice != null) {
return centralDevice.identifier;
} else {
return "";
}
}
private byte[] encodeDeviceInfo(NCMCDeviceInfo info) {
int len = info.identifier.length() + info.name.length() + 1;
byte[] idbyte = info.identifier.getBytes();
byte[] namebyte = info.name.getBytes();
byte[] result = new byte[len];
System.arraycopy(idbyte, 0, result, 0, idbyte.length);
result[idbyte.length] = (byte)info.uniqueID;