Android Question [Solved] Android blocking receiving UDP broadcast

rtek1000

Active Member
Licensed User
Hello,

I'm trying to implement a auto discovery system, but Android 6 is blocking receiving UDP sent via broadcast, the app can send broadcast but not receive.

Android 6 do not receive "255.255.255.255" and not even "192.168.1.255/24"

Android 6 only receives UDP if the transmitter directs to its IP address.

Already in Android 2.3 is not blocking.

Any configuration that can be performed?

B4X:
#Region  Project Attributes
   #ApplicationLabel: B4A Example
   #VersionCode: 1
   #VersionName:
   'SupportedOrientations possible values: unspecified, landscape or portrait.
   #SupportedOrientations: unspecified
   #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
   #FullScreen: False
   #IncludeTitle: True
#End Region

Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
   Private autodiscover As UDPSocket
   Private BroadcastTimer As Timer
   'Private serializator As B4XSerializator
   Private const discoverPort As Int = 22222
   Private server As ServerSocket 'ignore
   Public DiscoveredServer As String
End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
End Sub

Sub Activity_Create(FirstTime As Boolean)
   'Do not forget to load the layout file created with the visual designer. For example:
   'Activity.LoadLayout("Layout1")
   autodiscover.Initialize("autodiscover", 22222 , 8192)
   BroadcastTimer.Initialize("BroadcastTimer", 5000)
   BroadcastTimer.Enabled = True
   
   Log(server.GetMyWifiIP)

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Private Sub autodiscover_PacketArrived (Packet As UDPPacket)
   Dim msg As String
   msg = BytesToString(Packet.Data, Packet.Offset, Packet.Length, "UTF8")
   Log(msg)
   
   Try
       'Dim bc As ByteConverter
       'Dim data(Packet.Length) As Byte
       'bc.ArrayCopy(Packet.Data, Packet.Offset, data, 0, Packet.Length)
       Dim ds As String = msg ' serializator.ConvertBytesToObject(data)
       If ds <> DiscoveredServer Then
           DiscoveredServer = ds
           Log("Discovered server: " & DiscoveredServer)
           'CallSub(Main, "UpdateState")
       End If
   Catch
       Log(LastException)
   End Try
End Sub

Private Sub BroadcastTimer_Tick
   Dim address As String = GetBroadcastAddress
   Dim data() As Byte = server.GetMyWifiIP.GetBytes("UTF8")
   
   Log("GetBroadcastAddress: " & address)
       
   If address <> "" Then
       Dim up As UDPPacket
       'up.Initialize(serializator.ConvertObjectToBytes(server.GetMyWifiIP), address, discoverPort)
       up.Initialize(data, address, discoverPort)
       autodiscover.Send(up)
   End If
End Sub

'Returns the UDP broadcast address.
'Returns an empty string if not available.
Private Sub GetBroadcastAddress As String
   Dim niIterator As JavaObject
   niIterator = niIterator.InitializeStatic("java.net.NetworkInterface").RunMethod("getNetworkInterfaces", Null)
   Do While niIterator.RunMethod("hasMoreElements", Null)
       Dim ni As JavaObject = niIterator.RunMethod("nextElement", Null)
       If ni.RunMethod("isLoopback", Null) = False Then
           Dim addresses As List = ni.RunMethod("getInterfaceAddresses", Null)
           For Each ia As JavaObject In addresses
               Dim broadcast As Object = ia.RunMethod("getBroadcast", Null)
               If broadcast <> Null Then
                   Dim b As String = broadcast
                   Return b.SubString(1)
               End If
           Next
       End If
   Loop
   Return ""
End Sub
Source: https://www.b4x.com/android/forum/threads/mqtt-chat-with-auto-discovery.75713/

Log from Android 6:
GetBroadcastAddress: 192.168.1.255
192.168.1.61
GetBroadcastAddress: 192.168.1.255
192.168.1.61
GetBroadcastAddress: 192.168.1.255
192.168.1.61
GetBroadcastAddress: 192.168.1.255
192.168.1.61
GetBroadcastAddress: 192.168.1.255
192.168.1.61
GetBroadcastAddress: 192.168.1.255
192.168.1.61
Log from Android 2:
GetBroadcastAddress: 192.168.1.255
192.168.1.43
192.168.1.61
Discovered server: 192.168.1.61
GetBroadcastAddress: 192.168.1.255
192.168.1.43
Discovered server: 192.168.1.43
192.168.1.61
Discovered server: 192.168.1.61
GetBroadcastAddress: 192.168.1.255
192.168.1.43
Discovered server: 192.168.1.43
GetBroadcastAddress: 192.168.1.255
192.168.1.43
192.168.1.61
Discovered server: 192.168.1.61
192.168.1.61
GetBroadcastAddress: 192.168.1.255
192.168.1.43
 

rtek1000

Active Member
Licensed User
Hi, Actually I have not used B4A-Bridge much, because it makes my phone restart without explanation, I've already reported this before.

I am using USB cable to debug, I will search for "red heart".
 

rtek1000

Active Member
Licensed User
I looked for the B4A-Bridge source code and found the "red heart" (Label: lblPing from layout "1").

It happens the same, in Android 6 the heart does not appear, but in Android 2 it appears.

Only appears on Android 6 if something is sent to your IP address, port 58912.
 

rtek1000

Active Member
Licensed User
I found a UDP application (MashUDP) with this permission:

B4X:
AddPermission(android.permission.CHANGE_WIFI_MULTICAST_STATE)
But this App also does not work on my Android 6, same situation, works only when sending data to the IP address, the same address that appears on B4A-Bridge.

I'm thinking of using "tracking" on the device "not Android", this way I can send broadcast by any Android and monitor the network with "ESP8266".
 

