Mandatory Request Network in Android Multi-Network Environment (wifi,mobile)

Posted by Dowdy on Tue, 25 Jun 2019 00:07:40 +0200

This function is described in the title: under the simultaneous opening of wifi and mobile data network, the system did not provide such APIs to achieve this function well before Android 5.0. Now, when wifi is on, forcing network requests to be sent through the mobile data network may feel that there is no such need. I think that as long as you can access the network, and specifically mobile network, then I can only say that there is no such need in your business development. Well, there is no more nonsense to be said, and the realization is as follows:

 @TargetApi(21)
    private void forceSendRequestByMobileData() {
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkRequest.Builder builder = new NetworkRequest.Builder();
        builder.addCapability(NET_CAPABILITY_INTERNET);
        //Mandatory use of cellular data networks - Mobile Data
        builder.addTransportType(TRANSPORT_CELLULAR);
        NetworkRequest build = builder.build();
        connectivityManager.requestNetwork(build, new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                super.onAvailable(network);
                try {
                    URL url = new URL("");
                    HttpURLConnection connection = (HttpURLConnection)network.openConnection(url);
                    /*******Omitting parameter configuration*******/
                    connection.connect();
                    /*******Data Flow Processing*******/
                } catch (Exception e) {

                }

            }
        });
    }

among
builder.addCapability(NET_CAPABILITY_INTERNET);
// Mandatory use of cellular data networks - Mobile Data
builder.addTransportType(TRANSPORT_CELLULAR);
By configuring the TransportType parameter TRANSPORT_CELLULAR to specify the mobile network, its value can be as follows: in the mobile data network, wifi, bluetooth, ethernet, Vpn and other five transmission channels. At this time, no matter how many networks are connected in the current mobile phone, you can specify a single one.

 /**
     * Indicates this network uses a Cellular transport.
     */
    public static final int TRANSPORT_CELLULAR = 0;

    /**
     * Indicates this network uses a Wi-Fi transport.
     */
    public static final int TRANSPORT_WIFI = 1;

    /**
     * Indicates this network uses a Bluetooth transport.
     */
    public static final int TRANSPORT_BLUETOOTH = 2;

    /**
     * Indicates this network uses an Ethernet transport.
     */
    public static final int TRANSPORT_ETHERNET = 3;

    /**
     * Indicates this network uses a VPN transport.
     */
    public static final int TRANSPORT_VPN = 4;

The parameter configuration of addCapability(NET_CAPABILITY_INTERNET) mentioned above is also optional in a fixed range and can not be mismatched. There are 19 parameters available:

    public static final int NET_CAPABILITY_MMS            = 0;
    public static final int NET_CAPABILITY_SUPL           = 1;
    public static final int NET_CAPABILITY_DUN            = 2;
    public static final int NET_CAPABILITY_FOTA           = 3;
    public static final int NET_CAPABILITY_IMS            = 4;
    public static final int NET_CAPABILITY_CBS            = 5;
    public static final int NET_CAPABILITY_WIFI_P2P       = 6;
    public static final int NET_CAPABILITY_IA             = 7;
    public static final int NET_CAPABILITY_RCS            = 8;
    public static final int NET_CAPABILITY_XCAP           = 9;
    public static final int NET_CAPABILITY_EIMS           = 10;
    public static final int NET_CAPABILITY_NOT_METERED    = 11;

    /**
     * Indicates that this network should be able to reach the internet.
     */
    public static final int NET_CAPABILITY_INTERNET       = 12;
    public static final int NET_CAPABILITY_NOT_RESTRICTED = 13;
    public static final int NET_CAPABILITY_TRUSTED        = 14;
    public static final int NET_CAPABILITY_NOT_VPN        = 15;
    public static final int NET_CAPABILITY_VALIDATED      = 16;
    public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17;
    public static final int NET_CAPABILITY_FOREGROUND = 18;

It has the following configuration capabilities by default, so to access the network you need to add NET_CAPABILITY_INTERNET

private static final long DEFAULT_CAPABILITIES =
            (1 << NET_CAPABILITY_NOT_RESTRICTED) |
            (1 << NET_CAPABILITY_TRUSTED) |
            (1 << NET_CAPABILITY_NOT_VPN);

If the netWork is available after the Connectivity Manager. Request Network request, the onAvailable (Network) function in the Connectivity Manager. NetworkCallback will be called back. At this time, the returned Network is used to connect the network.openConnection(url) to http. Its source code is as follows:

 public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
            int timeoutMs, int legacyType) {
        sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, REQUEST,
                legacyType);
    }
