Un'app Android non riesce a risolvere un nome interno mentre il browser del client VPN stesso lo raggiunge, e la stessa build funziona su un altro telefono. L'URL non è la variabile. La variabile è quale rete il processo dell'app può usare per la risoluzione, e Android non garantisce che la VPN attiva sia quella che usano i vostri socket. Eseguite questi controlli in ordine e fermatevi quando l'istantanea del resolver non concorda con la vostra ipotesi.

Passo 1: dimostrate su quale rete si trova l'app

Leggete la vista dell'app stessa, non la barra di stato. Da ConnectivityManager ricavate la rete attiva, i suoi LinkProperties, l'elenco dei server DNS e l'impostazione DNS privato. Se l'elenco contiene il resolver della VPN, l'app ha un percorso verso di esso; se contiene il resolver del Wi-Fi, il tunnel non è la rete predefinita per quel processo.

Un'icona VPN dice solo che esiste un tunnel. Non dice che il processo dell'app vi sia stato associato, e Android non instrada sempre la risoluzione dei nomi attraverso la rete che sembra attiva. Le app VPN locali che inoltrano a un altro dispositivo, le reti Wi-Fi senza internet e le build dei produttori possono lasciare l'app a interrogare il resolver predefinito invece dei server del tunnel.

Passo 2: richiedete il trasporto VPN e collegate il processo

Quando l'app deve usare il resolver del tunnel, richiedete una rete con trasporto VPN e collegate il processo nella callback, così il codice non indovina quando compare la rete:

val request = NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    .build()

connectivityManager.requestNetwork(request, object : NetworkCallback() {
    override fun onAvailable(network: Network) {
        val bound = connectivityManager.bindProcessToNetwork(network)
        Log.i(TAG, "bound to VPN network: " + bound)
    }
})

La chiamata cambia la rete predefinita dell'intero processo, quindi ogni socket creato dopo la usa. I socket già esistenti mantengono la rete su cui sono stati creati, ed è per questo che un pool di connessioni aperto prima del binding può continuare a risolvere attraverso il vecchio percorso. Il valore restituito è un boolean, e un risultato falso significa che il binding non è avvenuto; riprovate con un breve ritardo e registrate entrambi i tentativi.

Passo 3: limitate il binding a ciò che ne ha bisogno

Il binding sull'intero processo è grossolano. Telemetria, controlli degli aggiornamenti e SDK di terze parti passano anch'essi al tunnel, e se il tunnel non ha internet iniziano a fallire. Per una singola richiesta usate la socket factory della rete e lasciate il predefinito del dispositivo per tutto il resto. Scegliete un modello di proposito: un binding ristretto per connessione, o un binding di processo con unbind esplicito quando la VPN scompare.

Due funzionalità del resolver possono sconfiggere il binding senza alcun errore. Il DNS privato in modalità rigorosa invia le query a un server DoT con nome invece che al resolver della rete, e un'app che include un proprio client DNS-over-HTTPS aggira del tutto il resolver di sistema. Controllate entrambe prima di concludere che il binding è fallito. Una query che non raggiunge mai il server DNS della VPN non apparirà in nessun log lato tunnel.

Passo 4: non fidatevi di un vincolo CONNECTED

NetworkType.CONNECTED di WorkManager significa che una rete è connessa, e il trasporto VPN lo soddisfa anche quando il collegamento Wi-Fi o cellulare sottostante è sparito. Il job gira, ogni richiesta fallisce e la politica di retry lo trasforma in una tempesta che sembra un bug di pianificazione. Il tunnel è una rete; il vincolo non è mai stato un controllo di internet.

Controllate la capacità di cui il lavoro ha davvero bisogno, come l'accesso a internet convalidato, e mantenete una politica di retry limitata con backoff. Se il job ha bisogno di un nome privato, risolvete quel nome nella rete selezionata prima di svolgere il lavoro, e fallite presto con un errore chiaro invece di ripetere una richiesta che non può riuscire.

Passo 5: registrate le prove e ritestate la gamma

Registrate i tipi di trasporto della rete selezionata, i server DNS da LinkProperties, se il binding ha restituito true e l'esatto fallimento della prima richiesta. Poi eseguite un nome privato e un nome pubblico sotto lo stesso binding. Quella coppia mostra se l'app ha usato il resolver del tunnel o è ricaduta silenziosamente sul predefinito del dispositivo, ed è la differenza che l'utente sperimenta.

Ripetete sulla versione di Android più vecchia che supportate e su una release attuale. Il comportamento del resolver che fa sembrare corretto il percorso predefinito è esattamente dove dispositivi e versioni differiscono, e una build che passa solo sul dispositivo di test più recente non è verificata per la gamma che distribuite. adb shell dumpsys connectivity è un modo rapido per confermare quali reti e capacità il dispositivo riporta mentre riproducete il guasto.

Articoli correlati