rtek1000

Active Member
Licensed User
Hi, I found a possible solution,

Is MulticastLock already implemented in any b4a library?

You need to add the following permission to your app -
B4X:
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
And then in your code acquire a lock like so
B4X:
WifiManager wifi = (WifiManager)getSystemService( Context.WIFI_SERVICE );
if(wifi != null){
    WifiManager.MulticastLock lock = wifi.createMulticastLock("Log_Tag");
    lock.acquire();
}
WifiManager.MulticastLock. From the android documentation -

Allows an application to receive Wifi Multicast packets. Normally the Wifi stack filters out packets not explicitly addressed to this device. Acquring a MulticastLock will cause the stack to receive packets addressed to multicast addresses. Processing these extra packets can cause a noticable battery drain and should be disabled when not needed.
Source: https://stackoverflow.com/questions/27838660/moto-g-dosent-receive-udp-packets-over-wifi-network
 

rtek1000

Active Member
Licensed User
Now I can see the "red heart beating" :)

B4X:
Sub multicastInit()

Sub multicastQuit()

Sub multicastOn()

Sub multicastOff()
Service1 from B4a-Bridge:
B4X:
#Region Module Attributes
   #StartAtBoot: False
#End Region

'Service module
Sub Process_Globals
   Private WifiServer As ServerSocket
   Private Client As Socket
   Private Streams As AsyncStreams
   Private Notification1 As Notification
   Private PE As PhoneEvents
   Private Phone As Phone
   Private LogCat As LogCat
   Public ConnectedStatus As Boolean
   Private Out As OutputStream
   Private LastToastMessageShown As Long 'ignore
   Private Const PING = 1, APK_COPY_START = 2, APK_PACKET = 3 _
       ,APK_COPY_CLOSE = 4, SEND_DEVICE_NAME = 5, START_LOGCAT = 6 _
       ,STOP_LOGCAT = 7, LOGCAT_DATA = 8, LAUNCH_PROGRAM = 9, KILL_PROGRAM = 10 _
       ,ASK_FOR_DESIGNER = 11, BRIDGE_LOG_PORT = 12, SEND_FTP_ENABLED = 13, APK_SIZE = 14, VERSIONS = 15 As Byte
   Private DisconnectTimer As Timer
   Private DisconnectTicks As Int
   Private apkName As Int = 1
   Private noDesignerInstalled As Boolean
   Private lastCheckForDesigner As Long
   Private UdpServer As UDPSocket
   Public FTP As FTPServer
   Public const FTP_PORT = 6781 As Int
   Public currentAPKTotal As Int
   Public currentAPKWritten As Int
   Private Phone As Phone
   Private ReflectorIGMP As Reflector
End Sub

Sub Service_Create
   
   Try
       If WifiServer.IsInitialized = False Then
           WifiServer.Initialize(6789, "Server")
       End If
   Catch
       Log(LastException.Message)
       'switch to alternate port
       WifiServer.Initialize(6789 + 117, "Server")
   End Try
   If Phone.SdkVersion >= 16 And UdpServer.IsInitialized = False Then
       UdpServer.Initialize("Udp", 0, 8192)
   End If
   DisconnectTimer.Initialize("DisconnectTimer", 1000)
   
   PE.Initialize("PE")
   UpdateNotification
   Service.StartForeground(1, Notification1)
   Try
       SetFTPServerState
   Catch
       Log(LastException)
   End Try
   
   multicastInit
End Sub

Public Sub SetFTPServerState
   If Main.FTPServerEnabled Then
       If FTP.IsInitialized And FTP.Running Then Return
       FTP.Initialize(Main, "ftp")
       FTP.AddUser("anonymous", "")
       FTP.BaseDir = File.DirRootExternal
       FTP.SetPorts(FTP_PORT, 6782, 6788)
       FTP.Start
   Else
       If FTP.IsInitialized And FTP.Running Then
           FTP.Stop
       End If
   End If
End Sub


Private Sub UpdateNotification
   Dim icon As String
   Dim content As String
   If ConnectedStatus Then
       icon = "a_connected"
       content = "Connected"
   Else
       icon = "a_disconnected"
       content = "Disconnected"
   End If
   Notification1.Initialize '.Initialize2(Notification1.IMPORTANCE_LOW)
   Notification1.Sound = False
   Notification1.Vibrate = False
   Notification1.Icon = icon
   Notification1.SetInfo("B4A-Bridge", content, Main)
   Notification1.Notify(1)
End Sub

Sub Service_Start (StartingIntent As Intent)
   Try
       UpdateStatus (ConnectedStatus, True)
   Catch
       ShowToastMessage("Error starting service: " & LastException.Message)
   End Try
End Sub

Sub UpdateStatus (Connected As Boolean, AllowFurtherConnections As Boolean)
   ConnectedStatus = Connected
   If Connected Then
       DisconnectTimer.Enabled = True
       DisconnectTicks = 0
       CheckIfDesignerIsInstalled
   Else
       If Streams.IsInitialized Then Streams.Close
       DisconnectTimer.Enabled = False
       
   End If
   If AllowFurtherConnections Then
       UpdateNotification
       WifiServer.Listen
   End If
   If IsPaused(Main) = False Then CallSubDelayed(Main, "UpdateStatus")
End Sub

Sub DisconnectTimer_Tick
   DisconnectTicks = DisconnectTicks + 1
   If DisconnectTicks = 10 Then
       UpdateStatus(False, True)
   End If
End Sub

Sub Service_Destroy
   multicastQuit
   Service.StopForeground(1)
   UpdateStatus (False, False)
   LogCat.LogCatStop
   If FTP.IsInitialized Then FTP.Stop
