共用方式為


ConnectivityManager in Android SDK: Compared with Connection Manager in Windows Mobile

Anyone who has ever used Windows Mobile (actually WinCE) Connection Manager will probably share mix feelings: on one side, the ConnMgr APIs provide a nice abstraction for applications to request and use a connection without knowing and controlling the physical connectivity; on the other side, it has been a pain in the neck to override or bypass Connection Manager's connection selection logic if the application should use a specific physical connection (for example, GPRS rather than WiFi). And the concept of metanetworks, Internet, Work, WAP network, Secured WAP, is indeed very confusing and difficult to manipulate.

So how does Android manage connections?

In Android SDK 1.5 (code named cupcake),  the android.net.ConnectivityManager class (this is Java, not C++) provides connection state management and notification functionality. Specifically, it can be used to:

  1. Obtain broadcast intents when network connectivity changes by checking ConnectivityManager.CONNECTIVITY_ACTION and ConnectivityManager.EXTRA_NETWORK_INFO in a class extended from BroadcastReceiver. (For a brief introduction to Android application fundamentals, read this document. An example of network connectivity listener can be found here.
  2. Query the coarse-grained or fine-grained state of the available networks using ConnectivityManager.GetActiveNetworkInfo() or ConnectivityManager.GetNetworkInfo(int networkType).

This is same-old same-old as in Windows Mobile, which provides similar APIs like ConnMgrRegisterStatusNotification(), ConnMgrConnectionStatus, and ConnMgrQueryDetailedStatus() (examples of these APIs can be found in WM SDK cmhelper).

However, the ConnectivityManager class does offer an interesting method called requestRouteToHost():

public boolean requestRouteToHost (int networkType, int hostAddress)

Since: API Level 1

Ensure that a network route exists to deliver traffic to the specified host via the specified network interface. An attempt to add a route that already exists is ignored, but treated as successful.

Parameters
networkType the type of the network over which traffic to the specified host is to be routed
hostAddress the IP address of the host to which the route is desired
Returns
  • true on success, false on failure

 This method is very useful--it allows your application to ensure that a desired physical connectivity (represented by the networkType) is available to reach a specific destination. If a route to the destination IP address from the desired network type is in place (in the routing table), then it simply returns success. Otherwise, it will try to make that happen--this is not mentioned in the above description but can be traced to ConnectivityService class:

In ConnectivityManager class where mService is a ConnectivityService object:

/**
     * Ensure that a network route exists to deliver traffic to the specified
     * host via the specified network interface.
     * @param networkType the type of the network over which traffic to the specified
     * host is to be routed
     * @param hostAddress the IP address of the host to which the route is desired
     * @return {@code true} on success, {@code false} on failure
     */
    public boolean requestRouteToHost(int networkType, int hostAddress) {
        enforceChangePermission();
        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
            return false;
        }
        NetworkStateTracker tracker = mNetTrackers[networkType];
        /*
         * If there's only one connected network, and it's the one requested,
         * then we don't have to do anything - the requested route already
         * exists. If it's not the requested network, then it's not possible
         * to establish the requested route. Finally, if there is more than
         * one connected network, then we must insert an entry in the routing
         * table.
         */
        if (getNumConnectedNetworks() > 1) {
            return tracker.requestRouteToHost(hostAddress);
        } else {
            return tracker.getNetworkInfo().getType() == networkType;
        }
    }

The mNetTrackers array contains NetworkStateTracker objects for different network types (mobile data and WiFi).

In MobileDataStateTracker class, which extends from NetworkStateTracker:

/**
     * Ensure that a network route exists to deliver traffic to the specified
     * host via the mobile data network.
     * @param hostAddress the IP address of the host to which the route is desired,
     * in network byte order.
     * @return {@code true} on success, {@code false} on failure
     */
    @Override
    public boolean requestRouteToHost(int hostAddress) {
        if (mInterfaceName != null && hostAddress != -1) {
            if (DBG) {
                Log.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress));
            }
            return NetworkUtils.addHostRoute(mInterfaceName, hostAddress) == 0;
        } else {
            return false;
        }
    }

Notice the NetworkUtils.addHostRoute() call. This will add a host route into the system's routing table for the specified destination address via the mobile WWAN interface. This enables the scenario where you want your MMS go through a specific APN for MMS while other traffic on another APN. As of Android SDK 1.5, only MobileDataStateTracker class will actually does this--the WiFiStateTracker class does not do anything yet at this point.

Comments

  • Anonymous
    October 01, 2009
    Mr Zhang, thank you for the useful article.  However, I'm not sure that it is possible for both Mobile Data and Wifi Data connections to exist simultaneously.  The code in ConnectivityService.enforcePreference shuts down the non-preferred network when both are up. As a simple test, I enabled Wifi in the control panel settings, then called:     mConnMgr.requestRouteToHost(ConnectivityManager.TYPE_MOBILE, addr) After doing this, I made a simple HttpClient request to addr, but it still originated from the mobile interface. Have you tested your method on a G1?  If so, can you explain what you may have done differently? THanks!

  • Anonymous
    October 02, 2009
    Not sure I understand your question... You called mConnMgr.requestRouteToHost(ConnectivityManager.TYPE_MOBILE, addr) to route your IP traffic to addr via the mobile data connection. Then you call HttpClient to addr, and it used the mobile data connection. Anything wrong?

  • Anonymous
    February 10, 2010
    Hello, I think the original commenter was trying to understand if requestRouteToHost(ConnectivityManager.TYPE_MOBILE, someIPAddress) would allow him to connect through the mobile route WITHOUT forcing the Wi-Fi to close. Its a big problem not having mobile and Wi-Fi connectivity at the same time, especially for developers trying to create services which need mobile operator connectivity (to identify the user).