Add 5G-NSA detection.

PiperOrigin-RevId: 364607555
This commit is contained in:
tonihei 2021-03-23 18:42:35 +00:00 committed by Ian Baker
parent c202d406fb
commit b35c81124d
4 changed files with 122 additions and 9 deletions

View File

@ -930,8 +930,8 @@ public final class C {
/** /**
* Network connection type. One of {@link #NETWORK_TYPE_UNKNOWN}, {@link #NETWORK_TYPE_OFFLINE}, * Network connection type. One of {@link #NETWORK_TYPE_UNKNOWN}, {@link #NETWORK_TYPE_OFFLINE},
* {@link #NETWORK_TYPE_WIFI}, {@link #NETWORK_TYPE_2G}, {@link #NETWORK_TYPE_3G}, {@link * {@link #NETWORK_TYPE_WIFI}, {@link #NETWORK_TYPE_2G}, {@link #NETWORK_TYPE_3G}, {@link
* #NETWORK_TYPE_4G}, {@link #NETWORK_TYPE_5G}, {@link #NETWORK_TYPE_CELLULAR_UNKNOWN}, {@link * #NETWORK_TYPE_4G}, {@link #NETWORK_TYPE_5G_SA}, {@link #NETWORK_TYPE_5G_NSA}, {@link
* #NETWORK_TYPE_ETHERNET} or {@link #NETWORK_TYPE_OTHER}. * #NETWORK_TYPE_CELLULAR_UNKNOWN}, {@link #NETWORK_TYPE_ETHERNET} or {@link #NETWORK_TYPE_OTHER}.
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@ -942,7 +942,8 @@ public final class C {
NETWORK_TYPE_2G, NETWORK_TYPE_2G,
NETWORK_TYPE_3G, NETWORK_TYPE_3G,
NETWORK_TYPE_4G, NETWORK_TYPE_4G,
NETWORK_TYPE_5G, NETWORK_TYPE_5G_SA,
NETWORK_TYPE_5G_NSA,
NETWORK_TYPE_CELLULAR_UNKNOWN, NETWORK_TYPE_CELLULAR_UNKNOWN,
NETWORK_TYPE_ETHERNET, NETWORK_TYPE_ETHERNET,
NETWORK_TYPE_OTHER NETWORK_TYPE_OTHER
@ -960,8 +961,10 @@ public final class C {
public static final int NETWORK_TYPE_3G = 4; public static final int NETWORK_TYPE_3G = 4;
/** Network type for a 4G cellular connection. */ /** Network type for a 4G cellular connection. */
public static final int NETWORK_TYPE_4G = 5; public static final int NETWORK_TYPE_4G = 5;
/** Network type for a 5G cellular connection. */ /** Network type for a 5G stand-alone (SA) cellular connection. */
public static final int NETWORK_TYPE_5G = 9; public static final int NETWORK_TYPE_5G_SA = 9;
/** Network type for a 5G non-stand-alone (NSA) cellular connection. */
public static final int NETWORK_TYPE_5G_NSA = 10;
/** /**
* Network type for cellular connections which cannot be mapped to one of {@link * Network type for cellular connections which cannot be mapped to one of {@link
* #NETWORK_TYPE_2G}, {@link #NETWORK_TYPE_3G}, or {@link #NETWORK_TYPE_4G}. * #NETWORK_TYPE_2G}, {@link #NETWORK_TYPE_3G}, or {@link #NETWORK_TYPE_4G}.

View File

@ -207,7 +207,7 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
private static Map<Integer, Long> getInitialBitrateEstimatesForCountry(String countryCode) { private static Map<Integer, Long> getInitialBitrateEstimatesForCountry(String countryCode) {
List<Integer> groupIndices = getCountryGroupIndices(countryCode); List<Integer> groupIndices = getCountryGroupIndices(countryCode);
Map<Integer, Long> result = new HashMap<>(/* initialCapacity= */ 6); Map<Integer, Long> result = new HashMap<>(/* initialCapacity= */ 8);
result.put(C.NETWORK_TYPE_UNKNOWN, DEFAULT_INITIAL_BITRATE_ESTIMATE); result.put(C.NETWORK_TYPE_UNKNOWN, DEFAULT_INITIAL_BITRATE_ESTIMATE);
result.put( result.put(
C.NETWORK_TYPE_WIFI, C.NETWORK_TYPE_WIFI,
@ -222,7 +222,12 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
C.NETWORK_TYPE_4G, C.NETWORK_TYPE_4G,
DEFAULT_INITIAL_BITRATE_ESTIMATES_4G.get(groupIndices.get(COUNTRY_GROUP_INDEX_4G))); DEFAULT_INITIAL_BITRATE_ESTIMATES_4G.get(groupIndices.get(COUNTRY_GROUP_INDEX_4G)));
result.put( result.put(
C.NETWORK_TYPE_5G, C.NETWORK_TYPE_5G_NSA,
DEFAULT_INITIAL_BITRATE_ESTIMATES_5G_NSA.get(
groupIndices.get(COUNTRY_GROUP_INDEX_5G_NSA)));
result.put(
C.NETWORK_TYPE_5G_SA,
// TODO: Retrieve actual 5G-SA estimates.
DEFAULT_INITIAL_BITRATE_ESTIMATES_5G_NSA.get( DEFAULT_INITIAL_BITRATE_ESTIMATES_5G_NSA.get(
groupIndices.get(COUNTRY_GROUP_INDEX_5G_NSA))); groupIndices.get(COUNTRY_GROUP_INDEX_5G_NSA)));
// Assume default Wifi speed for Ethernet to prevent using the slower fallback. // Assume default Wifi speed for Ethernet to prevent using the slower fallback.