End Sub

Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
   If Streams.IsInitialized Then Streams.Close
   Dim Streams As AsyncStreams 'create a new streams object.
   If Successful Then
       Log("Connected to B4A-Bridge (Wifi)")
       Client = NewSocket
       Streams.InitializePrefix(Client.InputStream, False, Client.OutputStream, _
           "Streams")
       AfterConnect
   Else
       ShowToastMessage(LastException.Message)
       UpdateStatus(False, True)
   End If
End Sub

Sub AfterConnect
   Dim pingB(1) As Byte
   pingB(0) = PING
   Streams.Write(pingB)
   Dim name As String = Phone.Manufacturer & " " & Phone.Model
   Dim b() As Byte = name.GetBytes("UTF8")
   Streams.Write(Utils.AddCommandToBytes(SEND_DEVICE_NAME, b, b.Length))
   If Main.FTPServerEnabled Then
       Streams.Write(Utils.AddCommandToBytes(SEND_FTP_ENABLED, Array As Byte(1), 1))
   End If
   SendVersions
   UpdateStatus(True, True)
End Sub

Sub SendVersions
   Dim b(9) As Byte
   Dim raf As RandomAccessFile = CreateRAF(b)
   raf.WriteByte(VERSIONS, raf.CurrentPosition)
   raf.WriteInt(Application.VersionCode, raf.CurrentPosition)
   Dim p As Phone
   raf.WriteInt(p.SdkVersion, raf.CurrentPosition)
   Streams.Write(b)
End Sub


Sub Streams_Error
   If Sender <> Streams Then Return
   ShowToastMessage(LastException.Message)
   UpdateStatus(False, True)
End Sub

Sub Streams_Terminated
   If Sender <> Streams Then Return
   UpdateStatus(False, True)
   Log("Streams_terminated")
End Sub



Sub Streams_NewData (Buffer() As Byte)
   If Streams.IsInitialized = False Then Return
   DisconnectTicks = 0 'reset the disconnect timer
   Dim command As Byte
   command = Buffer(0)
   Select command
       Case PING
           Streams.Write(Array As Byte(PING))
           If noDesignerInstalled And DateTime.Now > lastCheckForDesigner + 20 * DateTime.TicksPerSecond Then CheckIfDesignerIsInstalled
       Case APK_COPY_START
           HandleFileStart
       Case APK_PACKET
           Streams.Write(Array As Byte(PING))
           HandleFilePacket(Buffer)
       Case APK_COPY_CLOSE
           HandleFileClose
       Case START_LOGCAT
           StartLogcat(Buffer)
       Case STOP_LOGCAT
           StopLogcat
       Case LAUNCH_PROGRAM
           LaunchProgram(Buffer)
       Case KILL_PROGRAM
           KillProgram(Buffer)
       Case APK_SIZE
           Dim raf As RandomAccessFile = CreateRAF(Buffer)
           currentAPKTotal = raf.ReadInt(1)
           currentAPKWritten = 0
       Case VERSIONS
           Dim raf As RandomAccessFile = CreateRAF(Buffer)
           Dim ideVersion As Int = raf.ReadInt(1) 'ignore
   End Select
End Sub

Sub CreateRAF(buffer() As Byte) As RandomAccessFile
   Dim raf As RandomAccessFile
   raf.Initialize3(buffer, True)
   Return raf
End Sub

Sub CheckIfDesignerIsInstalled
   Dim pm As PackageManager
   Dim packages As List
   packages = pm.GetInstalledPackages
   Dim buffer() As Byte
   If packages.IndexOf("anywheresoftware.b4a.designer") = -1 Then
       buffer = "0".GetBytes("UTF8")
       noDesignerInstalled = True
       lastCheckForDesigner = DateTime.Now
   Else
       noDesignerInstalled = False
       Dim s As String
       s = pm.GetVersionCode("anywheresoftware.b4a.designer")
       buffer = s.GetBytes("UTF8")
   End If
   
   'we are sending the installed designer version so the IDE will decide if a new version should be installed.
   Streams.Write(Utils.AddCommandToBytes(ASK_FOR_DESIGNER, buffer, buffer.Length))
End Sub
Sub LaunchProgram (Buffer() As Byte)
   Dim s As String
   s = BytesToString(Buffer, 1, Buffer.Length - 1, "UTF8")
   Dim args() As String
   args = Regex.Split(",", s)
   Dim In1 As Intent
   For i = 1 To args.Length - 1 Step 2
       Select args(i)
           Case "-a"
               In1.Initialize(args(i + 1), "")
           Case "-n"
               In1.SetComponent(args(i + 1))
           Case "-f"
               In1.Flags = Bit.ParseInt(args(i + 1).SubString(2), 16)
           Case "-c"
               In1.SetComponent(args(i + 1))
           Case "-e"
               In1.PutExtra(args(i + 1), args(i + 2))
               Exit
       End Select
   Next
   Try
       StartActivity(In1)
   Catch
       ShowToastMessage("Error starting intent: " & In1) 'ignore
   End Try
End Sub

Sub KillProgram (Buffer() As Byte)
   Dim package As String
   package = BytesToString(Buffer, 1, Buffer.Length - 1, "UTF8")
   Dim sb As StringBuilder
   sb.Initialize
   Phone.Shell("ps", Null, sb, Null)
   Dim m As Matcher
   m = Regex.Matcher2("^[^ ]*\s+(\d+) .*" & package, Regex.MULTILINE, sb.ToString)
   If m.Find Then
       Log("Package found: " & package)
       Phone.Shell("kill", Array As String(m.Group(1)), Null, Null)
   End If
End Sub
Sub StartLogcat (Buffer() As Byte)
   Dim args As String
   args = BytesToString(Buffer, 1, Buffer.Length - 1, "UTF8")
   LogCat.LogCatStart(Regex.Split(",", args), "LogCat")
