androidandroid-connectivitymanager

NetworkCallback with NetworkCapabilities is called only once


I'm tying to get imformation from getLinkDownstreamBandwidthKbps(), getLinkUpstreamBandwidthKbps() and getSignalStrength() of NetworkCapabilities class. For this I do the following in MainActivity:

private static ConnectivityManager connectivityManager;

...

@Override
protected void onCreate(final Bundle savedInstanceState) {
    connectivityManager = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
}

private final NetworkRequest networkRequest = new NetworkRequest.Builder()
        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        .build();

private final NetworkCallback networkCallback = new NetworkCallback() {
    @Override
    public void onCapabilitiesChanged(@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
        int dLBandwidth = networkCapabilities.getLinkDownstreamBandwidthKbps();
        int uLBandwidth = networkCapabilities.getLinkUpstreamBandwidthKbps();
        int signalStrength = networkCapabilities.getSignalStrength();

        final String timestamp = Util.getCurrentTimestamp();
        Util.log(String.format(Locale.US, "CM::%s::dLBandwidth=%d::uLBandwidth=%d::sStrength=%d\n",
                timestamp, dLBandwidth, uLBandwidth, signalStrength));
    }
};

private View getStartButton() { // button click handler
    connectivityManager.registerNetworkCallback(networkRequest, networkCallback);
}

private View getStopButton() { // button click handler
    connectivityManager.unregisterNetworkCallback(networkCallback);
}

By some reason I get only one entry in log.

CM::2023-09-17 13:11:07.882::dLBandwidth=30000::uLBandwidth=15000::sStrength=-2147483648

So, NetworkCallback is called only once, although I'm moving with the phone around the home so that the signal strength changes. Could somebody advise what the reason of such behaviour? What I'm doing wrong?

In my case minSdkVersion 29, compileSdk 33.

Thanks!


