Заставьте Android использовать 3G, когда в локальной сети Wi-Fi без доступа к сети - PullRequest
14 голосов
/ 13 января 2012

У меня есть настройка Wi-Fi, у которой нет доступа к интернету.Просто различные другие локальные устройства Wi-Fi, подключенные к нему.DHCP настроен так, чтобы не возвращать шлюз или DNS-сервер.Только IP и маска сети.

Когда я подключаю свой андроид к этой точке доступа Wi-Fi, он подключается нормально, но все интернет-соединения на телефоне перестают работать.настройка шлюза, что Android должен понимать, что Интернет не может проходить через это соединение, и вместо этого должен маршрутизироваться через 3G-соединение, которое находится на 5 барах.

Я также пытался установить статический IP на телефоне Android, но это не помогло.

Основная причина этой настройки заключается в том, что устройство Android может передавать данные в этой удаленной сети на сервер через Интернет, поскольку оно может без проблем подключаться к локальным устройствам.Однако после настройки Wi-Fi сторона 3G не работает.

Есть мысли о том, как обойти эту проблему?

Ответы [ 5 ]

5 голосов
/ 25 июня 2014

После небольшого кодирования и тестирования я объединил Squonk и это решение.Это класс, который я создал:

package it.helian.exampleprj.network;

import java.net.InetAddress;
import java.net.UnknownHostException;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.State;
import android.net.wifi.WifiManager;
import android.text.TextUtils;
import android.util.Log;

public class NetworkUtils {
    private static final String TAG_LOG = "ExamplePrj";

    Context context;
    WifiManager wifiMan = null;
    WifiManager.WifiLock wifiLock = null;

    public NetworkUtils(Context context) {
        super();
        this.context = context;
    }