End Sub

Sub LogCat_LogCatData (Buffer() As Byte, Length As Int)
   If ConnectedStatus = True Then
       Streams.Write(Utils.AddCommandToBytes(LOGCAT_DATA, Buffer, Length))
   End If
End Sub
Sub StopLogcat
   LogCat.LogCatStop
End Sub
Sub HandleFileStart
   Log("Installing file.")
   If Phone.SdkVersion >= 26 Then
       If IsPaused(Main) Then
           StartActivity(Main)
       End If
   End If
   Try
       If Out.IsInitialized Then Out.Close
   Catch
       Log(LastException)
   End Try
   Dim retries As Int = 5
   Do While retries > 0
       Try
           Out = File.OpenOutput(Starter.folder, "temp" & apkName & ".apk", False)
           Exit
       Catch
           Log(LastException.Message) 'this can happen if the file is locked.
           apkName = apkName + 1
           retries = retries - 1
       End Try
   Loop
   If retries = 0 Then
       ToastMessageShow("Error writing file.", True)
   End If
End Sub

Sub HandleFilePacket(Buffer() As Byte)
   Try
       Out.WriteBytes(Buffer, 1, Buffer.Length - 1)
   Catch
       Log(LastException)
   End Try
   currentAPKWritten = currentAPKWritten + Buffer.Length
   UpdateFileProgress
End Sub

Private Sub UpdateFileProgress
   If IsPaused(Main) = False Then
       CallSub(Main, "UpdateFileProgress")
   End If
End Sub

Sub HandleFileClose
   Try
       Out.Close
   Catch
       Log(LastException)
       ToastMessageShow("Error saving APK", True)
       Return
   End Try
   currentAPKTotal = 0
   UpdateFileProgress
   'ask the system to install the apk
   SendInstallIntent
   If UdpServer.IsInitialized Then
       Dim port As String
       port = UdpServer.port
       Dim b() As Byte = port.GetBytes("UTF8")
       Streams.Write(Utils.AddCommandToBytes(BRIDGE_LOG_PORT, b, b.Length))
   End If
   Sleep(1000)
   If IsPaused(Main) = False Then
       Log("sending another install intent")
       SendInstallIntent
   End If
End Sub

Private Sub SendInstallIntent
   Dim i As Intent
   If Phone.SdkVersion >= 24 Then
       i.Initialize("android.intent.action.INSTALL_PACKAGE", CreateFileProviderUri(Starter.folder, "temp" & apkName & ".apk"))
       i.Flags = Bit.Or(i.Flags, 1) 'FLAG_GRANT_READ_URI_PERMISSION
   Else
       i.Initialize(i.ACTION_VIEW, "file://" & File.Combine(Starter.folder, "temp" & apkName & ".apk"))
       i.SetType("application/vnd.android.package-archive")
   End If
   StartActivity(i)
End Sub

Sub PE_PackageAdded (Package As String, Intent As Intent)
   Log("PackageAdded: " & Package)
End Sub
Sub PE_ConnectivityChanged (NetworkType As String, State As String, Intent As Intent)
   If NetworkType = "WIFI" Then CallSub(Main, "UpdateIp")
End Sub
Sub ShowToastMessage(s As String)
#if debug
   Log(s)
   If LastToastMessageShown + 5 * DateTime.TicksPerSecond > DateTime.Now Then
       Return
   Else
       LastToastMessageShown = DateTime.Now
       ToastMessageShow(s, True)
   End If
#End If
End Sub
Sub UDP_PacketArrived (Packet As UDPPacket)
   Log(BytesToString(Packet.Data, Packet.Offset, Packet.Length, "UTF8"))
End Sub

Sub CreateFileProviderUri (Dir As String, FileName As String) As Object
   Dim FileProvider As JavaObject
   Dim context As JavaObject
   context.InitializeContext
   FileProvider.InitializeStatic("android.support.v4.content.FileProvider")
   Dim f As JavaObject
   f.InitializeNewInstance("java.io.File", Array(Dir, FileName))
   Return FileProvider.RunMethod("getUriForFile", Array(context, Application.PackageName & ".provider", f))
End Sub

Sub multicastInit()
   ReflectorIGMP.Target = ReflectorIGMP.GetContext
   ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("getSystemService", "wifi", "java.lang.String")
   ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("createMulticastLock", "mylock", "java.lang.String")
   ReflectorIGMP.RunMethod2("setReferenceCounted", False, "java.lang.boolean") ' not really necessary but safer
   
   multicastOn
End Sub

Sub multicastQuit()
   multicastOff
End Sub

Sub multicastOn()
   Log("Multicast: On")
'   ReflectorIGMP.Target = ReflectorIGMP.GetContext
'   ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("getSystemService", "wifi", "java.lang.String")
'   ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("createMulticastLock", "mylock", "java.lang.String")
'   ReflectorIGMP.RunMethod2("setReferenceCounted", False, "java.lang.boolean") ' not really necessary but safer
   ReflectorIGMP.RunMethod("acquire")
End Sub

Sub multicastOff()
   Log("Multicast: Off")

   ReflectorIGMP.RunMethod("release")
End Sub
Source: https://www.b4x.com/android/forum/threads/how-to-receive-multicast-packet.25408/#post-147816
 

rtek1000

Active Member
Licensed User
Modifield code:
[isHeld() added]

B4X:
#Region Module Attributes
    #StartAtBoot: False
#End Region