private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
            NetworkCallback networkCallback, int timeoutSec, int action,
            int legacyType) {
        if (networkCallback == null) {
            throw new IllegalArgumentException("null NetworkCallback");
        }
        if (need == null && action != REQUEST) {
            throw new IllegalArgumentException("null NetworkCapabilities");
        }
        try {
            incCallbackHandlerRefCount();
            synchronized(sNetworkCallback) {
                if (action == LISTEN) {
                    networkCallback.networkRequest = mService.listenForNetwork(need,
                            new Messenger(sCallbackHandler), new Binder());
                } else {
                    networkCallback.networkRequest = mService.requestNetwork(need,
                            new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
                }
                if (networkCallback.networkRequest != null) {
                    sNetworkCallback.put(networkCallback.networkRequest, networkCallback);
                }
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        if (networkCallback.networkRequest == null) decCallbackHandlerRefCount();
        return networkCallback.networkRequest;
    }

Introducing the use is so simple, but it will not be so perfect in practice, the first thing to think about is the adaptation situation.

  • Adapter type problem
    Op, millet and Meizu are found to be able to achieve this function in several brands of mobile phones, but onAvailable (Network) can return to the network in the callback of Huawei p-Series p7 and Glory mobile phones, and can also be connected, but there has been a timeout when reading the network results.

  • Adapt to Low Version
    The above mentioned also mentioned that before 5.0 to achieve such a function, in the low version you can use the startUsingNetwork Feature (String feature) abandoned method to achieve, in fact, the same principle as the above implementation, but the use of performance and efficiency issues, the old Api can not immediately use mobile networks for requests.

    public int startUsingNetworkFeature(int networkType, String feature) {
        checkLegacyRoutingApiAccess();
        NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
        if (netCap == null) {
            Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " +
                    feature);
            return PhoneConstants.APN_REQUEST_FAILED;
        }

        NetworkRequest request = null;
        synchronized (sLegacyRequests) {
            LegacyRequest l = sLegacyRequests.get(netCap);
            if (l != null) {
                Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest);
                renewRequestLocked(l);
                if (l.currentNetwork != null) {
                    return PhoneConstants.APN_ALREADY_ACTIVE;
                } else {
                    return PhoneConstants.APN_REQUEST_STARTED;
                }
            }

            request = requestNetworkForFeatureLocked(netCap);
        }
        if (request != null) {
            Log.d(TAG, "starting startUsingNetworkFeature for request " + request);
            return PhoneConstants.APN_REQUEST_STARTED;
        } else {
            Log.d(TAG, " request Failed");
            return PhoneConstants.APN_REQUEST_FAILED;
        }
    }

Continue with the request Network ForFeature Locked (netCap) implementation

    private NetworkRequest requestNetworkForFeatureLocked(NetworkCapabilities netCap) {
        int delay = -1;
        int type = legacyTypeForNetworkCapabilities(netCap);
        try {
            delay = mService.getRestoreDefaultNetworkDelay(type);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        LegacyRequest l = new LegacyRequest();
        l.networkCapabilities = netCap;
        l.delay = delay;
        l.expireSequenceNumber = 0;
        l.networkRequest = sendRequestForNetwork(netCap, l.networkCallback, 0,
                REQUEST, type);
        if (l.networkRequest == null) return null;
        sLegacyRequests.put(netCap, l);
        sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay);
        return l.networkRequest;
    }

You can see that it eventually goes to the lsendRequest ForNetwork (netCap, L. networkCallback, 0, REQUEST, type); this method and the request network (NetworkRequest request, NetworkCallback networkCallback) after 5.0 are an effect.

  • Some might even think of disconnecting wifi and accessing it through mobile networks, as follows:
ConnectivityManager cm = (ConnectivityManager)Context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if(ni == null)
  //no connectivity, abort
if(ni.getType() == ConnectivityManager.TYPE_WIFI || ni.getType() == ConnectivityManager.TYPE_WIMAX) {
  WifiManager wm = (WifiManager)Context.getSystemService(Context.WIFI_SERVICE);
  if( wm != null)
    wm.disconnect();
  //this will force android to fallback to other available n/w which is 3G
}
while(true) {
  NetworkInfo ni = cm.getActiveNetworkInfo();
  if( ni != null && ni.getType() == ConnectivityManager.TYPE_MOBILE && ni.isConnected()) {
    //send your http request
    break;
  }
  //sleep for some time, so that android can connect you to other n/w
}

But it's not easy to use. Many mobile phones can't realize switch wifi and mobile network through code api. Comparatively speaking, it's more suitable to use the former.

Topics: network Mobile Android VPN