    /**
     * Enable mobile connection for a specific address
     * @param context a Context (application or activity)
     * @param address the address to enable
     * @return true for success, else false
     */
    public boolean forceMobileConnectionForAddress(Context context, String address) {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (null == connectivityManager) {
            Log.d(TAG_LOG, "ConnectivityManager is null, cannot try to force a mobile connection");
            return false;
        }

        //check if mobile connection is available and connected
        State state = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
        Log.d(TAG_LOG, "TYPE_MOBILE_HIPRI network state: " + state);
        if (0 == state.compareTo(State.CONNECTED) || 0 == state.compareTo(State.CONNECTING)) {
            return true;
        }

        //activate mobile connection in addition to other connection already activated
        int resultInt = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
        Log.d(TAG_LOG, "startUsingNetworkFeature for enableHIPRI result: " + resultInt);

        //-1 means errors
        // 0 means already enabled
        // 1 means enabled
        // other values can be returned, because this method is vendor specific
        if (-1 == resultInt) {
            Log.e(TAG_LOG, "Wrong result of startUsingNetworkFeature, maybe problems");
            return false;
        }
        if (0 == resultInt) {
            Log.d(TAG_LOG, "No need to perform additional network settings");
            return true;
        }

        //find the host name to route
        String hostName = extractAddressFromUrl(address);
        Log.d(TAG_LOG, "Source address: " + address);
        Log.d(TAG_LOG, "Destination host address to route: " + hostName);
        if (TextUtils.isEmpty(hostName)) hostName = address;

        //create a route for the specified address
        int hostAddress = lookupHost(hostName);
        if (-1 == hostAddress) {
            Log.e(TAG_LOG, "Wrong host address transformation, result was -1");
            return false;
        }
        //wait some time needed to connection manager for waking up
        try {
            for (int counter=0; counter<30; counter++) {
                State checkState = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
                if (0 == checkState.compareTo(State.CONNECTED))
                    break;
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            //nothing to do
        }
        boolean resultBool = connectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
        Log.d(TAG_LOG, "requestRouteToHost result: " + resultBool);
        if (!resultBool)
            Log.e(TAG_LOG, "Wrong requestRouteToHost result: expected true, but was false");

        state = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
        Log.d(TAG_LOG, "TYPE_MOBILE_HIPRI network state after routing: " + state);

        return resultBool;
    }

    /**
     * This method extracts from address the hostname
     * @param url eg. http://some.where.com:8080/sync
     * @return some.where.com
     */
    public String extractAddressFromUrl(String url) {
        String urlToProcess = null;

        //find protocol
        int protocolEndIndex = url.indexOf("://");
        if(protocolEndIndex>0) {
            urlToProcess = url.substring(protocolEndIndex + 3);
        } else {
            urlToProcess = url;
        }

        // If we have port number in the address we strip everything
        // after the port number
        int pos = urlToProcess.indexOf(':');
        if (pos >= 0) {
            urlToProcess = urlToProcess.substring(0, pos);
        }

        // If we have resource location in the address then we strip
        // everything after the '/'
        pos = urlToProcess.indexOf('/');
        if (pos >= 0) {
            urlToProcess = urlToProcess.substring(0, pos);
        }

        // If we have ? in the address then we strip
        // everything after the '?'
        pos = urlToProcess.indexOf('?');
        if (pos >= 0) {
            urlToProcess = urlToProcess.substring(0, pos);
        }
        return urlToProcess;
    }

    /**
     * Transform host name in int value used by {@link ConnectivityManager.requestRouteToHost}
     * method
     *
     * @param hostname
     * @return -1 if the host doesn't exists, elsewhere its translation
     * to an integer
     */
    private int lookupHost(String hostname) {
        InetAddress inetAddress;
        try {
            inetAddress = InetAddress.getByName(hostname);
        } catch (UnknownHostException e) {
            return -1;
        }
        byte[] addrBytes;
        int addr;
        addrBytes = inetAddress.getAddress();
        addr = ((addrBytes[3] & 0xff) << 24)
                | ((addrBytes[2] & 0xff) << 16)
                | ((addrBytes[1] & 0xff) << 8 )
                |  (addrBytes[0] & 0xff);
        return addr;
    }

    @SuppressWarnings("unused")
    private int lookupHost2(String hostname) {
        InetAddress inetAddress;
        try {
            inetAddress = InetAddress.getByName(hostname);
        } catch (UnknownHostException e) {
            return -1;
        }
        byte[] addrBytes;
        int addr;
        addrBytes = inetAddress.getAddress();
        addr = ((addrBytes[3] & 0xff) << 24)


        | ((addrBytes[2] & 0xff) << 16)
            | ((addrBytes[1] & 0xff) << 8 )
            |  (addrBytes[0] & 0xff);
        return addr;
    }

    public Boolean disableWifi() {
        wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        if (wifiMan != null) {
            wifiLock = wifiMan.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, "HelianRCAWifiLock");
        }
        return wifiMan.setWifiEnabled(false);
    }

    public Boolean enableWifi() {
        Boolean success = false;

        if (wifiLock != null && wifiLock.isHeld())
            wifiLock.release();
        if (wifiMan != null)
        success = wifiMan.setWifiEnabled(true);
        return success;
    }
}

Это использование :

