UstadMobile / Meshrabiya

Virtual mesh network for Android that operates over WiFi
GNU Lesser General Public License v3.0
119 stars 11 forks source link
android android-library mesh mesh-networking wifidirect

Meshrabiya

Meshrabiya is a mesh network for Android that operates over WiFi. It allows applications to seamlessly communicate over multiple hops and multiple WiFi direct and/or Local Only Hotspots. Each device is given a "virtual" IP address (typically a random auto-generated address e.g. 169.254.x.y). Applications can then use the provided SocketFactory and/or DatagramSocket class to communicate with other nodes over multiple hops as if they were directly connected. This works with various higher level networking libraries such as OkHttp.

It is intended for use in situations where multiple Android devices need to communicate with each other and a WiFi access point is not available e.g. schools and health clinics without WiFi infrastructure, when hiking, etc. WiFi enables high-speed connections with tests obtaining 300Mbps+. Using multiple hops over multiple WiFi direct groups enables more devices to connect than is possible using a single device hotspot.

Meshrabiya is entirely open-source and does not have any proprietary dependencies (e.g. it works with Android Open Source Project devices and does not use/require Google Play Services, Nearby Connections API, etc).

Meshrabiya provides socket factories (for both TCP and UDP) that can create sockets to route data between nodes over multiple hops as if they were directly connected. The socket factory can also be used with other networking libraries (e.g. OkHttp) to make it easy to send/receive data over the virtual mesh.

How it works:

Want to try it yourself? Download the test app APK from releases.

Want to collaborate on development? Join us on #meshrabiya:matrix.org.

Diagram

Getting started

Add repository to settings.gradle :

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
      ...
      maven { url "https://devserver3.ustadmobile.com/maven2/" }
    }
}       

Add the dependency

implementation "com.github.UstadMobile.Meshrabiya:lib-meshrabiya:0.1-snapshot"

Connect devices

Create a Virtual Node:

//Create a DataStore instance that Meshrabiya can use to remember networks etc.
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "meshr_settings")

val myNode = AndroidVirtualNode(
    appContext = applicationContext,
    dataStore = applicationContext.dataStore,
    //optionally - set address, network prefix length, etc.
)

Create a hotspot on one node:

myNode.setWifiHotspotEnabled(
  enabled = true,
  preferredBand = ConnectBand.BAND_5GHZ,
)

val connectLink = myNode.state.filter {
   it.connectUri != null
}.first()

Use the connect link to connect from another node:


val connectLink = ... //Get this from QR code scan etc.
val connectConfig = MeshrabiyaConnectLink.parseUri(connectLink).hotspotConfig
if(connectConfig != null) {
  myNode.connectAsStation(connectConfig)
}

Exchange data using TCP

  1. On the server side - create a normal server socket:

    val serverVirtualAddr: InetAddress = myNode.address 
    val serverSocket = ServerSocket(port)
  2. On the client side - use the socket factory to create a socket

    val socketFactory = myNode.socketFactory
    val clientSocket = socketFactory.createSocket(serverVirtualAddr, port)

The Socket Factory uses a "socket chain" under the hood. It will lookup the next hop to reach the given destination. It will then connect to the next hop and write its destination to socket stream, similar to how an http proxy uses the host header. Each node runs a chain socket forwarding server. Once the next hop is the destination (e.g. it reaches a node that has a direct connection to the destination node), then the socket is connected to the destination port. See ChainSocketServer for further details.

The Socket factory will fallback to using the system default socket factory for any destination that is not on the virtual network (e.g where the ip address does not match the netmask of the virtual node). It is therefor possible to use the socket factory anywhere, even when connections to non-virtual destinations are required - e.g. it can be used with an OKHttp Client and the client will be able to connect to both virtual and non-virtual addresses. e.g.

val okHttpClient: OkHttpClient = OkHttpClient.Builder()
            .socketFactory(myNode.socketFactory)
            .build()

Exchange data using UDP

Create a DatagramSocket with a given port (or use 0 to get a random port assignment)

val datagramSocket = myNode.createBoundDatagramSocket(port)

The socket can be used the same as a normal DatagramSocket (e.g. by using send/receive), but it will send/receive ONLY over the virtual network. Broadcast packets are supported by setting the destination address to 255.255.255.255

Known issues

Instrumented test debug: You must go to test settings, debug tab, and change to "java only" debugger type. Thank you, Google.