@@ -132,27 +132,44 @@ public IEnumerator ConnectSingleClient_WebSocket_IPAddressAndPath()
132132#if HOSTNAME_RESOLUTION_AVAILABLE
133133 // Check connection with a single client (hostname).
134134 [ UnityTest ]
135- [ UnityPlatform ( exclude = new [ ] { RuntimePlatform . Android } ) ] // Test fails on Android for editors 6000.3+ Tracked in MTT-14757
136135 public IEnumerator ConnectSingleClient_Hostname ( )
137136 {
138137 InitializeTransport ( out m_Server , out m_ServerEvents ) ;
139138 InitializeTransport ( out m_Clients [ 0 ] , out m_ClientsEvents [ 0 ] ) ;
140139
141- // We don't know if localhost will resolve to 127.0.0.1 or ::1, so we wait until we know
142- // before starting the server. Because localhost is pretty much always defined locally
143- // it should resolve immediatly and thus waiting one frame should be enough.
140+ // We don't know if localhost will resolve to 127.0.0.1 or ::1 (or even a device LAN
141+ // IP on some Android versions), so we wait until we know before starting the server.
142+ // We poll until GetLocalEndpoint() returns a valid endpoint rather than assuming one
143+ // frame is always enough — resolution may span multiple driver updates on some platforms.
144144
145145 // We'll need to retry connection requests most likely so make this fast.
146146 m_Clients [ 0 ] . ConnectTimeoutMS = 50 ;
147147
148148 m_Clients [ 0 ] . SetConnectionData ( "localhost" , 7777 ) ;
149149 m_Clients [ 0 ] . StartClient ( ) ;
150150
151- yield return null ;
152-
153- var endpoint = m_Clients [ 0 ] . GetLocalEndpoint ( ) ;
154- var ip = endpoint . Family == NetworkFamily . Ipv4 ? "127.0.0.1" : "::1" ;
155- m_Server . SetConnectionData ( ip , 7777 , ip ) ;
151+ // Wait until hostname resolution has completed and the driver has bound.
152+ // On some Android devices "localhost" can resolve to the device's LAN IP rather than
153+ // a loopback address, so we use the actual resolved address instead of hardcoding
154+ // "127.0.0.1" or "::1" based solely on the address family.
155+ NetworkEndpoint endpoint ;
156+ var resolutionDeadline = Time . realtimeSinceStartup + 2f ;
157+ do
158+ {
159+ yield return null ;
160+ endpoint = m_Clients [ 0 ] . GetLocalEndpoint ( ) ;
161+ } while ( endpoint . Family == NetworkFamily . Invalid &&
162+ Time . realtimeSinceStartup < resolutionDeadline ) ;
163+
164+ Assert . AreNotEqual ( NetworkFamily . Invalid , endpoint . Family ,
165+ "Timed out waiting for localhost hostname resolution to complete." ) ;
166+
167+ // Use the wildcard listen address for the resolved address family. This handles
168+ // cases where "localhost" resolves to a non-loopback address (e.g. a device's LAN
169+ // IP on some Android versions) and avoids relying on the exact IP in the local
170+ // endpoint (which may be a wildcard 0.0.0.0 when UTP binds before routing).
171+ var listenAddress = endpoint . Family == NetworkFamily . Ipv6 ? "::" : "0.0.0.0" ;
172+ m_Server . SetConnectionData ( listenAddress , 7777 , listenAddress ) ;
156173 m_Server . StartServer ( ) ;
157174
158175 yield return WaitForNetworkEvent ( NetworkEvent . Connect , m_ClientsEvents [ 0 ] ) ;
0 commit comments