Add 5G-NSA detection for API 31+

The hacky workaround for APIs 29/30 doesn't work on API 31. Instead,
we can use the onDisplayInfoChanged callback, that is accessible from
API 31.

PiperOrigin-RevId: 364997282
This commit is contained in:
tonihei 2021-03-25 10:09:26 +00:00 committed by Oliver Woodman
parent 1526ca5e3c
commit e42004652a
2 changed files with 27 additions and 8 deletions

View File

@ -27,9 +27,11 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.telephony.PhoneStateListener; import android.telephony.PhoneStateListener;
import android.telephony.ServiceState; import android.telephony.ServiceState;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import androidx.annotation.GuardedBy; import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -215,15 +217,21 @@ public final class NetworkTypeObserver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@C.NetworkType int networkType = getNetworkTypeFromConnectivityManager(context); @C.NetworkType int networkType = getNetworkTypeFromConnectivityManager(context);
if (networkType == C.NETWORK_TYPE_4G && Util.SDK_INT >= 29 && Util.SDK_INT < 31) { if (networkType == C.NETWORK_TYPE_4G && Util.SDK_INT >= 29) {
// Delay update of the network type to check whether this is actually 5G-NSA. // Delay update of the network type to check whether this is actually 5G-NSA.
try { try {
// We can't access TelephonyManager.getServiceState() directly as it requires special // We can't access TelephonyManager getters like getServiceState() directly as they
// permissions. Attaching a listener is permission-free. // require special permissions. Attaching a listener is permission-free because the
// callback data is censored to not include sensitive information.
TelephonyManager telephonyManager = TelephonyManager telephonyManager =
checkNotNull((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)); checkNotNull((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
ServiceStateListener listener = new ServiceStateListener(); TelephonyManagerListener listener = new TelephonyManagerListener();
if (Util.SDK_INT < 31) {
telephonyManager.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); telephonyManager.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
} else {
// Display info information can only be requested without permission from API 31.
telephonyManager.listen(listener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED);
}
// We are only interested in the initial response with the current state, so unregister // We are only interested in the initial response with the current state, so unregister
// the listener immediately. // the listener immediately.
telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE); telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE);
@ -236,15 +244,26 @@ public final class NetworkTypeObserver {
} }
} }
private class ServiceStateListener extends PhoneStateListener { private class TelephonyManagerListener extends PhoneStateListener {
@Override @Override
public void onServiceStateChanged(@Nullable ServiceState serviceState) { public void onServiceStateChanged(@Nullable ServiceState serviceState) {
// This workaround to check the toString output of ServiceState only works on API 29 and 30.
String serviceStateString = serviceState == null ? "" : serviceState.toString(); String serviceStateString = serviceState == null ? "" : serviceState.toString();
boolean is5gNsa = boolean is5gNsa =
serviceStateString.contains("nrState=CONNECTED") serviceStateString.contains("nrState=CONNECTED")
|| serviceStateString.contains("nrState=NOT_RESTRICTED"); || serviceStateString.contains("nrState=NOT_RESTRICTED");
updateNetworkType(is5gNsa ? C.NETWORK_TYPE_5G_NSA : C.NETWORK_TYPE_4G); updateNetworkType(is5gNsa ? C.NETWORK_TYPE_5G_NSA : C.NETWORK_TYPE_4G);
} }
@RequiresApi(31)
@Override
public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
int overrideNetworkType = telephonyDisplayInfo.getOverrideNetworkType();
boolean is5gNsa =
overrideNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA
|| overrideNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE;
updateNetworkType(is5gNsa ? C.NETWORK_TYPE_5G_NSA : C.NETWORK_TYPE_4G);
}
} }
} }

View File

@ -56,8 +56,8 @@ public final class DefaultBandwidthMeterTest {
private NetworkInfo networkInfo2g; private NetworkInfo networkInfo2g;
private NetworkInfo networkInfo3g; private NetworkInfo networkInfo3g;
private NetworkInfo networkInfo4g; private NetworkInfo networkInfo4g;
// TODO: Add tests covering 5G-NSA networks. Not testable right now because Robolectric's // TODO: Add tests covering 5G-NSA networks. Not testable right now because we need to set the
// ShadowTelephonyManager doesn't handle requests to return the ServiceState. // TelephonyDisplayInfo on API 31, which isn't available for Robolectric yet.
private NetworkInfo networkInfo5gSa; private NetworkInfo networkInfo5gSa;
private NetworkInfo networkInfoEthernet; private NetworkInfo networkInfoEthernet;