'Service module
Sub Process_Globals
    Private WifiServer As ServerSocket
    Private Client As Socket
    Private Streams As AsyncStreams
    Private Notification1 As Notification
    Private PE As PhoneEvents
    Private Phone As Phone
    Private LogCat As LogCat
    Public ConnectedStatus As Boolean
    Private Out As OutputStream
    Private LastToastMessageShown As Long 'ignore
    Private Const PING = 1, APK_COPY_START = 2, APK_PACKET = 3 _
        ,APK_COPY_CLOSE = 4, SEND_DEVICE_NAME = 5, START_LOGCAT = 6 _
        ,STOP_LOGCAT = 7, LOGCAT_DATA = 8, LAUNCH_PROGRAM = 9, KILL_PROGRAM = 10 _
        ,ASK_FOR_DESIGNER = 11, BRIDGE_LOG_PORT = 12, SEND_FTP_ENABLED = 13, APK_SIZE = 14, VERSIONS = 15 As Byte
    Private DisconnectTimer As Timer
    Private DisconnectTicks As Int
    Private apkName As Int = 1
    Private noDesignerInstalled As Boolean
    Private lastCheckForDesigner As Long
    Private UdpServer As UDPSocket
    Public FTP As FTPServer
    Public const FTP_PORT = 6781 As Int
    Public currentAPKTotal As Int
    Public currentAPKWritten As Int
    Private Phone As Phone
    Private ReflectorIGMP As Reflector
    private prevStateMulticast as Boolean
End Sub

Sub Service_Create
    
    Try
        If WifiServer.IsInitialized = False Then
            WifiServer.Initialize(6789, "Server")
        End If
    Catch
        Log(LastException.Message)
        'switch to alternate port
        WifiServer.Initialize(6789 + 117, "Server")
    End Try
    If Phone.SdkVersion >= 16 And UdpServer.IsInitialized = False Then
        UdpServer.Initialize("Udp", 0, 8192)
    End If
    DisconnectTimer.Initialize("DisconnectTimer", 1000)
    
    PE.Initialize("PE")
    UpdateNotification
    Service.StartForeground(1, Notification1)
    Try
        SetFTPServerState
    Catch
        Log(LastException)
    End Try
    
    multicastInit
End Sub

Public Sub SetFTPServerState
    If Main.FTPServerEnabled Then
        If FTP.IsInitialized And FTP.Running Then Return
        FTP.Initialize(Main, "ftp")
        FTP.AddUser("anonymous", "")
        FTP.BaseDir = File.DirRootExternal
        FTP.SetPorts(FTP_PORT, 6782, 6788)
        FTP.Start
    Else
        If FTP.IsInitialized And FTP.Running Then
            FTP.Stop
        End If
    End If
End Sub


Private Sub UpdateNotification
    Dim icon As String
    Dim content As String
    If ConnectedStatus Then
        icon = "a_connected"
        content = "Connected"
    Else
        icon = "a_disconnected"
        content = "Disconnected"
    End If
    Notification1.Initialize '.Initialize2(Notification1.IMPORTANCE_LOW)
    Notification1.Sound = False
    Notification1.Vibrate = False
    Notification1.Icon = icon
    Notification1.SetInfo("B4A-Bridge", content, Main)
    Notification1.Notify(1)
End Sub

Sub Service_Start (StartingIntent As Intent)
    Try
        UpdateStatus (ConnectedStatus, True)
    Catch
        ShowToastMessage("Error starting service: " & LastException.Message)
    End Try
End Sub

Sub UpdateStatus (Connected As Boolean, AllowFurtherConnections As Boolean)
    ConnectedStatus = Connected
    If Connected Then
        DisconnectTimer.Enabled = True
        DisconnectTicks = 0
        CheckIfDesignerIsInstalled
    Else
        If Streams.IsInitialized Then Streams.Close
        DisconnectTimer.Enabled = False
        
    End If
    If AllowFurtherConnections Then
        UpdateNotification
        WifiServer.Listen
    End If
    If IsPaused(Main) = False Then CallSubDelayed(Main, "UpdateStatus")
End Sub

Sub DisconnectTimer_Tick
    DisconnectTicks = DisconnectTicks + 1
    If DisconnectTicks = 10 Then
        UpdateStatus(False, True)
    End If
End Sub

Sub Service_Destroy
    multicastQuit
    Service.StopForeground(1)
    UpdateStatus (False, False)
    LogCat.LogCatStop
    If FTP.IsInitialized Then FTP.Stop
End Sub

Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
    If Streams.IsInitialized Then Streams.Close
    Dim Streams As AsyncStreams 'create a new streams object.
    If Successful Then
        Log("Connected to B4A-Bridge (Wifi)")
        Client = NewSocket
        Streams.InitializePrefix(Client.InputStream, False, Client.OutputStream, _
            "Streams")
        AfterConnect
    Else
        ShowToastMessage(LastException.Message)
        UpdateStatus(False, True)
    End If
End Sub

Sub AfterConnect
    Dim pingB(1) As Byte
    pingB(0) = PING
    Streams.Write(pingB)
    Dim name As String = Phone.Manufacturer & " " & Phone.Model
    Dim b() As Byte = name.GetBytes("UTF8")
    Streams.Write(Utils.AddCommandToBytes(SEND_DEVICE_NAME, b, b.Length))
    If Main.FTPServerEnabled Then
        Streams.Write(Utils.AddCommandToBytes(SEND_FTP_ENABLED, Array As Byte(1), 1))
    End If
    SendVersions
    UpdateStatus(True, True)
End Sub

Sub SendVersions
    Dim b(9) As Byte
    Dim raf As RandomAccessFile = CreateRAF(b)
    raf.WriteByte(VERSIONS, raf.CurrentPosition)
    raf.WriteInt(Application.VersionCode, raf.CurrentPosition)
    Dim p As Phone
    raf.WriteInt(p.SdkVersion, raf.CurrentPosition)
    Streams.Write(b)
