Отправить данные через Wi-Fi (без интернета), когда мобильные данные на

Я разрабатываю и приложение, которое подключается к аппаратному устройству через Wi-Fi (генерируется устройством) и отправляет данные на него через сокет-соединение. Проблема в том, что при активации мобильных данных (3G/4G) Android пытается отправить данные через него, а не через Wi-Fi, сгенерированный устройством, потому что Wi-Fi не имеет подключения к Интернету. Я думал об использовании ConnectivityManager#setNetworkPreference(), но это устарело в API 21.

Как я могу настроить его для отправки данных, используя Wi-Fi, сгенерированный устройством вместо мобильного интерфейса данных?

2 ответа

Решение

После долгих исследований и попыток понять все это я обнаружил, что это невозможно в версиях, предшествующих Android 5.0 (Lollipop), ОС поддерживает только один сетевой интерфейс за раз, а приложения не имеют контроля через это.

Таким образом, чтобы сделать это на версиях, больших или равных Lollipop, я сделал следующее:

  1. Перед выполнением подключения к сокету проверьте, больше ли Android или равен Lollipop, если это не вы просто делаете то, что обычно делаете;
  2. Если версия Lollipop равна или больше, вам нужно сделать что-то вроде этого:

    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. После того, как вы закончите, вы должны прекратить связывать процесс с этим:

    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);
    }
    

Этот ответ помог мне понять, как это сделать /questions/15331294/kak-ostavatsya-na-svyazi-cherez-mobilnuyu-set-posle-podklyucheniya-wifi-na-android/15331302#15331302.

Сеть переключится обратно на мобильную сеть после того, как setProcessDefaultNetwork перейдет на Wi-Fi. Таким способом невозможно отправить большие данные, поскольку все созданные таким образом сокеты перестанут работать. Я не нахожу идеальный способ сейчас, здесь только для справки.

1. Если вы можете получить root-доступ, сделайте это перед подключением к wifi:

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();

И удалите его после отправки данных:

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

2.Если вы можете получить разрешение WRITE_SECURE_SETTINGS:

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

Или же:

Settings.Global.putInt(getContentResolver(),"captive_portal_detection_enabled",0);
Другие вопросы по тегам