View File

@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.util; package com.google.android.exoplayer2.util;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -23,6 +25,8 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import androidx.annotation.GuardedBy; import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -196,7 +200,7 @@ public final class NetworkTypeObserver {
case TelephonyManager.NETWORK_TYPE_LTE: case TelephonyManager.NETWORK_TYPE_LTE:
return C.NETWORK_TYPE_4G; return C.NETWORK_TYPE_4G;
case TelephonyManager.NETWORK_TYPE_NR: case TelephonyManager.NETWORK_TYPE_NR:
return Util.SDK_INT >= 29 ? C.NETWORK_TYPE_5G : C.NETWORK_TYPE_UNKNOWN; return Util.SDK_INT >= 29 ? C.NETWORK_TYPE_5G_SA : C.NETWORK_TYPE_UNKNOWN;
case TelephonyManager.NETWORK_TYPE_IWLAN: case TelephonyManager.NETWORK_TYPE_IWLAN:
return C.NETWORK_TYPE_WIFI; return C.NETWORK_TYPE_WIFI;
case TelephonyManager.NETWORK_TYPE_GSM: case TelephonyManager.NETWORK_TYPE_GSM:
@ -211,7 +215,36 @@ 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) {
// Delay update of the network type to check whether this is actually 5G-NSA.
try {
// We can't access TelephonyManager.getServiceState() directly as it requires special
// permissions. Attaching a listener is permission-free.
TelephonyManager telephonyManager =
checkNotNull((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
ServiceStateListener listener = new ServiceStateListener();
telephonyManager.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
// We are only interested in the initial response with the current state, so unregister
// the listener immediately.
telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE);
return;
} catch (RuntimeException e) {
// Ignore problems with listener registration and keep reporting as 4G.
}
}
updateNetworkType(networkType); updateNetworkType(networkType);
} }
} }
private class ServiceStateListener extends PhoneStateListener {
@Override
public void onServiceStateChanged(@Nullable ServiceState serviceState) {
String serviceStateString = serviceState == null ? "" : serviceState.toString();
boolean is5gNsa =
serviceStateString.contains("nrState=CONNECTED")
|| serviceStateString.contains("nrState=NOT_RESTRICTED");
updateNetworkType(is5gNsa ? C.NETWORK_TYPE_5G_NSA : C.NETWORK_TYPE_4G);
}
}
} }

View File