End Sub


Sub Streams_Error
    If Sender <> Streams Then Return
    ShowToastMessage(LastException.Message)
    UpdateStatus(False, True)
End Sub

Sub Streams_Terminated
    If Sender <> Streams Then Return
    UpdateStatus(False, True)
    Log("Streams_terminated")
End Sub



Sub Streams_NewData (Buffer() As Byte)
    If Streams.IsInitialized = False Then Return
    DisconnectTicks = 0 'reset the disconnect timer
    Dim command As Byte
    command = Buffer(0)
    Select command
        Case PING
            Streams.Write(Array As Byte(PING))
            If noDesignerInstalled And DateTime.Now > lastCheckForDesigner + 20 * DateTime.TicksPerSecond Then CheckIfDesignerIsInstalled
        Case APK_COPY_START
            HandleFileStart
        Case APK_PACKET
            Streams.Write(Array As Byte(PING))
            HandleFilePacket(Buffer)
        Case APK_COPY_CLOSE
            HandleFileClose
        Case START_LOGCAT
            StartLogcat(Buffer)
        Case STOP_LOGCAT
            StopLogcat
        Case LAUNCH_PROGRAM
            LaunchProgram(Buffer)
        Case KILL_PROGRAM
            KillProgram(Buffer)
        Case APK_SIZE
            Dim raf As RandomAccessFile = CreateRAF(Buffer)
            currentAPKTotal = raf.ReadInt(1)
            currentAPKWritten = 0
        Case VERSIONS
            Dim raf As RandomAccessFile = CreateRAF(Buffer)
            Dim ideVersion As Int = raf.ReadInt(1) 'ignore
    End Select
End Sub

Sub CreateRAF(buffer() As Byte) As RandomAccessFile
    Dim raf As RandomAccessFile
    raf.Initialize3(buffer, True)
    Return raf
End Sub

Sub CheckIfDesignerIsInstalled
    Dim pm As PackageManager
    Dim packages As List
    packages = pm.GetInstalledPackages
    Dim buffer() As Byte
    If packages.IndexOf("anywheresoftware.b4a.designer") = -1 Then
        buffer = "0".GetBytes("UTF8")
        noDesignerInstalled = True
        lastCheckForDesigner = DateTime.Now
    Else
        noDesignerInstalled = False
        Dim s As String
        s = pm.GetVersionCode("anywheresoftware.b4a.designer")
        buffer = s.GetBytes("UTF8")
    End If
    
    'we are sending the installed designer version so the IDE will decide if a new version should be installed.
    Streams.Write(Utils.AddCommandToBytes(ASK_FOR_DESIGNER, buffer, buffer.Length))
End Sub
Sub LaunchProgram (Buffer() As Byte)
    Dim s As String
    s = BytesToString(Buffer, 1, Buffer.Length - 1, "UTF8")
    Dim args() As String
    args = Regex.Split(",", s)
    Dim In1 As Intent
    For i = 1 To args.Length - 1 Step 2
        Select args(i)
            Case "-a"
                In1.Initialize(args(i + 1), "")
            Case "-n"
                In1.SetComponent(args(i + 1))
            Case "-f"
                In1.Flags = Bit.ParseInt(args(i + 1).SubString(2), 16)
            Case "-c"
                In1.SetComponent(args(i + 1))
            Case "-e"
                In1.PutExtra(args(i + 1), args(i + 2))
                Exit
        End Select
    Next
    Try
        StartActivity(In1)
    Catch
        ShowToastMessage("Error starting intent: " & In1) 'ignore
    End Try
End Sub

Sub KillProgram (Buffer() As Byte)
    Dim package As String
    package = BytesToString(Buffer, 1, Buffer.Length - 1, "UTF8")
    Dim sb As StringBuilder
    sb.Initialize
    Phone.Shell("ps", Null, sb, Null)
    Dim m As Matcher
    m = Regex.Matcher2("^[^ ]*\s+(\d+) .*" & package, Regex.MULTILINE, sb.ToString)
    If m.Find Then
        Log("Package found: " & package)
        Phone.Shell("kill", Array As String(m.Group(1)), Null, Null)
    End If
End Sub
Sub StartLogcat (Buffer() As Byte)
    Dim args As String
    args = BytesToString(Buffer, 1, Buffer.Length - 1, "UTF8")
    LogCat.LogCatStart(Regex.Split(",", args), "LogCat")
End Sub

Sub LogCat_LogCatData (Buffer() As Byte, Length As Int)
    If ConnectedStatus = True Then
        Streams.Write(Utils.AddCommandToBytes(LOGCAT_DATA, Buffer, Length))
    End If
End Sub
Sub StopLogcat
    LogCat.LogCatStop
End Sub
Sub HandleFileStart
    Log("Installing file.")
    If Phone.SdkVersion >= 26 Then
        If IsPaused(Main) Then
            StartActivity(Main)
        End If
    End If
    Try
        If Out.IsInitialized Then Out.Close
    Catch
        Log(LastException)
    End Try
    Dim retries As Int = 5
    Do While retries > 0
        Try
            Out = File.OpenOutput(Starter.folder, "temp" & apkName & ".apk", False)
            Exit
        Catch
            Log(LastException.Message) 'this can happen if the file is locked.
            apkName = apkName + 1
            retries = retries - 1
        End Try
    Loop
    If retries = 0 Then
        ToastMessageShow("Error writing file.", True)
    End If
End Sub

Sub HandleFilePacket(Buffer() As Byte)
    Try
        Out.WriteBytes(Buffer, 1, Buffer.Length - 1)
    Catch
        Log(LastException)
    End Try
    currentAPKWritten = currentAPKWritten + Buffer.Length
    UpdateFileProgress
End Sub