Solution

  • It looks like such behaviour is predictable.

    1. SignalStrength == -2147483648 == Integer.MIN_VALUE == SIGNAL_STRENGTH_UNSPECIFIED.

    According to documentation it means the following:

    Magic value that indicates no signal strength provided. A request specifying this value is always satisfied.
    
    Constant Value: -2147483648 (0x80000000)
    

    NetworkCapabilities.java:1758

    /**
     * Magic value that indicates no signal strength provided. A request specifying this value is
     * always satisfied.
     */
    public static final int SIGNAL_STRENGTH_UNSPECIFIED = Integer.MIN_VALUE;
    

    So, it looks like in my case signal strength is not provided.

    1. LinkDownstreamBandwidth == 30000 and LinkUpstreamBandwidth == 15000.

    In my case I get these magic numbers and they are never updated. If we trace the sequence of calls of setters for these values we get the following sequence:

    setLinkDownstreamBandwidthKbps:3013

    /**
     * Sets the downstream bandwidth for this network in Kbps. This always only refers to
     * the estimated first hop transport bandwidth.
     * <p>
     * Note that when used to request a network, this specifies the minimum acceptable.
     * When received as the state of an existing network this specifies the typical
     * first hop bandwidth expected. This is never measured, but rather is inferred
     * from technology type and other link parameters. It could be used to differentiate
     * between very slow 1xRTT cellular links and other faster networks or even between
     * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
     * fast backhauls and slow backhauls.
     *
     * @param downKbps the estimated first hop downstream (network to device) bandwidth.
     * @return this builder
     */
    @NonNull
    public Builder setLinkDownstreamBandwidthKbps(final int downKbps) {
        mCaps.setLinkDownstreamBandwidthKbps(downKbps);
        return this;
    }
    

    updateNetworkCapabilities:2080

    /**
     * Update the network capabilities.
     */
    private void updateNetworkCapabilities() {
        final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
        boolean roaming = mPhone.getServiceState().getDataRoaming();
        ...
        // Set the bandwidth information.
        builder.setLinkDownstreamBandwidthKbps(mNetworkBandwidth.downlinkBandwidthKbps);    <---
        builder.setLinkUpstreamBandwidthKbps(mNetworkBandwidth.uplinkBandwidthKbps);        <---
        ...
    }
    

    NetworkBandwidth:715

    /**
     * The network bandwidth.
     */
    public static class NetworkBandwidth {
        /** The downlink bandwidth in Kbps. */
        public final int downlinkBandwidthKbps;
    
        /** The uplink Bandwidth in Kbps. */
        public final int uplinkBandwidthKbps;
    
        /**
         * Constructor.
         *
         * @param downlinkBandwidthKbps The downlink bandwidth in Kbps.
         * @param uplinkBandwidthKbps The uplink Bandwidth in Kbps.
         */
        public NetworkBandwidth(int downlinkBandwidthKbps, int uplinkBandwidthKbps) {    <---
            this.downlinkBandwidthKbps = downlinkBandwidthKbps;
            this.uplinkBandwidthKbps = uplinkBandwidthKbps;
        }
    
        @Override
        public String toString() {
            return String.format("NetworkBandwidth=[downlink=%d, uplink=%d]",
                    downlinkBandwidthKbps, uplinkBandwidthKbps);
        }
    }
    

    updateBandwidths:801

    /**
     * Update the downlink and uplink bandwidth values from the carrier config.
     */
    private void updateBandwidths() {
        synchronized (this) {
            mBandwidthMap.clear();
            String[] bandwidths = mCarrierConfig.getStringArray(
                    CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY);           <---
            boolean useLte = mCarrierConfig.getBoolean(CarrierConfigManager
                    .KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL);
            if (bandwidths != null) {
                for (String bandwidth : bandwidths) {
                    // split1[0] = network type as string
                    // split1[1] = downlink,uplink
                    String[] split1 = bandwidth.split(":");
                    if (split1.length != 2) {
                        loge("Invalid bandwidth: " + bandwidth);
                        continue;
                    }
                    // split2[0] = downlink bandwidth in kbps
                    // split2[1] = uplink bandwidth in kbps
                    String[] split2 = split1[1].split(",");
                    if (split2.length != 2) {
                        loge("Invalid bandwidth values: " + Arrays.toString(split2));
                        continue;
                    }
                    int downlink, uplink;
                    try {
                        downlink = Integer.parseInt(split2[0]);
                        uplink = Integer.parseInt(split2[1]);
                    } catch (NumberFormatException e) {
                        loge("Exception parsing bandwidth values for network type " + split1[0]
                                + ": " + e);
                        continue;
                    }
                    if (useLte && split1[0].startsWith("NR")) {
                        // We can get it directly from mBandwidthMap because LTE is defined before
                        // the NR values in CarrierConfigManager#KEY_BANDWIDTH_STRING_ARRAY.
                        uplink = mBandwidthMap.get(DATA_CONFIG_NETWORK_TYPE_LTE)
                                .uplinkBandwidthKbps;
                    }
                    mBandwidthMap.put(split1[0],
                            new DataNetwork.NetworkBandwidth(downlink, uplink));
                }
            }
        }
    }
    

    KEY_BANDWIDTH_STRING_ARRAY:2996

    /**
     * String array of default bandwidth values per network type.
     * The entries should be of form: "network_name:downlink,uplink", with values in Kbps.
     * For NR (5G), the following network names should be used:
     * - NR_NSA: NR NSA, sub-6 frequencies
     * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
     * - NR_SA: NR SA, sub-6 frequencies
     * - NR_SA_MMWAVE: NR SA, mmwave frequencies
     * @hide
     */
    public static final String KEY_BANDWIDTH_STRING_ARRAY = "bandwidth_string_array";
    

    sDefaults.putStringArray:9168

    sDefaults.putStringArray(KEY_BANDWIDTH_STRING_ARRAY, new String[]{
                "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA:14,14",
                "1xRTT:30,30", "EvDo_0:750,48", "EvDo_A:950,550", "HSDPA:4300,620",
                "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo_B:1500,550", "eHRPD:750,48",
                "iDEN:14,14", "LTE:30000,15000", "HSPA+:13000,3400", "GSM:24,24",
                "TD_SCDMA:115,115", "LTE_CA:30000,15000", "NR_NSA:47000,18000",
                "NR_NSA_MMWAVE:145000,60000", "NR_SA:145000,60000", "NR_SA_MMWAVE:145000,60000"});
    

    So, I can assume for devices which I used for test I can get only default values for CELLULAR transport type. Perhaps modem firmware/driver doesn't expose such data. In the same time this API provides correct values for WIFI transport type.