ИСПОЛЬЗОВАНИЕ КОДА

            boolean mobileRoutingEnabled = checkMobileInternetRouting();

            if(!mobileRoutingEnabled) {
                networkUtils.disableWifi();

                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            networkUtils.forceMobileConnectionForAddress(context, RCA_URL);

            if(!mobileRoutingEnabled) {
                networkUtils.enableWifi();
            }

            // This second check is for testing purpose
            checkMobileInternetRouting();

            return callWebService(RCA_COMPLETE_URL, _plate);

где checkMobileInternetRouting is:

private boolean checkMobileInternetRouting() {
    ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

    State state = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
    return 0 == state.compareTo(State.CONNECTED) || 0 == state.compareTo(State.CONNECTING);
}

ПРОЦЕДУРА ИСПОЛЬЗОВАНИЯ

  1. Проверьте, включена ли маршрутизация к хосту
  2. Если да, продолжайте общение независимо от того, есть ли WiFiподключен или нет и выполняет только точки 6 (точка 4 будет проверять только то, что маршрутизация уже включена, не выполняя какого-либо соответствующего действия).В противном случае временное отключение Wi-Fi.
  3. Потока сна около 3 секунд для восстановления соединения 3g
  4. Установите маршрутизацию 3g на указанный URL-адрес
  5. Включить обратно Wi-Fi
  6. Теперь данный URL-адрес может быть вызван даже с подключением Wi-Fi без доступа к сети

ВЫВОДЫ

Это немного странно, но работает правильно.Единственная проблема заключается в том, что у этой маршрутизации есть тайм-аут в несколько секунд (например, 20-30), что заставляет вас выполнить всю вышеуказанную процедуру еще раз.Установка этого тайм-аута на более высокое значение было бы очень хорошо.

1 голос
/ 11 февраля 2018

Google добавил для этой цели несколько полезных методов в Android SDK 21.

Вы можете создать NetworkRequest:

NetworkRequest networkRequest = new NetworkRequest.Builder()
    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    .build();

И затем вы можете запросить такую ​​сеть, используя ConnectivityManager.Например, вы хотите быть уверены, что все HTTP-запросы будут передаваться через сеть с доступом в Интернет.Вы можете создать свой Retrofit API следующим образом:

ApiConfig apiConfig;

ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        apiConfig = new Retrofit.Builder()
            .baseUrl("https://api.imatrix.io/")
            .client(new OkHttpClient.Builder()
                .socketFactory(network.getSocketFactory())
                .build())
            .build()
            .create(ApiConfig.class);
    }

    @Override
    public void onLost(Network network) {
        apiConfig = null; 
    }
});

Обратите внимание на безопасность потоков, когда вы используете такой фрагмент кода.

Кроме того, я предлагаю проверить ConnectivityManager # bindProcessToNetwork и этот блог .

ConnectivityManager.NetworkCallback является пустым классом и имеет несколько методов .

0 голосов
/ 27 февраля 2015

вам не нужно ничего кодировать. Я нашел приложение, которое делает именно эту вещь. Вы можете настроить автоматическое отключение от Wi-Fi, если нет подключения к Интернету.

https://play.google.com/store/apps/details?id=com.nLabs.internetconnectivity&hl=en

0 голосов
/ 13 января 2012

Я не могу гарантировать, что это сработает, так как я экспериментировал только некоторое время назад.У меня была аналогичная необходимость использовать 3G (или другую мобильную сеть), когда сеть, подключенная к Wi-Fi, не имела маршрута к внешнему миру.

Следующий код должен разорвать соединение Wi-Fi, чтобы позволить мобильной сетивступают в игру.Вам нужно будет выполнить различные тесты и заново установить соединение Wi-Fi после этого ...

WifiManager wifiMan = null;
WifiManager.WifiLock wifiLock = null;

private Boolean disableWifi() {
    wifiMan = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    if (wifiMan != null) {
        wifiLock = wifiMan.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, "MyWifiLock");
    }
    return wifiMan.setWifiEnabled(false);
}

private Boolean enableWifi() {
    Boolean success;

    if (wifiLock != null)
        wifiLock.release();
    if (wifiMan != null)
        success = wifiMan.setWifiEnabled(true);
    return success;
}
0 голосов
/ 13 января 2012

Из кода, когда вы обнаружите, что нет соединения, вы можете отключить WiFi ...

Что касается настройки, то ее нет (нет хорошего способа проверить, действительно ли существует универсальность и надежность соединения).Но некоторые телефоны выполняют то, что вы описываете автоматически, например, мой LG P-970.

(Примечание: Android отключается от мобильных сетей при подключении к WiFi, поэтому нет возможности подключиться кWiFi, но маршрутизировать доступ в Интернет через мобильный телефон, хотя Linux может это сделать (с помощью набора инструментов ip route ...)

...