Private Sub UpdateFileProgress
    If IsPaused(Main) = False Then
        CallSub(Main, "UpdateFileProgress")
    End If
End Sub

Sub HandleFileClose
    Try
        Out.Close
    Catch
        Log(LastException)
        ToastMessageShow("Error saving APK", True)
        Return
    End Try
    currentAPKTotal = 0
    UpdateFileProgress
    'ask the system to install the apk
    SendInstallIntent
    If UdpServer.IsInitialized Then
        Dim port As String
        port = UdpServer.port
        Dim b() As Byte = port.GetBytes("UTF8")
        Streams.Write(Utils.AddCommandToBytes(BRIDGE_LOG_PORT, b, b.Length))
    End If
    Sleep(1000)
    If IsPaused(Main) = False Then
        Log("sending another install intent")
        SendInstallIntent
    End If
End Sub

Private Sub SendInstallIntent
    Dim i As Intent
    If Phone.SdkVersion >= 24 Then
        i.Initialize("android.intent.action.INSTALL_PACKAGE", CreateFileProviderUri(Starter.folder, "temp" & apkName & ".apk"))
        i.Flags = Bit.Or(i.Flags, 1) 'FLAG_GRANT_READ_URI_PERMISSION
    Else
        i.Initialize(i.ACTION_VIEW, "file://" & File.Combine(Starter.folder, "temp" & apkName & ".apk"))
        i.SetType("application/vnd.android.package-archive")
    End If
    StartActivity(i)
End Sub

Sub PE_PackageAdded (Package As String, Intent As Intent)
    Log("PackageAdded: " & Package)
End Sub
Sub PE_ConnectivityChanged (NetworkType As String, State As String, Intent As Intent)
    If NetworkType = "WIFI" Then CallSub(Main, "UpdateIp")
End Sub
Sub ShowToastMessage(s As String)
#if debug
    Log(s)
    If LastToastMessageShown + 5 * DateTime.TicksPerSecond > DateTime.Now Then
        Return
    Else
        LastToastMessageShown = DateTime.Now
        ToastMessageShow(s, True)
    End If
#End If
End Sub
Sub UDP_PacketArrived (Packet As UDPPacket)
    Log(BytesToString(Packet.Data, Packet.Offset, Packet.Length, "UTF8"))
End Sub

Sub CreateFileProviderUri (Dir As String, FileName As String) As Object
    Dim FileProvider As JavaObject
    Dim context As JavaObject
    context.InitializeContext
    FileProvider.InitializeStatic("android.support.v4.content.FileProvider")
    Dim f As JavaObject
    f.InitializeNewInstance("java.io.File", Array(Dir, FileName))
    Return FileProvider.RunMethod("getUriForFile", Array(context, Application.PackageName & ".provider", f))
End Sub

Sub multicastInit()
    ReflectorIGMP.Target = ReflectorIGMP.GetContext
    ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("getSystemService", "wifi", "java.lang.String")
    ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("createMulticastLock", "mylock", "java.lang.String")
    ReflectorIGMP.RunMethod("isHeld")
    
    prevStateMulticast = isMulticast
    
    If prevStateMulticast = False Then
        Log("MulticastLock previous state: not held")
        multicastOn
        isMulticast
    Else
        Log("MulticastLock previous state: held")
    End If
End Sub

Sub multicastQuit()
    If prevStateMulticast = False And isMulticast Then
        multicastOff
    End If
    
    ReflectorIGMP.RunMethod("finalize")
    
    isMulticast
End Sub

Sub multicastOn()
    Log("Multicast: On")
'    ReflectorIGMP.Target = ReflectorIGMP.GetContext
'    ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("getSystemService", "wifi", "java.lang.String")
'    ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("createMulticastLock", "mylock", "java.lang.String")
    ReflectorIGMP.RunMethod2("setReferenceCounted", False, "java.lang.boolean") ' not really necessary but safer
    ReflectorIGMP.RunMethod("acquire")
End Sub

Sub multicastOff()
    Log("Multicast: Off")

    ReflectorIGMP.RunMethod("release")
End Sub

Sub isMulticast() As Boolean
    ReflectorIGMP.RunMethod("isHeld")
    Log(ReflectorIGMP.ToString)
    
    If ReflectorIGMP.ToString.Contains("held") Then
        Log("MulticastLock current state: held")
        Return True
    Else
        Log("MulticastLock current state: not held")
        Return False
    End If
End Sub
Log:

App start:
*** Service (service1) Create ***
MulticastLock{ d1050fa; refcounted: refcount = 0 }
MulticastLock current state: not held
MulticastLock previous state: not held
Multicast: On
MulticastLock{ d1050fa; held; not refcounted }
MulticastLock current state: held
** Service (service1) Start **
Broadcast from your 'Arduino Uno WiFi' over UDP socket:
IP Address 192.168.1.68
Broadcast from your 'Arduino Uno WiFi' over UDP socket:
IP Address 192.168.1.68
Button STOP:
** Service (service1) Destroy **
MulticastLock{ d1050fa; held; not refcounted }
MulticastLock current state: held
Multicast: Off
MulticastLock{ d1050fa; not refcounted }
MulticastLock current state: not held
Button START:
*** Service (service1) Create ***
MulticastLock{ 100eb7f; refcounted: refcount = 0 }
MulticastLock current state: not held
MulticastLock previous state: not held
Multicast: On
MulticastLock{ 100eb7f; held; not refcounted }
MulticastLock current state: held
** Service (service1) Start **
Broadcast from your 'Arduino Uno WiFi' over UDP socket:
IP Address 192.168.1.68
Broadcast from your 'Arduino Uno WiFi' over UDP socket:
IP Address 192.168.1.68
Button STOP:
** Service (service1) Destroy **
MulticastLock{ 100eb7f; held; not refcounted }
MulticastLock current state: held
Multicast: Off
MulticastLock{ 100eb7f; not refcounted }
MulticastLock current state: not held
 

