11

Update: it does work: TRANSPORT_CELLULAR connections are only available when TRANSPORT_WIFI is disconnected. You will not receive callbacks when WiFi is enabled unless you have enabled simultaneous cellular data in the phone's Developer Settings.


I'm trying to receive callbacks whenever the phone's cellular data connection changes. I essentially need to know when it turns off and when it turns on.

I used to do this with the TelephonyManager and PhoneStateListener.LISTEN_SERVICE_STATE, but now I want to use the ConnectionManager with registerNetworkCallback.

The following code sample has been tried on an Android Emulator (API 28), A HUAWEI P20 Lite (API 26), an HTC One M8 (API 23), and a Motorola G6 (API 26). None of them call any callbacks when I switch mobile data on/off, or activate airplane mode.

People say this should work.

    NetworkRequest.Builder builder = new NetworkRequest.Builder();

    builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
    builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);

    connManager.registerNetworkCallback(builder.build(), new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            Log.d("Conman Test", "onAvailable");
        }

        @Override
        public void onLost(Network network) {
            Log.d("Conman Test", "onLost");
        }

        @Override
        public void onUnavailable() {
            Log.d("Conman Test", "onUnavailable");
        }

        @Override
        public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
            Log.d("Conman Test", "onCapabilitiesChanged");
        }

        @Override
        public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
            Log.d("Conman Test", "onLinkPropertiesChanged");
        }
    });

I have tried NetworkCapabilities.TRANSPORT_WIFI, and it works fine on all the devices. So why not NetworkCapabilities.TRANSPORT_CELLULAR?

Chris Watts
  • 6,197
  • 7
  • 49
  • 98

1 Answers1

-2

I haven't tried to solve this problem using NetworkCapabilities , but I used an alternative approach to determine whether my device is connected via cellular or wifi.

I just registered a receiver

  <receiver android:name=".NetworkChangeReceiver" >
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
  </receiver>

I added the permission

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

And then I did the following

public class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager cm =
                (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        boolean isConnected = activeNetwork != null &&
                activeNetwork.isConnectedOrConnecting();

        if(activeNetwork ==null){
            Log.d("Connectivity","Not connected");
            return;
        }

        boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;

        if(isConnected  && isWiFi){
            Log.d("Connectivity","WIFI");
        }else{
            Log.d("Connectivity","CELLULAR");
        }
    }
}

For Oreo device you may need to register receiver in activity or service rather than manifest

As an example

public class MainActivity extends Activity {

    IntentFilter filter1;

    @Override
    public void onCreate() {
        filter1 = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
        registerReceiver(myReceiver, filter1);
    }

    //The BroadcastReceiver that listens for network change
    private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction().equalsIgnoreCase("android.net.conn.CONNECTIVITY_CHANGE")) {
                Log.d(TAG,"connectivity change");

             ConnectivityManager cm =
            (ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        boolean isConnected = activeNetwork != null &&
                activeNetwork.isConnectedOrConnecting();

        if(activeNetwork ==null){
            Log.d("Connectivity","Not connected");
            return;
        }

        boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;

        if(isConnected  && isWiFi){
            Log.d("Connectivity","WIFI");
        }else{
            Log.d("Connectivity","CELLULAR");
        }
            }
        }
    };

    @Override
    public void onDestroy() {
        unregisterReceiver(myReceiver);
    }
}
Mithun Sarker Shuvro
  • 3,902
  • 6
  • 32
  • 64
  • Thanks, this is similar to the way I used to do it. I'll just check if this works with API 28. – Chris Watts Sep 20 '18 at 10:35
  • It won't working since in oreo make limitations on system event actions, and it will not trigger your receiver. To make it works, need to register dynamically that receiver in service – HeyAlex Sep 21 '18 at 08:33
  • This might not work on Oreo , you need to register receiver in activity or service rather than manifest . I have edited my answer. – Mithun Sarker Shuvro Sep 21 '18 at 21:17
  • Just tried this, and it says `NetworkInfo.isConnectedOrConnecting()` is now depracated. From the official documentation, apps should instead use the NetworkCallback API, which is what isn't working in the original question :/ `isConnected()` isn't deprecated though. – Chris Watts Sep 24 '18 at 09:18
  • getType() is also deprecated :( For the sake of future readers, I think the accepted answer should work for the latest versions of Android. This method otherwise works though. – Chris Watts Sep 24 '18 at 09:25