With this setup, I've been able to get two android phones to send and receive UDP broadcasts. I can also use this setup to send a UDP broadcast from a physical Android device to an iPhone.
However, my problem is that it doesn't seem to work the other way around. The send function is ran on the iPhone, and the receive function is being run on the Android phone. The Android phone never gets the broadcast. It seems like something is wrong with the iPhone's sending function. Here's the setup:
The Android side that has worked for me before:
const port = 37069;
const address = '224.0.0.1';
void receive() async {
final socket = await RawDatagramSocket.bind(address, port);
socket.multicastHops = 1;
socket.broadcastEnabled = true;
socket.writeEventsEnabled = true;
socket.listen((RawSocketEvent event) {
print("still listening...");
final packet = socket.receive();
print("The packet was $packet");
print("It came from ${packet?.address}");
});
}
and this is the iPhone side, that seems to be the problem. I'm not getting errors, so I'm wondering if there are any permissions in the Info.plist file that need to be added?
void broadcast() {
// for the iphone
RawDatagramSocket.bind(address, port).then((RawDatagramSocket socket) {
socket.multicastLoopback = false;
socket.broadcastEnabled = true;
socket.readEventsEnabled = true;
for (int i = 0; i < 150; i++) {
socket.send("Sent #$i".codeUnits, InternetAddress(address), port);
print("sent $i");
}
socket.close();
});
}
I've tested this same setup in my project, and it has worked in the following situations:
Android -> Android
Android -> iOS
but, iOS -> Android doesn't work. When I run the app, I can see that the iPhone is indeed sending the data, but the Android isn't receiving anything. Is the Android side the problem? What am I doing wrong?
I ended up using a package called Bonsoir to achieve what I wanted to.
It lets you broadcast and receive network services, and I'm pretty sure its the same underlying technology as household programs like Airplay and Google Casting. It's also very reliable and simple to use.
To send a certain string, I passed in a string argument in the form of a dictionary into the attributes attribute within the BonsoirService class.
The package can be found here.
Related
I would like to be informed, if a (Android) USB device of a specific interface (USB debugging) is connected to my Windows computer.
For this, I'm trying to use .Net with this code:
const string GUID_DEVINTERFACE_ANDROID = "f72fe0d4-cbcb-407d-8814-9ed673d0dd6b";
const string usbDeviceSelector = "System.Devices.InterfaceClassGuid:=\"{" + GUID_DEVINTERFACE_ANDROID + "}\" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True";
_usbDeviceWatcher = DeviceInformation.CreateWatcher(
usbDeviceSelector,
new string[] { "System.Devices.InterfaceEnabled" },
DeviceInformationKind.AssociationEndpoint);
_usbDeviceWatcher.Updated += UsbDeviceWatcher_Updated;
Unfortunately, the event will not be thrown to my UsbDeviceWatcher_Update function.
I don't want to be informed about a specific device, I want to be notified about all devices, which supports this interface.
How can I get an event, if an device with this special interface will be connected / disconnected from my computer?
If there is a WinUsb solution for this, I would be happy too.
Filter for the a USB device with the interface class {f72fe0d4-cbcb-407d-8814-9ed673d0dd6b} and then subscribe to the Added and Removed events:
using Windows.Devices.Enumeration;
using Windows.Devices.Usb;
var filter = UsbDevice.GetDeviceSelector(new Guid("f72fe0d4-cbcb-407d-8814-9ed673d0dd6b"));
var usbDeviceWatcher = DeviceInformation.CreateWatcher(filter);
usbDeviceWatcher.Added += UsbDeviceWatcher_Added;
usbDeviceWatcher.Removed += UsbDeviceWatcher_Removed;
usbDeviceWatcher.Start();
Console.WriteLine("Press key to exit...");
Console.ReadKey();
void UsbDeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
Console.WriteLine("Added");
}
void UsbDeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
Console.WriteLine("Removed");
}
Alternatively, you can also filter for interface class 0xff, subclass 0x42 and protocol 0x01:
var filter = UsbDevice.GetDeviceClassSelector(new UsbDeviceClass() {
ClassCode = 0xff,
SubclassCode = 0x42,
ProtocolCode = 0x01
});
You can also implement the approach proposed by David Grayson's. But it's considerably more effort.
The standard way to get notified of new or removed USB devices in Windows is to listen for the WM_DEVICECHANGE message sent by the OS to your window. This will give you more notifications than you care about, so whenever you get that message, you should use your favorite library or program to list the devices you care about and see if anything changed.
You will need to override your windows WndProc message. The documentation here discusses how to do that in C#:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.wndproc?view=windowsdesktop-7.0
I have already successfully used BLE advertising to broadcast information from one android device and receive it on another. Now I want the observer to be a Unity-app running on the HoloLens v2. The HoloLens does not need to connect to the android-device as I am aware that this does not seem to be supported. I am looking for a broadcaster -> observer solution.
As mentioned, I already have the broadcaster written and it works fine with android -> android. Now I have implemented my observer in Unity, largely inspired by this article, and it looks like this:
#if ENABLE_WINMD_SUPPORT
using System;
using Windows.Devices.Bluetooth.Advertisement;
#endif
public class DemoManager : MonoBehaviour
{
[SerializeField] private StatusDisplay statusDisplay;
private void Awake()
{
#if ENABLE_WINMD_SUPPORT
StartWatcher();
#else
statusDisplay.Display("UWP APIs are not supported on this platform!");
#endif
}
#if ENABLE_WINMD_SUPPORT
private void StartWatcher()
{
void OnAdvertisementReceived(object sender, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
{
statusDisplay.Display("Advertisement received!");
}
try {
BluetoothLEAdvertisementWatcher watcher = new BluetoothLEAdvertisementWatcher();
watcher.AdvertisementFilter.Advertisement.ManufacturerData.Add(GetManufacturerData());
watcher.Received += OnAdvertisementReceived;
watcher.Start();
statusDisplay.Display("Watcher started!");
} catch (Exception e){
statusDisplay.Display($"Watcher could not start! Error: {e.Message}");
}
}
private BluetoothLEManufacturerData GetManufacturerData()
{
var manufacturerData = new BluetoothLEManufacturerData();
manufacturerData.CompanyId = 1234;
return manufacturerData;
}
#endif
}
The StatusDisplay script is used for displaying text in a thread-safe way. The company-id 1234 is also used by the broadcaster.
My app has bluetooth capabilities (enabled both in the Unity-editor and in the built solution)
All looks very promising, but sadly the advertisement never seems to be received, or at the very least I am getting no corresponding status message.
Does anybody have any ide what might be wrong? Does anyone have any experience with this problem?
We tested the Bluetooth.Advertisement API and works well on the HoloLens. I found that you assigned the CompanyId(a 16-bit unsigned integer) property a signed decimal number, but we usually provide a hexadecimal number as a Bluetooth LE company identifier code. Could you double-check this point both in your watcher and publisher? For example, it should look like 0xFFFE. Besides, more information about how to use the Bluetooth Advertisement API to send and receive Bluetooth Low Energy advertisements please see:Bluetooth advertisement sample
The problem was not with the Unity-side. My advertisement was malformed. I tested my advertisements with a observer that I also wrote myself on Android. So I accounted for the incorrect formatting there, but of course, the C# Advertisement-watcher did not.
I have android platform on one end and arduino on the other, connected via serial. Everything works fine, however in some cases arduino restarts itself and causes a flow of unknown characters while its restarting to the serial.
Here is a serial log while arduino is rebooting:
�z������"&O�Z&���B
���F ���cd�:{����t�>��+������2�~����. ���r���DD���^��.�.B�.��ڮ2t��Z:��,R��A�ڢr��Ckˡ���.-���N^���b�����^���
Question is, how can I check on android end if the response was malformed?
You should probably add some kind of "framing" to your messages. CR/LF isn't enough.
For example, put a special "preamble" at the front, and watch for it on the Android side. Choose something that will not occur in the body ("payload") of the message. And choose something that is very unlikely to occur in the random chars that show up on a reboot, a couple of chars long.
You could also put a CRC at the end. "Fletcher" is easy.
I ended up using simple solution like this:
private String filterData(String receivedStr) {
if (receivedStr.contains(RECV_HEADER) && receivedStr.contains(mReadRules.RECV_END)) {
int header_pos = receivedStr.indexOf(RECV_HEADER);
int crc_pos = receivedStr.indexOf(RECV_END);
return receivedStr.substring(header_pos, crc_pos);
} else {
return null;
}
}
It also extracts message if its wrapped around with malformed data.
After reading the dot42 comments and trolling Java examples I managed to setup a Bluetooth connection but fail to open the connection. I cannot determine the problem. I followed the docs step by step.
My target device is a HTC Explorer running on 2.3 Gingerbread. Here is my code.
//Target 2.3 (Gingerbread)
[assembly: Application("dot42Application1")]
[assembly: UsesPermission(Android.Manifest.Permission.BLUETOOTH)]
[assembly: UsesPermission(Android.Manifest.Permission.BLUETOOTH_ADMIN)]
namespace dot42Application1
{
[Activity]
public class MainActivity : Activity
{
private TextView txStatus;
protected override void OnCreate(Bundle savedInstance)
{
base.OnCreate(savedInstance);
SetContentView(R.Layouts.MainLayout);
// Find UI controls
txStatus = FindViewById<TextView>(R.Ids.txStatus);
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
var bt = BluetoothAdapter.GetDefaultAdapter();
if (bt != null) //If device has not Bluetooth this will be null
{
if (bt.IsEnabled()) //Is Bluetooth device enabled?
{
var BT_My_Addr = bt.Address; //Get the devices MAC
var BT_Bonded = bt.GetBondedDevices().ToList(); //Get a list of bonded devices- I bonded to a BT2TTL Board earlier.
txStatus.Text = BT_My_Addr + System.Environment.NewLine; //Shows my MAC on screen.
string BT_Remote_Address = string.Empty;
foreach (var BTDevice in BT_Bonded) //Just searchging for string in bonded list
{
if (BTDevice.Name.Contains("linvor"))
{
BT_Remote_Address = BTDevice.Address;
}
}
//Gets remote device
var BT_Remote_Device = bt.GetRemoteDevice(BT_Remote_Address);
//Create a RFCOMM Socket to remote device using popular UUID ofr BT Serial boards
var BTsocket = BT_Remote_Device.CreateInsecureRfcommSocketToServiceRecord(Java.Util.UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"));
//Call anyway to make sure there is no discvoerry in the backgorund. It slows stuff down.
bt.CancelDiscovery();
//Exception here? Dont know why :(
BTsocket.Connect();
//Suppsoed to dump 0 to 99999 to my listening serial device but I never get this far.
var BT_Out = BTsocket.GetOutputStream();
for (int i = 0; i < 99999; i++)
{
BT_Out.Write(Encoding.ASCII.GetBytes(i.ToString()));
}
}
else
{
txStatus.Text = "Bluetooth is disabled :(";
}
}
}
}
And this is what it shows after the socket creation
and the error...
What am I doing wrong? :(
I seem to have solved the problem by analysing various code snippets on the internet. I think the problem was trying to do everything in the OnCreate method. The steps I followed are the following:
Created a button on the main view (MainActivity.xml) and attached a onClick method.
Moved all the code OUT of the OnCreate method. (I think this allows the application to fully initialise.) Created an event handler for the button with two methods.
The two methods are the same as the code I posted in my original question. Just they are separated out and called when the user clicks the button.
findBT() Gets the default adapter. Checks if Bluetooth is enabled if not does the intent filter. Or if it is it will cycle through the bonded list and match a device name and store the BluetoohDevice in a variable. This is another thing that is different from my code. I do not use GetRemoteDevice I just assign the device from the BondedList to my global variable.
openBT() creates the RFCOMM socket (this did not work with unsecure - it threw an exception but using the secure method worked!)
You have to pair to the remote device using the Androids Bluetooth control panel. This code will not scan or connect to devices that are not paired. It will just throw null exceptions.
Also I left the target SDK 2.3.x but I am using the 4.x API.
-Disclosure. I am not a seasoned Android developer and just learning about the life cycle of Java applications in the Android context. I hope this can help other C# developers trying to do the same.
I have 2 Android devices using WiFi Direct. On one device I can get information about the other device using the WifiP2pManager class, and request a connection to the other device. However when I request a connection, the other device pops up a little window and asks the user if they want to accept the connection request.
Is it possible to auto-accept these connection requests? I.E to be able to connect to the other device without user confirmation?
It can be easily done with the help of Xposed framework. You just need to replace the single method inside one of android java classes (see the link from snihalani's answer). But of course to use Xposed your device must be rooted. The main idea can be expressed in the following code (using Xposed)
#Override
public void handleLoadPackage(LoadPackageParam lpparam) {
try {
Class<?> wifiP2pService = Class.forName("android.net.wifi.p2p.WifiP2pService", false, lpparam.classLoader);
for (Class<?> c : wifiP2pService.getDeclaredClasses()) {
//XposedBridge.log("inner class " + c.getSimpleName());
if ("P2pStateMachine".equals(c.getSimpleName())) {
XposedBridge.log("Class " + c.getName() + " found");
Method notifyInvitationReceived = c.getDeclaredMethod("notifyInvitationReceived");
final Method sendMessage = c.getMethod("sendMessage", int.class);
XposedBridge.hookMethod(notifyInvitationReceived, new XC_MethodReplacement() {
#Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
final int PEER_CONNECTION_USER_ACCEPT = 0x00023000 + 2;
sendMessage.invoke(param.thisObject, PEER_CONNECTION_USER_ACCEPT);
return null;
}
});
break;
}
}
} catch (Throwable t) {
XposedBridge.log(t);
}
}
I tested it on SGS4 stock 4.2.2 ROM and it worked.
I guess the same could be done with the help of Substrate for android.
From my current understanding of the API, You cannot really accept connections automatically without user's intervention. You can initiate a connection, that doesn't require user intervention. If both of your devices are mobile devices, you will have to accept connection request on one end.
I have put this as a feature request in android project hosting.
You can monitor their response here: https://code.google.com/p/android/issues/detail?id=30880
Based on the comments, do you really need to connect to the devices if you just want to track and log the vehicles around you ?
I don't know the scope of the project, but you could simply use the WifiP2pDeviceList that you get when you request the peers in the WifiP2pManager. You could get the list of the devices (~= vehicles) around you and could log this.
Connection is useful if you want to send more detailed information I guess.
If you can modify the framework, you can ignore the accept window and direct send the "PEER_CONNECTION_USER_ACCEPT".
Base on Android 5.0, "frameworks/opt/net/wifi/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java".
You must find the "notifyInvitationReceived", and modify to ...
private void notifyInvitationReceived() {
/*Direct sends the accept message.*/
sendMessage(PEER_CONNECTION_USER_ACCEPT);
/*
... old code
*/
}