首页 文章

强制Android在局域网wifi上使用3G而无需网络访问

提问于
浏览
14

我有一个无线局域网设置,没有互联网接入 . 只是连接到它的各种其他本地WiFi设备 . DHCP配置为不返回网关或DNS服务器 . 只有IP和网络掩码 .

当我将我的机器人连接到这个无线网络AP它连接正常,但手机上的所有互联网连接都停止工作 .

我希望由于wifi没有网关设置,android应该意识到互联网无法通过该连接,而应该通过3G连接进行路由 .

我也尝试在Android手机上设置静态IP,但这没有用 .

这种设置的主要原因是Android设备可以将这个远程网络上的数据传输到基于互联网的服务器,因为它可以毫无问题地连接到本地设备 . 然而,一旦设置了wifi,3G侧就会被打破 .

关于如何解决这个问题的任何想法?

5 回答

  • 5

    经过一些编码和测试后,我合并了Squonk和this解决方案 . 这是我创建的类:

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

    这是 usage

    使用代码

    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 是:

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

    使用程序

    • 检查是否启用了到主机的路由

    • 如果是,则无论是否连接了wifi,都要进行通信,并且仅执行第6点(第4点将仅检查路由是否已启用而不执行任何rilevant操作) . 否则临时禁用wifi .

    • 线程休眠约3秒,让3g连接回来

    • 将3g路由设置为给定的URL

    • 启用wifi

    • 现在即使没有网络访问的wifi连接也可以调用给定的URL

    结论

    这有点hacky但工作正常 . 唯一的问题是这个路由有几秒的超时(如20-30),迫使你再次执行整个上述过程 . 将此超时设置为更高的值将非常好 .

  • -1

    从代码中,当您检测到没有连接时,您可以关闭WiFi ...

    至于设置,没有(没有好的方法来检查是否确实存在普遍可靠的连接) . 但有些手机会自动执行您所描述的内容,例如我的LG P-970 .

    (注意:Android在连接到WiFi时会断开与移动网络的连接,因此无法通过移动设备连接到WiFi但通过移动设备进行互联网访问,即使Linux可以这样做(使用 ip route ... 工具套件))

  • 0

    我无法保证这会有效,因为这是我前一段时间才进行的实验 . 当无线连接网络无法通往外界时,我也有类似的需要使用3G(或其他移动网络) .

    以下代码应删除wifi连接,以允许移动网络进入播放 . 你需要在整个过程中进行各种测试,然后再重新 Build wifi连接......

    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

    为此,Google在Android SDK 21中添加了一些有用的方法 .

    您可以创建 NetworkRequest

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

    然后你可以使用 ConnectivityManager 请求这样的网络 . 例如,您希望确保所有HTTP请求都将通过具有Internet访问权限的网络传递 . 您可以通过以下方式构建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#bindProcessToNetworkblog .

    ConnectivityManager.NetworkCallback 是一个空类,它有several methods .

  • 0

    你不需要编码任何东西 . 我找到了一个完全正确的应用程序 . 如果此连接没有互联网,您可以配置为自动断开与wifi的连接 .

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

相关问题