How to monitor Android network type: 5G/4G/3G/2G capabilities

How to monitor Android network type: 5G/4G/3G/2G capabilities


Apps could benefit by knowing the type of network they are connected to, such as enabling certain features that require the bandwidth and low latency 5G offers. If only 2G or 3G network is available, the load time will be slower, so we can have some expectations for the load time.

Here, we can use the TelephonyManager class to obtain various information about the state of the mobile network, including the network type! However, using TelephonyManager is quite complicated, because different Android versions have different situations to consider.

Below I provide a sample application that can detect the type of mobile network we are connected to, not just 5G/4G/3G/2G, but also get the specific subtype. The app is written using TelephonyManagerand combining Jetpack Compose, ViewModel and Kotlin Flow.

https://github.com/tdcolvin/NetworkTypeDetector

Sign up TelephonyManagerto receive network information updates

The method of obtaining TelephonyManageris as follows:

val telephonyManager =
  context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager

...when the context is a Context instance. Note that some phones have multiple SIM cards; if you want to query a specific SIM, call TelephonyManageron the instance .createForSubscriptionId(simCardNumber).

Using this instance, we can now get network information updates. The process used depends on the Android version, i.e. the user's Android version, not your app's target API level.

Android ≥ 12(API ≥ 31)

Android 12 and above is the easiest case as there is a dedicated listener and no permissions are required.

To register to receive network type information, we use registerTelephonyCallback(Executor, TelephonyCallback)the method, as follows:

// The thread Executor used to run the listener. This governs how threads are created and
// reused. Here we use a single thread.
val exec = Executors.newSingleThreadExecutor()

// Create the callback object
val callback = object : TelephonyCallback(), TelephonyCallback.DisplayInfoListener {
    
    
    override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
    
    
        //TODO: This is next
    }
}

// Finally, register the callback so it can start receiving results.
telephonyManager.registerTelephonyCallback(exec, callback)

Unregister the listener, as follows:

telephonyManager.unregisterTelephonyCallback(callback)

“”"

Android 11 only (API 30)

The original way to register callbacks for the telephony manager is to use the listen method. The method accepts various types of listeners; what we need are listeners that implement onDisplayInfoChangedthe interface .

Interestingly, this method appeared and disappeared in one Android version:

This requires READ_PHONE_STATEpermissions . We'll deal with that later in the UI code. Now we'll continue, assuming we already have that permission.

// (At the top of the file)
@file:Suppress("DEPRECATION") //Suppressed as required to support old version

// SDK 30 uses TelephonyManager.listen() to listen for TelephonyDisplayInfo changes.
// It requires READ_PHONE_STATE permission.

@Suppress("OVERRIDE_DEPRECATION")  //Suppressed as required to support old version
// This is the object that will receive the results
val callback = object : PhoneStateListener(exec) {
    
    
    override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
    
    
        //TODO: This is next
    }
}

// Start listening for results
telephonyManager.listen(callback, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)

Logout listeners use the following code:

telephonyManager.listen(callback, 0)

Android ≥ 7(API ≥ 24)

Android 10 and below don't have any way to listen for network type changes. To support older versions, you need to implement a loop that actively checks every few seconds.

The checked code is as follows:

val networkType = telephonyManager.dataNetworkType

This requires READ_PHONE_STATEpermissions.

Note that Android 10 and below cannot support 5G, as 5G is only available on Android 11 and above.

Returned value: Network type constant

In the above code for Android 11 and ≥12, a TelephonyDisplayInfocallback with an object is received. This object contains a networkType and a overrideNetworkType. And in Android ≤10 code, only one will be received networkType.

In either case, networkTypeit can be one of the following:

val baseTypeString = when(networkType) {
    
    
    TelephonyManager.NETWORK_TYPE_CDMA -> "CDMA"
    TelephonyManager.NETWORK_TYPE_1xRTT -> "1xRTT"
    TelephonyManager.NETWORK_TYPE_EDGE -> "EDGE"
    TelephonyManager.NETWORK_TYPE_EHRPD -> "eHRPD"
    TelephonyManager.NETWORK_TYPE_EVDO_0 -> "EVDO rev 0"
    TelephonyManager.NETWORK_TYPE_EVDO_A -> "EVDO rev A"
    TelephonyManager.NETWORK_TYPE_EVDO_B -> "EVDO rev B"
    TelephonyManager.NETWORK_TYPE_GPRS -> "GPRS"
    TelephonyManager.NETWORK_TYPE_GSM -> "GSM"
    TelephonyManager.NETWORK_TYPE_HSDPA -> "HSDPA"
    TelephonyManager.NETWORK_TYPE_HSPA -> "HSPA"
    TelephonyManager.NETWORK_TYPE_HSPAP -> "HSPA+"
    TelephonyManager.NETWORK_TYPE_HSUPA -> "HSUPA"
    TelephonyManager.NETWORK_TYPE_IDEN -> "iDen"
    TelephonyManager.NETWORK_TYPE_IWLAN -> "IWLAN"
    TelephonyManager.NETWORK_TYPE_LTE -> "LTE"
    TelephonyManager.NETWORK_TYPE_NR -> "NR (new radio) 5G"
    TelephonyManager.NETWORK_TYPE_TD_SCDMA -> "TD_SCDMA"
    TelephonyManager.NETWORK_TYPE_UMTS -> "UMTS"
    else -> "[Unknown]"
}

overrideNetworkTypeMore information is provided for certain types of 4G and 5G connections when available . Here are the options:

val overrideString = when(overrideNetworkType) {
    
    
    TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> "5G non-standalone"
    TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> "5G standalone (advanced)"
    TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> "LTE Advanced Pro (5Ge)"
    TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> "LTE (carrier aggregation)"
    else -> null
}

Possible null handling:

val netTypeString = overrideString ?: baseTypeString

Build it as a Kotlin + Flows + ViewModel + Compose application

I'm using a Kotlin ViewModel callbackFlowto set the above listener. If you haven't come across it before callbackFlow, it's awesome: it's a flow that can be used to create a listener on an external API that is automatically removed when someone signs up.

I use convert .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)from This way, multiple ORs callbackFloware not created if multiple consumers register . Partially ensures that observables persist for some time after all consumers have gone away, in case they are about to reappear. (For example, this happens in case of screen rotation).DisplayInfoListenersPhoneStateListenersWhileSubscribed(5000)

In Composable, I use collectAsStateWithLifecycle()to make sure the listener is only active when the app is in the foreground.

GitHub

https://github.com/tdcolvin/NetworkTypeDetector

Guess you like

Origin blog.csdn.net/u011897062/article/details/131302481