Erel

Administrator
Staff member
Licensed User
This is the code added to B4A-Bridge:
B4X:
Sub AcquireMulticastLock
   Try
       Dim ctxt As JavaObject
       ctxt.InitializeContext
       Dim WifiManager As JavaObject = ctxt.RunMethod("getSystemService", Array("wifi"))
       MulticastLock = WifiManager.RunMethodJO("createMulticastLock", Array("b4a-udp"))
       MulticastLock.RunMethod("acquire", Null)
   Catch
       Log(LastException)
   End Try
End Sub
Note that the MulticastLock must be a process global variable.
 

rtek1000

Active Member
Licensed User
Nice!

I tested it with a second app, and the state change made by one app does not seem to affect another app

test_on_off.png

App test code: Service Starter
B4X:
#Region  Service Attributes
    #StartAtBoot: False
    #ExcludeFromLibrary: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private timer1 As Timer
    Private ReflectorIGMP As Reflector
    Private prevStateMulticast As Boolean
End Sub

Sub Service_Create
    'This is the program entry point.
    'This is a good place to load resources that are not specific to a single activity.
    timer1.Initialize("timer1", 10000)
    timer1.Enabled = True
    
    multicastInit ' need add AddPermission(android.permission.CHANGE_WIFI_MULTICAST_STATE)
End Sub

Sub Service_Start (StartingIntent As Intent)

End Sub

Sub Service_TaskRemoved
    'This event will be raised when the user removes the app from the recent apps list.
End Sub

'Return true to allow the OS default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

Sub Service_Destroy
    multicastQuit
End Sub

Sub timer1_tick()
    Log("Test")

    If isMulticast Then
        multicastOff
    Else
        multicastOn
    End If
    
End Sub

Sub multicastInit()
    ReflectorIGMP.Target = ReflectorIGMP.GetContext
    ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("getSystemService", "wifi", "java.lang.String")
    ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("createMulticastLock", "mylock", "java.lang.String")
    ReflectorIGMP.RunMethod("isHeld")
    
    prevStateMulticast = isMulticast
    
    If prevStateMulticast = False Then
        Log("MulticastLock previous state: not held")
        multicastOn
        isMulticast
    Else
        Log("MulticastLock previous state: held")
    End If
End Sub

Sub multicastQuit()
    If prevStateMulticast = False And isMulticast Then
        multicastOff
    End If
    
    ReflectorIGMP.RunMethod("finalize")
    
    isMulticast
End Sub

Sub multicastOn()
    Log("Multicast: On")
'    ReflectorIGMP.Target = ReflectorIGMP.GetContext
'    ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("getSystemService", "wifi", "java.lang.String")
'    ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("createMulticastLock", "mylock", "java.lang.String")
    ReflectorIGMP.RunMethod2("setReferenceCounted", False, "java.lang.boolean") ' not really necessary but safer
    ReflectorIGMP.RunMethod("acquire")
End Sub

Sub multicastOff()
    Log("Multicast: Off")

    ReflectorIGMP.RunMethod("release")
End Sub

Sub isMulticast() As Boolean
    ReflectorIGMP.RunMethod("isHeld")
    Log(ReflectorIGMP.ToString)
    
    If ReflectorIGMP.ToString.Contains("held") Then
        Log("MulticastLock current state: held")
        Return True
    Else
        Log("MulticastLock current state: not held")
        Return False
    End If
End Sub
 

rtek1000

Active Member
Licensed User
This is the code added to B4A-Bridge:
B4X:
Sub AcquireMulticastLock
   Try
       Dim ctxt As JavaObject
       ctxt.InitializeContext
       Dim WifiManager As JavaObject = ctxt.RunMethod("getSystemService", Array("wifi"))
       MulticastLock = WifiManager.RunMethodJO("createMulticastLock", Array("b4a-udp"))
       MulticastLock.RunMethod("acquire", Null)
   Catch
       Log(LastException)
   End Try
End Sub
Note that the MulticastLock must be a process global variable.
Another app gave an error message here:

(ClassCastException) java.lang.ClassCastException: android.net.wifi.WifiManager$MulticastLock cannot be cast to anywheresoftware.b4a.agraham.reflection.Reflection
B4X:
   Try
        Dim ctxt As JavaObject
        ctxt.InitializeContext
        Dim WifiManager As JavaObject = ctxt.RunMethod("getSystemService", Array("wifi"))
        MulticastLock = WifiManager.RunMethodJO("createMulticastLock", Array("mash-udp"))
        MulticastLock.RunMethod("acquire")
    Catch
        Log(LastException)
    End Try
If I use this code below, it does not error:
B4X:
    Try
       ReflectorIGMP.Target = ReflectorIGMP.GetContext
       ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("getSystemService", "wifi", "java.lang.String")
       ReflectorIGMP.Target = ReflectorIGMP.RunMethod2("createMulticastLock", "mash-udp", "java.lang.String")
       ReflectorIGMP.RunMethod("acquire")
   Catch
       Log(LastException)
   End Try
What is the best way to use this in another application?
 

rtek1000

Active Member
Licensed User
No need to post all of this code.

Use the code I posted. MulticastLock type is JavaObject.
Ok, it works, thanks,

B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private MulticastLock As JavaObject
End Sub
No need to use "release" too?

If the user decides to stop the service, can I use "release" to save the battery?
 

rtek1000

Active Member
Licensed User
In the future,

Would it be possible to add these functions to the same tooltip that the udp socket has? Or has it no direct relation?

tip tool.png
 
Top