8

Sto sviluppando e l'applicazione che si connette a un dispositivo hardware tramite Wi-Fi (generato dal dispositivo) e invia i dati ad esso tramite una connessione socket. Il problema è che quando si attivano i dati mobili (3G/4G), Android tenta di inviare i dati attraverso di essa invece di inviarli tramite il wifi generato dal dispositivo, perché il wifi non ha una connessione internet. Stavo pensando di usare ConnectivityManager#setNetworkPreference() ma è stato deprecato in api 21.Invia dati tramite Wi-Fi (senza Internet) quando i dati mobili su

Come posso impostarlo per inviare dati utilizzando il wifi generato dal dispositivo invece dell'interfaccia dati mobile?

risposta

9

Dopo molte ricerche e tempo cercando di capirlo tutto ho scoperto che questo non è possibile nelle versioni precedenti ad Android 5.0 (Lollipop), il sistema operativo mantiene una sola interfaccia di rete alla volta e le app non ho il controllo su questo.

Quindi, per fare questo su versioni maggiori o uguali al Lollipop ho fatto la seguente:

  1. Prima di fare il vostro check connessione socket se maggiore o uguale a Android Lollipop, nel caso in cui non basta fare qualunque cosa tu debba fare normalmente;
  2. Nel caso in cui la versione è uguale o superiore a Lollipop è necessario fare qualcosa di simile:

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { 
        final ConnectivityManager manager = (ConnectivityManager) context 
          .getSystemService(Context.CONNECTIVITY_SERVICE); 
        NetworkRequest.Builder builder; 
        builder = new NetworkRequest.Builder(); 
        //set the transport type do WIFI 
        builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); 
        manager.requestNetwork(builder.build(), new ConnectivityManager.NetworkCallback() { 
         @Override 
         public void onAvailable(Network network) { 
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
           manager.bindProcessToNetwork(network); 
          } else { 
           //This method was deprecated in API level 23 
           ConnectivityManager.setProcessDefaultNetwork(network); 
          } 
          try { 
           //do a callback or something else to alert your code that it's ok to send the message through socket now 
          } catch (Exception e) { 
           e.printStackTrace(); 
          } 
          manager.unregisterNetworkCallback(this); 
         } 
        }); 
    } 
    
  3. Al termine si dovrebbe smettere di legare il processo con questo:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
        ConnectivityManager manager = (ConnectivityManager) InovePlugApplication.getContext() 
          .getSystemService(Context.CONNECTIVITY_SERVICE); 
        manager.bindProcessToNetwork(null); 
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
        ConnectivityManager.setProcessDefaultNetwork(null); 
    } 
    

Questa risposta mi ha aiutato a capire come farlo https://stackoverflow.com/a/29837637/2550932.

+0

Avrai bisogno di '' nel tuo manifest affinché funzioni. – AppleGrew

+0

@hygorxaraujo Sto usando il tuo codice, ma questo non funziona per Android Torrone. Sto facendo manager.bindProcessToNetwork (rete) per Marshamallow e superiore. c'è qualche soluzione? –

+0

@ satpal002 scusate, ho usato questo codice solo con Android Marshmallow (e non ho un dispositivo con Nougat per testarlo). Non ho notato alcun cambiamento nell'API perché smettesse di funzionare https://developer.android.com/reference/android/net/ConnectivityManager.html. Funziona normalmente nelle versioni precedenti (sopra Lollipop)? – hygorxaraujo

0

La rete tornerà al cellulare dopo setProcessDefaultNetwork su wifi, non è possibile inviare grandi dati in questo modo, poiché tutti i socket creati in questo modo cesseranno di funzionare. Non trovo un modo perfetto ora, qui è solo per riferimento.

1. Se è possibile ottenere l'accesso root, fare questo prima di connettersi al wi-fi:

Process process = runtime.exec("su"); 
DataOutputStream dataOutputStream = new DataOutputStream(process.getOutputStream()); 
dataOutputStream.writeBytes("settings put global captive_portal_server 127.0.0.1\n"); 
dataOutputStream.writeBytes("exit\n"); 
dataOutputStream.flush(); 
int status = process.waitFor(); 

e cancellarlo dopo aver inviato i dati:

dataOutputStream.writeBytes("settings delete global captive_portal_server\n"); 

2. Se è possibile ottenere il permesso WRITE_SECURE_SETTINGS :

Settings.Global.putString(getContentResolver(),"captive_portal_server","127.0.0.1"); 

Oppure:

Settings.Global.putInt(getContentResolver(),"captive_portal_detection_enabled",0); 
+0

Ciao @MADAO, grazie per la risposta, penso che anche tu abbia ragione. Avevo già trovato una risposta a questo e ho dimenticato di aggiornare la domanda qui. – hygorxaraujo