diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/BandwidthMeter.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/BandwidthMeter.java index f35d745892..0c46ac48da 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/BandwidthMeter.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/BandwidthMeter.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.upstream; import android.os.Handler; import androidx.annotation.Nullable; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.Assertions; import java.util.concurrent.CopyOnWriteArrayList; @@ -106,6 +107,14 @@ public interface BandwidthMeter { /** Returns the estimated bitrate. */ long getBitrateEstimate(); + /** + * Returns the estimated time to first byte, in microseconds, or {@link C#TIME_UNSET} if no + * estimate is available. + */ + default long getTimeToFirstByteEstimateUs() { + return C.TIME_UNSET; + } + /** * Returns the {@link TransferListener} that this instance uses to gather bandwidth information * from data transfers. May be null if the implementation does not listen to data transfers. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ExperimentalBandwidthMeter.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ExperimentalBandwidthMeter.java deleted file mode 100644 index 5015b361e6..0000000000 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ExperimentalBandwidthMeter.java +++ /dev/null @@ -1,720 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.upstream; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.os.Handler; -import android.os.Looper; -import androidx.annotation.Nullable; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.upstream.BandwidthMeter.EventListener.EventDispatcher; -import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.Clock; -import com.google.android.exoplayer2.util.Util; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -/** - * An experimental {@link BandwidthMeter} that estimates bandwidth by listening to data transfers. - * - *

The initial estimate is based on the current operator's network country code or the locale of - * the user, as well as the network connection type. This can be configured in the {@link Builder}. - */ -public final class ExperimentalBandwidthMeter implements BandwidthMeter, TransferListener { - - /** - * Country groups used to determine the default initial bitrate estimate. The group assignment for - * each country is a list for [Wifi, 2G, 3G, 4G, 5G_NSA]. - */ - public static final ImmutableListMultimap - DEFAULT_INITIAL_BITRATE_COUNTRY_GROUPS = createInitialBitrateCountryGroupAssignment(); - - /** Default initial Wifi bitrate estimate in bits per second. */ - public static final ImmutableList DEFAULT_INITIAL_BITRATE_ESTIMATES_WIFI = - ImmutableList.of(6_100_000L, 3_900_000L, 2_300_000L, 1_300_000L, 600_000L); - - /** Default initial 2G bitrate estimates in bits per second. */ - public static final ImmutableList DEFAULT_INITIAL_BITRATE_ESTIMATES_2G = - ImmutableList.of(230_000L, 159_000L, 142_000L, 127_000L, 112_000L); - - /** Default initial 3G bitrate estimates in bits per second. */ - public static final ImmutableList DEFAULT_INITIAL_BITRATE_ESTIMATES_3G = - ImmutableList.of(2_200_000L, 1_300_000L, 940_000L, 760_000L, 520_000L); - - /** Default initial 4G bitrate estimates in bits per second. */ - public static final ImmutableList DEFAULT_INITIAL_BITRATE_ESTIMATES_4G = - ImmutableList.of(4_400_000L, 2_300_000L, 1_500_000L, 1_100_000L, 660_000L); - - /** Default initial 5G-NSA bitrate estimates in bits per second. */ - public static final ImmutableList DEFAULT_INITIAL_BITRATE_ESTIMATES_5G_NSA = - ImmutableList.of(13_000_000L, 9_100_000L, 6_300_000L, 4_000_000L, 2_000_000L); - - @Nullable private static ExperimentalBandwidthMeter singletonInstance; - - /** - * Default initial bitrate estimate used when the device is offline or the network type cannot be - * determined, in bits per second. - */ - public static final long DEFAULT_INITIAL_BITRATE_ESTIMATE = 1_000_000; - - /** Index for the Wifi group index in {@link #DEFAULT_INITIAL_BITRATE_COUNTRY_GROUPS}. */ - private static final int COUNTRY_GROUP_INDEX_WIFI = 0; - /** Index for the 2G group index in {@link #DEFAULT_INITIAL_BITRATE_COUNTRY_GROUPS}. */ - private static final int COUNTRY_GROUP_INDEX_2G = 1; - /** Index for the 3G group index in {@link #DEFAULT_INITIAL_BITRATE_COUNTRY_GROUPS}. */ - private static final int COUNTRY_GROUP_INDEX_3G = 2; - /** Index for the 4G group index in {@link #DEFAULT_INITIAL_BITRATE_COUNTRY_GROUPS}. */ - private static final int COUNTRY_GROUP_INDEX_4G = 3; - /** Index for the 5G-NSA group index in {@link #DEFAULT_INITIAL_BITRATE_COUNTRY_GROUPS}. */ - private static final int COUNTRY_GROUP_INDEX_5G_NSA = 4; - - /** Builder for a bandwidth meter. */ - public static final class Builder { - - @Nullable private final Context context; - - private Map initialBitrateEstimates; - private Clock clock; - private boolean resetOnNetworkTypeChange; - - /** - * Creates a builder with default parameters and without listener. - * - * @param context A context. - */ - public Builder(Context context) { - // Handling of null is for backward compatibility only. - this.context = context == null ? null : context.getApplicationContext(); - initialBitrateEstimates = getInitialBitrateEstimatesForCountry(Util.getCountryCode(context)); - clock = Clock.DEFAULT; - resetOnNetworkTypeChange = true; - } - - /** - * Sets the initial bitrate estimate in bits per second that should be assumed when a bandwidth - * estimate is unavailable. - * - * @param initialBitrateEstimate The initial bitrate estimate in bits per second. - * @return This builder. - */ - public Builder setInitialBitrateEstimate(long initialBitrateEstimate) { - for (Integer networkType : initialBitrateEstimates.keySet()) { - setInitialBitrateEstimate(networkType, initialBitrateEstimate); - } - return this; - } - - /** - * Sets the initial bitrate estimate in bits per second that should be assumed when a bandwidth - * estimate is unavailable and the current network connection is of the specified type. - * - * @param networkType The {@link C.NetworkType} this initial estimate is for. - * @param initialBitrateEstimate The initial bitrate estimate in bits per second. - * @return This builder. - */ - public Builder setInitialBitrateEstimate( - @C.NetworkType int networkType, long initialBitrateEstimate) { - initialBitrateEstimates.put(networkType, initialBitrateEstimate); - return this; - } - - /** - * Sets the initial bitrate estimates to the default values of the specified country. The - * initial estimates are used when a bandwidth estimate is unavailable. - * - * @param countryCode The ISO 3166-1 alpha-2 country code of the country whose default bitrate - * estimates should be used. - * @return This builder. - */ - public Builder setInitialBitrateEstimate(String countryCode) { - initialBitrateEstimates = - getInitialBitrateEstimatesForCountry(Util.toUpperInvariant(countryCode)); - return this; - } - - /** - * Sets the clock used to estimate bandwidth from data transfers. Should only be set for testing - * purposes. - * - * @param clock The clock used to estimate bandwidth from data transfers. - * @return This builder. - */ - public Builder setClock(Clock clock) { - this.clock = clock; - return this; - } - - /** - * Sets whether to reset if the network type changes. The default value is {@code true}. - * - * @param resetOnNetworkTypeChange Whether to reset if the network type changes. - * @return This builder. - */ - public Builder setResetOnNetworkTypeChange(boolean resetOnNetworkTypeChange) { - this.resetOnNetworkTypeChange = resetOnNetworkTypeChange; - return this; - } - - /** - * Builds the bandwidth meter. - * - * @return A bandwidth meter with the configured properties. - */ - public ExperimentalBandwidthMeter build() { - return new ExperimentalBandwidthMeter( - context, initialBitrateEstimates, clock, resetOnNetworkTypeChange); - } - - private static Map getInitialBitrateEstimatesForCountry(String countryCode) { - List groupIndices = getCountryGroupIndices(countryCode); - Map result = new HashMap<>(/* initialCapacity= */ 6); - result.put(C.NETWORK_TYPE_UNKNOWN, DEFAULT_INITIAL_BITRATE_ESTIMATE); - result.put( - C.NETWORK_TYPE_WIFI, - DEFAULT_INITIAL_BITRATE_ESTIMATES_WIFI.get(groupIndices.get(COUNTRY_GROUP_INDEX_WIFI))); - result.put( - C.NETWORK_TYPE_2G, - DEFAULT_INITIAL_BITRATE_ESTIMATES_2G.get(groupIndices.get(COUNTRY_GROUP_INDEX_2G))); - result.put( - C.NETWORK_TYPE_3G, - DEFAULT_INITIAL_BITRATE_ESTIMATES_3G.get(groupIndices.get(COUNTRY_GROUP_INDEX_3G))); - result.put( - C.NETWORK_TYPE_4G, - DEFAULT_INITIAL_BITRATE_ESTIMATES_4G.get(groupIndices.get(COUNTRY_GROUP_INDEX_4G))); - result.put( - C.NETWORK_TYPE_5G, - DEFAULT_INITIAL_BITRATE_ESTIMATES_5G_NSA.get( - groupIndices.get(COUNTRY_GROUP_INDEX_5G_NSA))); - // Assume default Wifi speed for Ethernet to prevent using the slower fallback. - result.put( - C.NETWORK_TYPE_ETHERNET, - DEFAULT_INITIAL_BITRATE_ESTIMATES_WIFI.get(groupIndices.get(COUNTRY_GROUP_INDEX_WIFI))); - return result; - } - - private static ImmutableList getCountryGroupIndices(String countryCode) { - ImmutableList groupIndices = DEFAULT_INITIAL_BITRATE_COUNTRY_GROUPS.get(countryCode); - // Assume median group if not found. - return groupIndices.isEmpty() ? ImmutableList.of(2, 2, 2, 2, 2) : groupIndices; - } - } - - /** - * Returns a singleton instance of an {@link ExperimentalBandwidthMeter} with default - * configuration. - * - * @param context A {@link Context}. - * @return The singleton instance. - */ - public static synchronized ExperimentalBandwidthMeter getSingletonInstance(Context context) { - if (singletonInstance == null) { - singletonInstance = new Builder(context).build(); - } - return singletonInstance; - } - - @Nullable private final Context context; - private final ImmutableMap initialBitrateEstimates; - private final EventDispatcher eventDispatcher; - private final Clock clock; - - private int streamCount; - private long sampleStartTimeMs; - private long sampleBytesTransferred; - - @C.NetworkType private int networkType; - private long bitrateEstimate; - private long lastReportedBitrateEstimate; - - private boolean networkTypeOverrideSet; - @C.NetworkType private int networkTypeOverride; - - private ExperimentalBandwidthMeter( - @Nullable Context context, - Map initialBitrateEstimates, - Clock clock, - boolean resetOnNetworkTypeChange) { - this.context = context == null ? null : context.getApplicationContext(); - this.initialBitrateEstimates = ImmutableMap.copyOf(initialBitrateEstimates); - this.eventDispatcher = new EventDispatcher(); - this.clock = clock; - // Set the initial network type and bitrate estimate - networkType = context == null ? C.NETWORK_TYPE_UNKNOWN : Util.getNetworkType(context); - bitrateEstimate = getInitialBitrateEstimateForNetworkType(networkType); - // Register to receive connectivity actions if possible. - if (context != null && resetOnNetworkTypeChange) { - ConnectivityActionReceiver connectivityActionReceiver = - ConnectivityActionReceiver.getInstance(context); - connectivityActionReceiver.register(/* bandwidthMeter= */ this); - } - } - - /** - * Overrides the network type. Handled in the same way as if the meter had detected a change from - * the current network type to the specified network type internally. - * - *

Applications should not normally call this method. It is intended for testing purposes. - * - * @param networkType The overriding network type. - */ - public synchronized void setNetworkTypeOverride(@C.NetworkType int networkType) { - networkTypeOverride = networkType; - networkTypeOverrideSet = true; - onConnectivityAction(); - } - - @Override - public synchronized long getBitrateEstimate() { - return bitrateEstimate; - } - - @Override - public TransferListener getTransferListener() { - return this; - } - - @Override - public void addEventListener(Handler eventHandler, EventListener eventListener) { - Assertions.checkNotNull(eventHandler); - Assertions.checkNotNull(eventListener); - eventDispatcher.addListener(eventHandler, eventListener); - } - - @Override - public void removeEventListener(EventListener eventListener) { - eventDispatcher.removeListener(eventListener); - } - - @Override - public void onTransferInitializing(DataSource source, DataSpec dataSpec, boolean isNetwork) { - // TODO: Track time for time-to-response estimation. - } - - @Override - public synchronized void onTransferStart( - DataSource source, DataSpec dataSpec, boolean isNetwork) { - if (!isTransferAtFullNetworkSpeed(dataSpec, isNetwork)) { - return; - } - if (streamCount == 0) { - sampleStartTimeMs = clock.elapsedRealtime(); - } - streamCount++; - - // TODO: Track time for time-to-response estimation. - } - - @Override - public synchronized void onBytesTransferred( - DataSource source, DataSpec dataSpec, boolean isNetwork, int bytes) { - if (!isTransferAtFullNetworkSpeed(dataSpec, isNetwork)) { - return; - } - sampleBytesTransferred += bytes; - } - - @Override - public synchronized void onTransferEnd(DataSource source, DataSpec dataSpec, boolean isNetwork) { - if (!isTransferAtFullNetworkSpeed(dataSpec, isNetwork)) { - return; - } - Assertions.checkState(streamCount > 0); - long nowMs = clock.elapsedRealtime(); - int sampleElapsedTimeMs = (int) (nowMs - sampleStartTimeMs); - if (sampleElapsedTimeMs > 0) { - // TODO: Add heuristic for bandwidth estimation. - bitrateEstimate = (long) (sampleBytesTransferred * 8000f) / sampleElapsedTimeMs; - maybeNotifyBandwidthSample(sampleElapsedTimeMs, sampleBytesTransferred, bitrateEstimate); - sampleStartTimeMs = nowMs; - sampleBytesTransferred = 0; - } // Else any sample bytes transferred will be carried forward into the next sample. - streamCount--; - } - - private synchronized void onConnectivityAction() { - int networkType = - networkTypeOverrideSet - ? networkTypeOverride - : (context == null ? C.NETWORK_TYPE_UNKNOWN : Util.getNetworkType(context)); - if (this.networkType == networkType) { - return; - } - - this.networkType = networkType; - if (networkType == C.NETWORK_TYPE_OFFLINE - || networkType == C.NETWORK_TYPE_UNKNOWN - || networkType == C.NETWORK_TYPE_OTHER) { - // It's better not to reset the bandwidth meter for these network types. - return; - } - - // Reset the bitrate estimate and report it, along with any bytes transferred. - this.bitrateEstimate = getInitialBitrateEstimateForNetworkType(networkType); - long nowMs = clock.elapsedRealtime(); - int sampleElapsedTimeMs = streamCount > 0 ? (int) (nowMs - sampleStartTimeMs) : 0; - maybeNotifyBandwidthSample(sampleElapsedTimeMs, sampleBytesTransferred, bitrateEstimate); - - // Reset the remainder of the state. - sampleStartTimeMs = nowMs; - sampleBytesTransferred = 0; - } - - private void maybeNotifyBandwidthSample( - int elapsedMs, long bytesTransferred, long bitrateEstimate) { - if (elapsedMs == 0 && bytesTransferred == 0 && bitrateEstimate == lastReportedBitrateEstimate) { - return; - } - lastReportedBitrateEstimate = bitrateEstimate; - eventDispatcher.bandwidthSample(elapsedMs, bytesTransferred, bitrateEstimate); - } - - private long getInitialBitrateEstimateForNetworkType(@C.NetworkType int networkType) { - Long initialBitrateEstimate = initialBitrateEstimates.get(networkType); - if (initialBitrateEstimate == null) { - initialBitrateEstimate = initialBitrateEstimates.get(C.NETWORK_TYPE_UNKNOWN); - } - if (initialBitrateEstimate == null) { - initialBitrateEstimate = DEFAULT_INITIAL_BITRATE_ESTIMATE; - } - return initialBitrateEstimate; - } - - private static boolean isTransferAtFullNetworkSpeed(DataSpec dataSpec, boolean isNetwork) { - return isNetwork && !dataSpec.isFlagSet(DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED); - } - - /* - * Note: This class only holds a weak reference to bandwidth meter instances. It should not - * be made non-static, since doing so adds a strong reference (i.e., - * ExperimentalBandwidthMeter.this). - */ - private static class ConnectivityActionReceiver extends BroadcastReceiver { - - private static @MonotonicNonNull ConnectivityActionReceiver staticInstance; - - private final Handler mainHandler; - private final ArrayList> bandwidthMeters; - - public static synchronized ConnectivityActionReceiver getInstance(Context context) { - if (staticInstance == null) { - staticInstance = new ConnectivityActionReceiver(); - IntentFilter filter = new IntentFilter(); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - context.registerReceiver(staticInstance, filter); - } - return staticInstance; - } - - private ConnectivityActionReceiver() { - mainHandler = new Handler(Looper.getMainLooper()); - bandwidthMeters = new ArrayList<>(); - } - - public synchronized void register(ExperimentalBandwidthMeter bandwidthMeter) { - removeClearedReferences(); - bandwidthMeters.add(new WeakReference<>(bandwidthMeter)); - // Simulate an initial update on the main thread (like the sticky broadcast we'd receive if - // we were to register a separate broadcast receiver for each bandwidth meter). - mainHandler.post(() -> updateBandwidthMeter(bandwidthMeter)); - } - - @Override - public synchronized void onReceive(Context context, Intent intent) { - if (isInitialStickyBroadcast()) { - return; - } - removeClearedReferences(); - for (int i = 0; i < bandwidthMeters.size(); i++) { - WeakReference bandwidthMeterReference = bandwidthMeters.get(i); - ExperimentalBandwidthMeter bandwidthMeter = bandwidthMeterReference.get(); - if (bandwidthMeter != null) { - updateBandwidthMeter(bandwidthMeter); - } - } - } - - private void updateBandwidthMeter(ExperimentalBandwidthMeter bandwidthMeter) { - bandwidthMeter.onConnectivityAction(); - } - - private void removeClearedReferences() { - for (int i = bandwidthMeters.size() - 1; i >= 0; i--) { - WeakReference bandwidthMeterReference = bandwidthMeters.get(i); - ExperimentalBandwidthMeter bandwidthMeter = bandwidthMeterReference.get(); - if (bandwidthMeter == null) { - bandwidthMeters.remove(i); - } - } - } - } - - private static ImmutableListMultimap - createInitialBitrateCountryGroupAssignment() { - ImmutableListMultimap.Builder countryGroupAssignment = - ImmutableListMultimap.builder(); - countryGroupAssignment.putAll("AD", 1, 2, 0, 0, 2); - countryGroupAssignment.putAll("AE", 1, 4, 4, 4, 2); - countryGroupAssignment.putAll("AF", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("AG", 4, 2, 1, 4, 2); - countryGroupAssignment.putAll("AI", 1, 2, 2, 2, 2); - countryGroupAssignment.putAll("AL", 1, 1, 1, 1, 2); - countryGroupAssignment.putAll("AM", 2, 2, 1, 3, 2); - countryGroupAssignment.putAll("AO", 3, 4, 3, 1, 2); - countryGroupAssignment.putAll("AR", 2, 4, 2, 1, 2); - countryGroupAssignment.putAll("AS", 2, 2, 3, 3, 2); - countryGroupAssignment.putAll("AT", 0, 2, 0, 0, 0); - countryGroupAssignment.putAll("AU", 0, 2, 0, 1, 1); - countryGroupAssignment.putAll("AW", 1, 2, 0, 4, 2); - countryGroupAssignment.putAll("AX", 0, 2, 2, 2, 2); - countryGroupAssignment.putAll("AZ", 3, 3, 3, 4, 2); - countryGroupAssignment.putAll("BA", 1, 1, 0, 1, 2); - countryGroupAssignment.putAll("BB", 0, 2, 0, 0, 2); - countryGroupAssignment.putAll("BD", 2, 0, 3, 3, 2); - countryGroupAssignment.putAll("BE", 0, 0, 2, 3, 2); - countryGroupAssignment.putAll("BF", 4, 4, 4, 2, 2); - countryGroupAssignment.putAll("BG", 0, 1, 0, 0, 2); - countryGroupAssignment.putAll("BH", 1, 0, 2, 4, 3); - countryGroupAssignment.putAll("BI", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("BJ", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("BL", 1, 2, 2, 2, 2); - countryGroupAssignment.putAll("BM", 0, 2, 0, 0, 2); - countryGroupAssignment.putAll("BN", 3, 2, 1, 0, 2); - countryGroupAssignment.putAll("BO", 1, 2, 4, 2, 2); - countryGroupAssignment.putAll("BQ", 1, 2, 1, 3, 2); - countryGroupAssignment.putAll("BR", 2, 4, 2, 2, 3); - countryGroupAssignment.putAll("BS", 2, 2, 1, 3, 2); - countryGroupAssignment.putAll("BT", 3, 0, 3, 2, 2); - countryGroupAssignment.putAll("BW", 3, 4, 1, 1, 2); - countryGroupAssignment.putAll("BY", 1, 1, 1, 2, 2); - countryGroupAssignment.putAll("BZ", 2, 2, 2, 2, 2); - countryGroupAssignment.putAll("CA", 0, 3, 1, 2, 4); - countryGroupAssignment.putAll("CD", 4, 3, 2, 1, 2); - countryGroupAssignment.putAll("CF", 4, 2, 3, 2, 2); - countryGroupAssignment.putAll("CG", 3, 4, 2, 2, 2); - countryGroupAssignment.putAll("CH", 0, 0, 0, 0, 2); - countryGroupAssignment.putAll("CI", 3, 3, 3, 3, 2); - countryGroupAssignment.putAll("CK", 2, 2, 3, 0, 2); - countryGroupAssignment.putAll("CL", 1, 1, 2, 2, 2); - countryGroupAssignment.putAll("CM", 3, 4, 3, 3, 2); - countryGroupAssignment.putAll("CN", 2, 2, 2, 1, 4); - countryGroupAssignment.putAll("CO", 2, 3, 4, 2, 2); - countryGroupAssignment.putAll("CR", 2, 3, 4, 4, 2); - countryGroupAssignment.putAll("CU", 4, 4, 2, 2, 2); - countryGroupAssignment.putAll("CV", 2, 3, 1, 0, 2); - countryGroupAssignment.putAll("CW", 1, 2, 0, 0, 2); - countryGroupAssignment.putAll("CY", 1, 1, 0, 0, 2); - countryGroupAssignment.putAll("CZ", 0, 1, 0, 0, 1); - countryGroupAssignment.putAll("DE", 0, 0, 1, 1, 0); - countryGroupAssignment.putAll("DJ", 4, 0, 4, 4, 2); - countryGroupAssignment.putAll("DK", 0, 0, 1, 0, 0); - countryGroupAssignment.putAll("DM", 1, 2, 2, 2, 2); - countryGroupAssignment.putAll("DO", 3, 4, 4, 4, 2); - countryGroupAssignment.putAll("DZ", 3, 3, 4, 4, 2); - countryGroupAssignment.putAll("EC", 2, 4, 3, 2, 2); - countryGroupAssignment.putAll("EE", 0, 1, 0, 0, 2); - countryGroupAssignment.putAll("EG", 3, 4, 3, 3, 2); - countryGroupAssignment.putAll("EH", 2, 2, 2, 2, 2); - countryGroupAssignment.putAll("ER", 4, 2, 2, 2, 2); - countryGroupAssignment.putAll("ES", 0, 1, 1, 1, 2); - countryGroupAssignment.putAll("ET", 4, 4, 4, 1, 2); - countryGroupAssignment.putAll("FI", 0, 0, 0, 0, 0); - countryGroupAssignment.putAll("FJ", 3, 0, 2, 2, 2); - countryGroupAssignment.putAll("FK", 4, 2, 2, 2, 2); - countryGroupAssignment.putAll("FM", 3, 2, 4, 4, 2); - countryGroupAssignment.putAll("FO", 1, 2, 0, 1, 2); - countryGroupAssignment.putAll("FR", 1, 1, 2, 0, 1); - countryGroupAssignment.putAll("GA", 3, 4, 1, 1, 2); - countryGroupAssignment.putAll("GB", 0, 0, 1, 1, 1); - countryGroupAssignment.putAll("GD", 1, 2, 2, 2, 2); - countryGroupAssignment.putAll("GE", 1, 1, 1, 3, 2); - countryGroupAssignment.putAll("GF", 2, 2, 2, 3, 2); - countryGroupAssignment.putAll("GG", 1, 2, 0, 0, 2); - countryGroupAssignment.putAll("GH", 3, 1, 3, 2, 2); - countryGroupAssignment.putAll("GI", 0, 2, 2, 0, 2); - countryGroupAssignment.putAll("GL", 1, 2, 0, 0, 2); - countryGroupAssignment.putAll("GM", 4, 3, 2, 4, 2); - countryGroupAssignment.putAll("GN", 3, 3, 4, 2, 2); - countryGroupAssignment.putAll("GP", 2, 1, 2, 3, 2); - countryGroupAssignment.putAll("GQ", 4, 2, 2, 4, 2); - countryGroupAssignment.putAll("GR", 1, 2, 0, 1, 2); - countryGroupAssignment.putAll("GT", 3, 2, 2, 1, 2); - countryGroupAssignment.putAll("GU", 1, 2, 3, 4, 2); - countryGroupAssignment.putAll("GW", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("GY", 3, 3, 3, 4, 2); - countryGroupAssignment.putAll("HK", 0, 1, 2, 3, 2); - countryGroupAssignment.putAll("HN", 3, 1, 3, 3, 2); - countryGroupAssignment.putAll("HR", 1, 1, 0, 0, 3); - countryGroupAssignment.putAll("HT", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("HU", 0, 0, 0, 0, 1); - countryGroupAssignment.putAll("ID", 3, 2, 3, 3, 2); - countryGroupAssignment.putAll("IE", 0, 0, 1, 1, 3); - countryGroupAssignment.putAll("IL", 1, 0, 2, 3, 4); - countryGroupAssignment.putAll("IM", 0, 2, 0, 1, 2); - countryGroupAssignment.putAll("IN", 2, 1, 3, 3, 2); - countryGroupAssignment.putAll("IO", 4, 2, 2, 4, 2); - countryGroupAssignment.putAll("IQ", 3, 3, 4, 4, 2); - countryGroupAssignment.putAll("IR", 3, 2, 3, 2, 2); - countryGroupAssignment.putAll("IS", 0, 2, 0, 0, 2); - countryGroupAssignment.putAll("IT", 0, 4, 1, 1, 2); - countryGroupAssignment.putAll("JE", 2, 2, 1, 2, 2); - countryGroupAssignment.putAll("JM", 3, 3, 4, 4, 2); - countryGroupAssignment.putAll("JO", 2, 2, 1, 1, 2); - countryGroupAssignment.putAll("JP", 0, 0, 0, 0, 3); - countryGroupAssignment.putAll("KE", 3, 4, 2, 2, 2); - countryGroupAssignment.putAll("KG", 2, 0, 1, 1, 2); - countryGroupAssignment.putAll("KH", 1, 0, 4, 3, 2); - countryGroupAssignment.putAll("KI", 4, 2, 4, 3, 2); - countryGroupAssignment.putAll("KM", 4, 3, 3, 3, 2); - countryGroupAssignment.putAll("KN", 1, 2, 2, 2, 2); - countryGroupAssignment.putAll("KP", 4, 2, 2, 2, 2); - countryGroupAssignment.putAll("KR", 0, 0, 1, 3, 1); - countryGroupAssignment.putAll("KW", 1, 3, 0, 0, 0); - countryGroupAssignment.putAll("KY", 1, 2, 0, 2, 2); - countryGroupAssignment.putAll("KZ", 2, 2, 2, 3, 2); - countryGroupAssignment.putAll("LA", 2, 2, 1, 1, 2); - countryGroupAssignment.putAll("LB", 3, 2, 0, 0, 2); - countryGroupAssignment.putAll("LC", 1, 2, 0, 1, 2); - countryGroupAssignment.putAll("LI", 0, 2, 2, 2, 2); - countryGroupAssignment.putAll("LK", 2, 0, 2, 3, 2); - countryGroupAssignment.putAll("LR", 3, 4, 4, 3, 2); - countryGroupAssignment.putAll("LS", 3, 3, 2, 3, 2); - countryGroupAssignment.putAll("LT", 0, 0, 0, 0, 2); - countryGroupAssignment.putAll("LU", 1, 0, 1, 1, 2); - countryGroupAssignment.putAll("LV", 0, 0, 0, 0, 2); - countryGroupAssignment.putAll("LY", 4, 2, 4, 4, 2); - countryGroupAssignment.putAll("MA", 3, 2, 2, 1, 2); - countryGroupAssignment.putAll("MC", 0, 2, 0, 0, 2); - countryGroupAssignment.putAll("MD", 1, 2, 0, 0, 2); - countryGroupAssignment.putAll("ME", 1, 2, 0, 1, 2); - countryGroupAssignment.putAll("MF", 1, 2, 1, 1, 2); - countryGroupAssignment.putAll("MG", 3, 4, 2, 2, 2); - countryGroupAssignment.putAll("MH", 4, 2, 2, 4, 2); - countryGroupAssignment.putAll("MK", 1, 1, 0, 0, 2); - countryGroupAssignment.putAll("ML", 4, 4, 2, 2, 2); - countryGroupAssignment.putAll("MM", 2, 3, 3, 3, 2); - countryGroupAssignment.putAll("MN", 2, 4, 1, 2, 2); - countryGroupAssignment.putAll("MO", 0, 2, 4, 4, 2); - countryGroupAssignment.putAll("MP", 0, 2, 2, 2, 2); - countryGroupAssignment.putAll("MQ", 2, 2, 2, 3, 2); - countryGroupAssignment.putAll("MR", 3, 0, 4, 3, 2); - countryGroupAssignment.putAll("MS", 1, 2, 2, 2, 2); - countryGroupAssignment.putAll("MT", 0, 2, 0, 0, 2); - countryGroupAssignment.putAll("MU", 3, 1, 1, 2, 2); - countryGroupAssignment.putAll("MV", 4, 3, 3, 4, 2); - countryGroupAssignment.putAll("MW", 4, 2, 1, 0, 2); - countryGroupAssignment.putAll("MX", 2, 4, 3, 4, 2); - countryGroupAssignment.putAll("MY", 1, 0, 3, 2, 2); - countryGroupAssignment.putAll("MZ", 3, 3, 2, 1, 2); - countryGroupAssignment.putAll("NA", 4, 3, 3, 2, 2); - countryGroupAssignment.putAll("NC", 2, 0, 3, 4, 2); - countryGroupAssignment.putAll("NE", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("NF", 2, 2, 2, 2, 2); - countryGroupAssignment.putAll("NG", 3, 3, 2, 2, 2); - countryGroupAssignment.putAll("NI", 2, 1, 4, 4, 2); - countryGroupAssignment.putAll("NL", 0, 2, 3, 2, 0); - countryGroupAssignment.putAll("NO", 0, 1, 2, 0, 0); - countryGroupAssignment.putAll("NP", 2, 0, 4, 2, 2); - countryGroupAssignment.putAll("NR", 3, 2, 2, 1, 2); - countryGroupAssignment.putAll("NU", 4, 2, 2, 2, 2); - countryGroupAssignment.putAll("NZ", 0, 2, 1, 2, 4); - countryGroupAssignment.putAll("OM", 2, 2, 1, 3, 2); - countryGroupAssignment.putAll("PA", 1, 3, 3, 3, 2); - countryGroupAssignment.putAll("PE", 2, 3, 4, 4, 2); - countryGroupAssignment.putAll("PF", 2, 2, 2, 1, 2); - countryGroupAssignment.putAll("PG", 4, 4, 3, 2, 2); - countryGroupAssignment.putAll("PH", 2, 1, 3, 3, 4); - countryGroupAssignment.putAll("PK", 3, 2, 3, 3, 2); - countryGroupAssignment.putAll("PL", 1, 0, 1, 2, 3); - countryGroupAssignment.putAll("PM", 0, 2, 2, 2, 2); - countryGroupAssignment.putAll("PR", 2, 1, 2, 2, 4); - countryGroupAssignment.putAll("PS", 3, 3, 2, 2, 2); - countryGroupAssignment.putAll("PT", 0, 1, 1, 0, 2); - countryGroupAssignment.putAll("PW", 1, 2, 4, 0, 2); - countryGroupAssignment.putAll("PY", 2, 0, 3, 2, 2); - countryGroupAssignment.putAll("QA", 2, 3, 1, 2, 3); - countryGroupAssignment.putAll("RE", 1, 0, 2, 2, 2); - countryGroupAssignment.putAll("RO", 0, 1, 0, 1, 1); - countryGroupAssignment.putAll("RS", 1, 2, 0, 0, 2); - countryGroupAssignment.putAll("RU", 0, 1, 0, 1, 2); - countryGroupAssignment.putAll("RW", 3, 3, 4, 1, 2); - countryGroupAssignment.putAll("SA", 2, 2, 2, 1, 2); - countryGroupAssignment.putAll("SB", 4, 2, 3, 2, 2); - countryGroupAssignment.putAll("SC", 4, 1, 1, 3, 2); - countryGroupAssignment.putAll("SD", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("SE", 0, 0, 0, 0, 0); - countryGroupAssignment.putAll("SG", 1, 0, 1, 2, 3); - countryGroupAssignment.putAll("SH", 4, 2, 2, 2, 2); - countryGroupAssignment.putAll("SI", 0, 0, 0, 0, 2); - countryGroupAssignment.putAll("SJ", 2, 2, 2, 2, 2); - countryGroupAssignment.putAll("SK", 0, 1, 0, 0, 2); - countryGroupAssignment.putAll("SL", 4, 3, 4, 0, 2); - countryGroupAssignment.putAll("SM", 0, 2, 2, 2, 2); - countryGroupAssignment.putAll("SN", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("SO", 3, 3, 3, 4, 2); - countryGroupAssignment.putAll("SR", 3, 2, 2, 2, 2); - countryGroupAssignment.putAll("SS", 4, 4, 3, 3, 2); - countryGroupAssignment.putAll("ST", 2, 2, 1, 2, 2); - countryGroupAssignment.putAll("SV", 2, 1, 4, 3, 2); - countryGroupAssignment.putAll("SX", 2, 2, 1, 0, 2); - countryGroupAssignment.putAll("SY", 4, 3, 3, 2, 2); - countryGroupAssignment.putAll("SZ", 4, 3, 2, 4, 2); - countryGroupAssignment.putAll("TC", 2, 2, 2, 0, 2); - countryGroupAssignment.putAll("TD", 4, 3, 4, 4, 2); - countryGroupAssignment.putAll("TG", 3, 2, 2, 4, 2); - countryGroupAssignment.putAll("TH", 0, 3, 2, 3, 2); - countryGroupAssignment.putAll("TJ", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("TL", 4, 0, 4, 4, 2); - countryGroupAssignment.putAll("TM", 4, 2, 4, 3, 2); - countryGroupAssignment.putAll("TN", 2, 2, 1, 2, 2); - countryGroupAssignment.putAll("TO", 3, 2, 4, 3, 2); - countryGroupAssignment.putAll("TR", 1, 2, 0, 1, 2); - countryGroupAssignment.putAll("TT", 1, 4, 0, 1, 2); - countryGroupAssignment.putAll("TV", 3, 2, 2, 4, 2); - countryGroupAssignment.putAll("TW", 0, 0, 0, 0, 1); - countryGroupAssignment.putAll("TZ", 3, 3, 3, 2, 2); - countryGroupAssignment.putAll("UA", 0, 3, 1, 1, 2); - countryGroupAssignment.putAll("UG", 3, 2, 3, 3, 2); - countryGroupAssignment.putAll("US", 1, 1, 2, 2, 4); - countryGroupAssignment.putAll("UY", 2, 1, 1, 1, 2); - countryGroupAssignment.putAll("UZ", 2, 1, 3, 4, 2); - countryGroupAssignment.putAll("VC", 1, 2, 2, 2, 2); - countryGroupAssignment.putAll("VE", 4, 4, 4, 4, 2); - countryGroupAssignment.putAll("VG", 2, 2, 1, 1, 2); - countryGroupAssignment.putAll("VI", 1, 2, 1, 2, 2); - countryGroupAssignment.putAll("VN", 0, 1, 3, 4, 2); - countryGroupAssignment.putAll("VU", 4, 0, 3, 1, 2); - countryGroupAssignment.putAll("WF", 4, 2, 2, 2, 2); - countryGroupAssignment.putAll("WS", 3, 1, 3, 1, 2); - countryGroupAssignment.putAll("XK", 0, 1, 1, 1, 2); - countryGroupAssignment.putAll("YE", 4, 4, 4, 3, 2); - countryGroupAssignment.putAll("YT", 4, 2, 2, 3, 2); - countryGroupAssignment.putAll("ZA", 3, 3, 2, 1, 2); - countryGroupAssignment.putAll("ZM", 3, 2, 3, 3, 2); - countryGroupAssignment.putAll("ZW", 3, 2, 4, 3, 2); - return countryGroupAssignment.build(); - } -} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/TimeToFirstByteEstimator.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/TimeToFirstByteEstimator.java new file mode 100644 index 0000000000..fce1f29ea3 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/TimeToFirstByteEstimator.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.upstream; + +import com.google.android.exoplayer2.C; + +/** Provides an estimate of the time to first byte of a transfer. */ +public interface TimeToFirstByteEstimator { + /** + * Returns the estimated time to first byte of the response body, in microseconds, or {@link + * C#TIME_UNSET} if no estimate is available. + */ + long getTimeToFirstByteEstimateUs(); + + /** Resets the estimator. The estimator should drop all samples and reset to its initial state. */ + void reset(); + + /** + * Called when a transfer is being initialized. + * + * @param dataSpec Describes the data for which the transfer is initialized. + */ + void onTransferInitializing(DataSpec dataSpec); + + /** + * Called when a transfer starts. + * + * @param dataSpec Describes the data being transferred. + */ + void onTransferStart(DataSpec dataSpec); +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/ExperimentalBandwidthMeterTest.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/ExperimentalBandwidthMeterTest.java deleted file mode 100644 index cb4675badb..0000000000 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/ExperimentalBandwidthMeterTest.java +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.upstream; - -import static android.net.NetworkInfo.State.CONNECTED; -import static android.net.NetworkInfo.State.DISCONNECTED; -import static com.google.common.truth.Truth.assertThat; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.NetworkInfo.DetailedState; -import android.net.Uri; -import android.telephony.TelephonyManager; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.testutil.FakeClock; -import com.google.android.exoplayer2.testutil.FakeDataSource; -import java.util.Random; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.Shadows; -import org.robolectric.shadows.ShadowNetworkInfo; - -/** Unit test for {@link ExperimentalBandwidthMeter}. */ -@RunWith(AndroidJUnit4.class) -public final class ExperimentalBandwidthMeterTest { - - private static final int SIMULATED_TRANSFER_COUNT = 100; - private static final String FAST_COUNTRY_ISO = "EE"; - private static final String SLOW_COUNTRY_ISO = "PG"; - - private TelephonyManager telephonyManager; - private ConnectivityManager connectivityManager; - private NetworkInfo networkInfoOffline; - private NetworkInfo networkInfoWifi; - private NetworkInfo networkInfo2g; - private NetworkInfo networkInfo3g; - private NetworkInfo networkInfo4g; - private NetworkInfo networkInfoEthernet; - - @Before - public void setUp() { - connectivityManager = - (ConnectivityManager) - ApplicationProvider.getApplicationContext() - .getSystemService(Context.CONNECTIVITY_SERVICE); - telephonyManager = - (TelephonyManager) - ApplicationProvider.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE); - Shadows.shadowOf(telephonyManager).setNetworkCountryIso(FAST_COUNTRY_ISO); - networkInfoOffline = - ShadowNetworkInfo.newInstance( - DetailedState.DISCONNECTED, - ConnectivityManager.TYPE_WIFI, - /* subType= */ 0, - /* isAvailable= */ false, - DISCONNECTED); - networkInfoWifi = - ShadowNetworkInfo.newInstance( - DetailedState.CONNECTED, - ConnectivityManager.TYPE_WIFI, - /* subType= */ 0, - /* isAvailable= */ true, - CONNECTED); - networkInfo2g = - ShadowNetworkInfo.newInstance( - DetailedState.CONNECTED, - ConnectivityManager.TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_GPRS, - /* isAvailable= */ true, - CONNECTED); - networkInfo3g = - ShadowNetworkInfo.newInstance( - DetailedState.CONNECTED, - ConnectivityManager.TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_HSDPA, - /* isAvailable= */ true, - CONNECTED); - networkInfo4g = - ShadowNetworkInfo.newInstance( - DetailedState.CONNECTED, - ConnectivityManager.TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_LTE, - /* isAvailable= */ true, - CONNECTED); - networkInfoEthernet = - ShadowNetworkInfo.newInstance( - DetailedState.CONNECTED, - ConnectivityManager.TYPE_ETHERNET, - /* subType= */ 0, - /* isAvailable= */ true, - CONNECTED); - } - - @Test - public void defaultInitialBitrateEstimate_forWifi_isGreaterThanEstimateFor2G() { - setActiveNetworkInfo(networkInfoWifi); - ExperimentalBandwidthMeter bandwidthMeterWifi = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateWifi = bandwidthMeterWifi.getBitrateEstimate(); - - setActiveNetworkInfo(networkInfo2g); - ExperimentalBandwidthMeter bandwidthMeter2g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate2g = bandwidthMeter2g.getBitrateEstimate(); - - assertThat(initialEstimateWifi).isGreaterThan(initialEstimate2g); - } - - @Test - public void defaultInitialBitrateEstimate_forWifi_isGreaterThanEstimateFor3G() { - setActiveNetworkInfo(networkInfoWifi); - ExperimentalBandwidthMeter bandwidthMeterWifi = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateWifi = bandwidthMeterWifi.getBitrateEstimate(); - - setActiveNetworkInfo(networkInfo3g); - ExperimentalBandwidthMeter bandwidthMeter3g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate3g = bandwidthMeter3g.getBitrateEstimate(); - - assertThat(initialEstimateWifi).isGreaterThan(initialEstimate3g); - } - - @Test - public void defaultInitialBitrateEstimate_forEthernet_isGreaterThanEstimateFor2G() { - setActiveNetworkInfo(networkInfoEthernet); - ExperimentalBandwidthMeter bandwidthMeterEthernet = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateEthernet = bandwidthMeterEthernet.getBitrateEstimate(); - - setActiveNetworkInfo(networkInfo2g); - ExperimentalBandwidthMeter bandwidthMeter2g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate2g = bandwidthMeter2g.getBitrateEstimate(); - - assertThat(initialEstimateEthernet).isGreaterThan(initialEstimate2g); - } - - @Test - public void defaultInitialBitrateEstimate_forEthernet_isGreaterThanEstimateFor3G() { - setActiveNetworkInfo(networkInfoEthernet); - ExperimentalBandwidthMeter bandwidthMeterEthernet = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateEthernet = bandwidthMeterEthernet.getBitrateEstimate(); - - setActiveNetworkInfo(networkInfo3g); - ExperimentalBandwidthMeter bandwidthMeter3g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate3g = bandwidthMeter3g.getBitrateEstimate(); - - assertThat(initialEstimateEthernet).isGreaterThan(initialEstimate3g); - } - - @Test - public void defaultInitialBitrateEstimate_for4G_isGreaterThanEstimateFor2G() { - setActiveNetworkInfo(networkInfo4g); - ExperimentalBandwidthMeter bandwidthMeter4g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate4g = bandwidthMeter4g.getBitrateEstimate(); - - setActiveNetworkInfo(networkInfo2g); - ExperimentalBandwidthMeter bandwidthMeter2g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate2g = bandwidthMeter2g.getBitrateEstimate(); - - assertThat(initialEstimate4g).isGreaterThan(initialEstimate2g); - } - - @Test - public void defaultInitialBitrateEstimate_for4G_isGreaterThanEstimateFor3G() { - setActiveNetworkInfo(networkInfo4g); - ExperimentalBandwidthMeter bandwidthMeter4g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate4g = bandwidthMeter4g.getBitrateEstimate(); - - setActiveNetworkInfo(networkInfo3g); - ExperimentalBandwidthMeter bandwidthMeter3g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate3g = bandwidthMeter3g.getBitrateEstimate(); - - assertThat(initialEstimate4g).isGreaterThan(initialEstimate3g); - } - - @Test - public void defaultInitialBitrateEstimate_for3G_isGreaterThanEstimateFor2G() { - setActiveNetworkInfo(networkInfo3g); - ExperimentalBandwidthMeter bandwidthMeter3g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate3g = bandwidthMeter3g.getBitrateEstimate(); - - setActiveNetworkInfo(networkInfo2g); - ExperimentalBandwidthMeter bandwidthMeter2g = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate2g = bandwidthMeter2g.getBitrateEstimate(); - - assertThat(initialEstimate3g).isGreaterThan(initialEstimate2g); - } - - @Test - public void defaultInitialBitrateEstimate_forOffline_isReasonable() { - setActiveNetworkInfo(networkInfoOffline); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isGreaterThan(100_000L); - assertThat(initialEstimate).isLessThan(50_000_000L); - } - - @Test - public void - defaultInitialBitrateEstimate_forWifi_forFastCountry_isGreaterThanEstimateForSlowCountry() { - setActiveNetworkInfo(networkInfoWifi); - setNetworkCountryIso(FAST_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterFast = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateFast = bandwidthMeterFast.getBitrateEstimate(); - - setNetworkCountryIso(SLOW_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterSlow = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateSlow = bandwidthMeterSlow.getBitrateEstimate(); - - assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow); - } - - @Test - public void - defaultInitialBitrateEstimate_forEthernet_forFastCountry_isGreaterThanEstimateForSlowCountry() { - setActiveNetworkInfo(networkInfoEthernet); - setNetworkCountryIso(FAST_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterFast = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateFast = bandwidthMeterFast.getBitrateEstimate(); - - setNetworkCountryIso(SLOW_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterSlow = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateSlow = bandwidthMeterSlow.getBitrateEstimate(); - - assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow); - } - - @Test - public void - defaultInitialBitrateEstimate_for2G_forFastCountry_isGreaterThanEstimateForSlowCountry() { - setActiveNetworkInfo(networkInfo2g); - setNetworkCountryIso(FAST_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterFast = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateFast = bandwidthMeterFast.getBitrateEstimate(); - - setNetworkCountryIso(SLOW_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterSlow = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateSlow = bandwidthMeterSlow.getBitrateEstimate(); - - assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow); - } - - @Test - public void - defaultInitialBitrateEstimate_for3G_forFastCountry_isGreaterThanEstimateForSlowCountry() { - setActiveNetworkInfo(networkInfo3g); - setNetworkCountryIso(FAST_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterFast = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateFast = bandwidthMeterFast.getBitrateEstimate(); - - setNetworkCountryIso(SLOW_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterSlow = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateSlow = bandwidthMeterSlow.getBitrateEstimate(); - - assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow); - } - - @Test - public void - defaultInitialBitrateEstimate_for4g_forFastCountry_isGreaterThanEstimateForSlowCountry() { - setActiveNetworkInfo(networkInfo4g); - setNetworkCountryIso(FAST_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterFast = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateFast = bandwidthMeterFast.getBitrateEstimate(); - - setNetworkCountryIso(SLOW_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterSlow = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateSlow = bandwidthMeterSlow.getBitrateEstimate(); - - assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow); - } - - @Test - public void initialBitrateEstimateOverwrite_whileConnectedToNetwork_setsInitialEstimate() { - setActiveNetworkInfo(networkInfoWifi); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isEqualTo(123456789); - } - - @Test - public void initialBitrateEstimateOverwrite_whileOffline_setsInitialEstimate() { - setActiveNetworkInfo(networkInfoOffline); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isEqualTo(123456789); - } - - @Test - public void initialBitrateEstimateOverwrite_forWifi_whileConnectedToWifi_setsInitialEstimate() { - setActiveNetworkInfo(networkInfoWifi); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_WIFI, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isEqualTo(123456789); - } - - @Test - public void - initialBitrateEstimateOverwrite_forWifi_whileConnectedToOtherNetwork_doesNotSetInitialEstimate() { - setActiveNetworkInfo(networkInfo2g); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_WIFI, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isNotEqualTo(123456789); - } - - @Test - public void - initialBitrateEstimateOverwrite_forEthernet_whileConnectedToEthernet_setsInitialEstimate() { - setActiveNetworkInfo(networkInfoEthernet); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_ETHERNET, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isEqualTo(123456789); - } - - @Test - public void - initialBitrateEstimateOverwrite_forEthernet_whileConnectedToOtherNetwork_doesNotSetInitialEstimate() { - setActiveNetworkInfo(networkInfo2g); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_WIFI, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isNotEqualTo(123456789); - } - - @Test - public void initialBitrateEstimateOverwrite_for2G_whileConnectedTo2G_setsInitialEstimate() { - setActiveNetworkInfo(networkInfo2g); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_2G, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isEqualTo(123456789); - } - - @Test - public void - initialBitrateEstimateOverwrite_for2G_whileConnectedToOtherNetwork_doesNotSetInitialEstimate() { - setActiveNetworkInfo(networkInfoWifi); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_2G, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isNotEqualTo(123456789); - } - - @Test - public void initialBitrateEstimateOverwrite_for3G_whileConnectedTo3G_setsInitialEstimate() { - setActiveNetworkInfo(networkInfo3g); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_3G, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isEqualTo(123456789); - } - - @Test - public void - initialBitrateEstimateOverwrite_for3G_whileConnectedToOtherNetwork_doesNotSetInitialEstimate() { - setActiveNetworkInfo(networkInfoWifi); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_3G, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isNotEqualTo(123456789); - } - - @Test - public void initialBitrateEstimateOverwrite_for4G_whileConnectedTo4G_setsInitialEstimate() { - setActiveNetworkInfo(networkInfo4g); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_4G, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isEqualTo(123456789); - } - - @Test - public void - initialBitrateEstimateOverwrite_for4G_whileConnectedToOtherNetwork_doesNotSetInitialEstimate() { - setActiveNetworkInfo(networkInfoWifi); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_4G, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isNotEqualTo(123456789); - } - - @Test - public void initialBitrateEstimateOverwrite_forOffline_whileOffline_setsInitialEstimate() { - setActiveNetworkInfo(networkInfoOffline); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_OFFLINE, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isEqualTo(123456789); - } - - @Test - public void - initialBitrateEstimateOverwrite_forOffline_whileConnectedToNetwork_doesNotSetInitialEstimate() { - setActiveNetworkInfo(networkInfoWifi); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(C.NETWORK_TYPE_OFFLINE, 123456789) - .build(); - long initialEstimate = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimate).isNotEqualTo(123456789); - } - - @Test - public void initialBitrateEstimateOverwrite_forCountry_usesDefaultValuesForCountry() { - setNetworkCountryIso(SLOW_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterSlow = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateSlow = bandwidthMeterSlow.getBitrateEstimate(); - - setNetworkCountryIso(FAST_COUNTRY_ISO); - ExperimentalBandwidthMeter bandwidthMeterFastWithSlowOverwrite = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setInitialBitrateEstimate(SLOW_COUNTRY_ISO) - .build(); - long initialEstimateFastWithSlowOverwrite = - bandwidthMeterFastWithSlowOverwrite.getBitrateEstimate(); - - assertThat(initialEstimateFastWithSlowOverwrite).isEqualTo(initialEstimateSlow); - } - - @Test - public void networkTypeOverride_updatesBitrateEstimate() { - setActiveNetworkInfo(networkInfoEthernet); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); - long initialEstimateEthernet = bandwidthMeter.getBitrateEstimate(); - - bandwidthMeter.setNetworkTypeOverride(C.NETWORK_TYPE_2G); - long initialEstimate2g = bandwidthMeter.getBitrateEstimate(); - - assertThat(initialEstimateEthernet).isGreaterThan(initialEstimate2g); - } - - @Test - public void networkTypeOverride_doesFullReset() { - // Simulate transfers for an ethernet connection. - setActiveNetworkInfo(networkInfoEthernet); - FakeClock clock = new FakeClock(/* initialTimeMs= */ 0); - ExperimentalBandwidthMeter bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setClock(clock) - .build(); - long[] bitrateEstimatesWithNewInstance = simulateTransfers(bandwidthMeter, clock); - - // Create a new instance and seed with some transfers. - setActiveNetworkInfo(networkInfo2g); - bandwidthMeter = - new ExperimentalBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) - .setClock(clock) - .build(); - simulateTransfers(bandwidthMeter, clock); - - // Override the network type to ethernet and simulate transfers again. - bandwidthMeter.setNetworkTypeOverride(C.NETWORK_TYPE_ETHERNET); - long[] bitrateEstimatesAfterReset = simulateTransfers(bandwidthMeter, clock); - - // If overriding the network type fully reset the bandwidth meter, we expect the bitrate - // estimates generated during simulation to be the same. - assertThat(bitrateEstimatesAfterReset).isEqualTo(bitrateEstimatesWithNewInstance); - } - - @Test - public void defaultInitialBitrateEstimate_withoutContext_isReasonable() { - ExperimentalBandwidthMeter bandwidthMeterWithBuilder = - new ExperimentalBandwidthMeter.Builder(/* context= */ null).build(); - long initialEstimateWithBuilder = bandwidthMeterWithBuilder.getBitrateEstimate(); - - assertThat(initialEstimateWithBuilder).isGreaterThan(100_000L); - assertThat(initialEstimateWithBuilder).isLessThan(50_000_000L); - } - - private void setActiveNetworkInfo(NetworkInfo networkInfo) { - Shadows.shadowOf(connectivityManager).setActiveNetworkInfo(networkInfo); - } - - private void setNetworkCountryIso(String countryIso) { - Shadows.shadowOf(telephonyManager).setNetworkCountryIso(countryIso); - } - - private static long[] simulateTransfers( - ExperimentalBandwidthMeter bandwidthMeter, FakeClock clock) { - long[] bitrateEstimates = new long[SIMULATED_TRANSFER_COUNT]; - Random random = new Random(/* seed= */ 0); - DataSource dataSource = new FakeDataSource(); - DataSpec dataSpec = new DataSpec(Uri.parse("https://test.com")); - for (int i = 0; i < SIMULATED_TRANSFER_COUNT; i++) { - bandwidthMeter.onTransferStart(dataSource, dataSpec, /* isNetwork= */ true); - clock.advanceTime(random.nextInt(/* bound= */ 5000)); - bandwidthMeter.onBytesTransferred( - dataSource, - dataSpec, - /* isNetwork= */ true, - /* bytes= */ random.nextInt(5 * 1024 * 1024)); - bandwidthMeter.onTransferEnd(dataSource, dataSpec, /* isNetwork= */ true); - bitrateEstimates[i] = bandwidthMeter.getBitrateEstimate(); - } - return bitrateEstimates; - } -}