@ -37,6 +37,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.Shadows; import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowNetworkInfo; import org.robolectric.shadows.ShadowNetworkInfo;
@ -45,7 +46,7 @@ import org.robolectric.shadows.ShadowNetworkInfo;
public final class DefaultBandwidthMeterTest { public final class DefaultBandwidthMeterTest {
private static final int SIMULATED_TRANSFER_COUNT = 100; private static final int SIMULATED_TRANSFER_COUNT = 100;
private static final String FAST_COUNTRY_ISO = "EE"; private static final String FAST_COUNTRY_ISO = "TW";
private static final String SLOW_COUNTRY_ISO = "PG"; private static final String SLOW_COUNTRY_ISO = "PG";
private TelephonyManager telephonyManager; private TelephonyManager telephonyManager;
@ -55,6 +56,9 @@ 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
// ShadowTelephonyManager doesn't handle requests to return the ServiceState.
private NetworkInfo networkInfo5gSa;
private NetworkInfo networkInfoEthernet; private NetworkInfo networkInfoEthernet;
@Before @Before
@ -103,6 +107,13 @@ public final class DefaultBandwidthMeterTest {
TelephonyManager.NETWORK_TYPE_LTE, TelephonyManager.NETWORK_TYPE_LTE,
/* isAvailable= */ true, /* isAvailable= */ true,
CONNECTED); CONNECTED);
networkInfo5gSa =
ShadowNetworkInfo.newInstance(
DetailedState.CONNECTED,
ConnectivityManager.TYPE_MOBILE,
TelephonyManager.NETWORK_TYPE_NR,
/* isAvailable= */ true,
CONNECTED);
networkInfoEthernet = networkInfoEthernet =
ShadowNetworkInfo.newInstance( ShadowNetworkInfo.newInstance(
DetailedState.CONNECTED, DetailedState.CONNECTED,
@ -217,6 +228,22 @@ public final class DefaultBandwidthMeterTest {
assertThat(initialEstimate3g).isGreaterThan(initialEstimate2g); assertThat(initialEstimate3g).isGreaterThan(initialEstimate2g);
} }
@Config(sdk = Config.NEWEST_SDK) // TODO: Remove once targetSDK >= 29
@Test
public void defaultInitialBitrateEstimate_for5gSa_isGreaterThanEstimateFor4g() {
setActiveNetworkInfo(networkInfo4g);
DefaultBandwidthMeter bandwidthMeter4g =
new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build();
long initialEstimate4g = bandwidthMeter4g.getBitrateEstimate();
setActiveNetworkInfo(networkInfo5gSa);
DefaultBandwidthMeter bandwidthMeter5gSa =
new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build();
long initialEstimate5gSa = bandwidthMeter5gSa.getBitrateEstimate();
assertThat(initialEstimate5gSa).isGreaterThan(initialEstimate4g);
}
@Test @Test
public void defaultInitialBitrateEstimate_forOffline_isReasonable() { public void defaultInitialBitrateEstimate_forOffline_isReasonable() {
setActiveNetworkInfo(networkInfoOffline); setActiveNetworkInfo(networkInfoOffline);
@ -313,6 +340,24 @@ public final class DefaultBandwidthMeterTest {
assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow); assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow);
} }
@Config(sdk = Config.NEWEST_SDK) // TODO: Remove once targetSDK >= 29
@Test
public void
defaultInitialBitrateEstimate_for5gSa_forFastCountry_isGreaterThanEstimateForSlowCountry() {
setActiveNetworkInfo(networkInfo5gSa);
setNetworkCountryIso(FAST_COUNTRY_ISO);
DefaultBandwidthMeter bandwidthMeterFast =
new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build();
long initialEstimateFast = bandwidthMeterFast.getBitrateEstimate();
setNetworkCountryIso(SLOW_COUNTRY_ISO);
DefaultBandwidthMeter bandwidthMeterSlow =
new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build();
long initialEstimateSlow = bandwidthMeterSlow.getBitrateEstimate();
assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow);
}
@Test @Test
public void initialBitrateEstimateOverwrite_whileConnectedToNetwork_setsInitialEstimate() { public void initialBitrateEstimateOverwrite_whileConnectedToNetwork_setsInitialEstimate() {
setActiveNetworkInfo(networkInfoWifi); setActiveNetworkInfo(networkInfoWifi);
@ -463,6 +508,33 @@ public final class DefaultBandwidthMeterTest {
assertThat(initialEstimate).isNotEqualTo(123456789); assertThat(initialEstimate).isNotEqualTo(123456789);
} }
@Config(sdk = Config.NEWEST_SDK) // TODO: Remove once targetSDK >= 29
@Test
public void initialBitrateEstimateOverwrite_for5gSa_whileConnectedTo5gSa_setsInitialEstimate() {
setActiveNetworkInfo(networkInfo5gSa);
DefaultBandwidthMeter bandwidthMeter =
new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext())
.setInitialBitrateEstimate(C.NETWORK_TYPE_5G_SA, 123456789)
.build();
long initialEstimate = bandwidthMeter.getBitrateEstimate();
assertThat(initialEstimate).isEqualTo(123456789);
}
@Config(sdk = Config.NEWEST_SDK) // TODO: Remove once targetSDK >= 29
@Test
public void
initialBitrateEstimateOverwrite_for5gSa_whileConnectedToOtherNetwork_doesNotSetInitialEstimate() {
setActiveNetworkInfo(networkInfoWifi);
DefaultBandwidthMeter bandwidthMeter =
new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext())
.setInitialBitrateEstimate(C.NETWORK_TYPE_5G_SA, 123456789)
.build();
long initialEstimate = bandwidthMeter.getBitrateEstimate();
assertThat(initialEstimate).isNotEqualTo(123456789);
}
@Test @Test
public void initialBitrateEstimateOverwrite_forOffline_whileOffline_setsInitialEstimate() { public void initialBitrateEstimateOverwrite_forOffline_whileOffline_setsInitialEstimate() {
setActiveNetworkInfo(networkInfoOffline); setActiveNetworkInfo(networkInfoOffline);