Commit 40480334 by tonihei Committed by Ian Baker

Move static network type observation out of DefaultBandwidthMeter

In order to detect 5G-NSA, we need to remove Util.getNetworkType
and replace it with a class that is actively listening to changes.
The network type observation in DefaultBandwidthMeter already provides
the right framework to integrate this and in order to facilitate
further changes we move it to a separate Util class.

The overall effect of this change should be a complete no-op.

PiperOrigin-RevId: 363384567
parent 3e613beb
...@@ -15,29 +15,22 @@ ...@@ -15,29 +15,22 @@
*/ */
package com.google.android.exoplayer2.upstream; package com.google.android.exoplayer2.upstream;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.upstream.BandwidthMeter.EventListener.EventDispatcher; import com.google.android.exoplayer2.upstream.BandwidthMeter.EventListener.EventDispatcher;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.NetworkTypeObserver;
import com.google.android.exoplayer2.util.SlidingPercentile; import com.google.android.exoplayer2.util.SlidingPercentile;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* Estimates bandwidth by listening to data transfers. * Estimates bandwidth by listening to data transfers.
...@@ -262,7 +255,6 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList ...@@ -262,7 +255,6 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
private static final int ELAPSED_MILLIS_FOR_ESTIMATE = 2000; private static final int ELAPSED_MILLIS_FOR_ESTIMATE = 2000;
private static final int BYTES_TRANSFERRED_FOR_ESTIMATE = 512 * 1024; private static final int BYTES_TRANSFERRED_FOR_ESTIMATE = 512 * 1024;
@Nullable private final Context context;
private final ImmutableMap<Integer, Long> initialBitrateEstimates; private final ImmutableMap<Integer, Long> initialBitrateEstimates;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final SlidingPercentile slidingPercentile; private final SlidingPercentile slidingPercentile;
...@@ -298,7 +290,6 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList ...@@ -298,7 +290,6 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
int maxWeight, int maxWeight,
Clock clock, Clock clock,
boolean resetOnNetworkTypeChange) { boolean resetOnNetworkTypeChange) {
this.context = context == null ? null : context.getApplicationContext();
this.initialBitrateEstimates = ImmutableMap.copyOf(initialBitrateEstimates); this.initialBitrateEstimates = ImmutableMap.copyOf(initialBitrateEstimates);
this.eventDispatcher = new EventDispatcher(); this.eventDispatcher = new EventDispatcher();
this.slidingPercentile = new SlidingPercentile(maxWeight); this.slidingPercentile = new SlidingPercentile(maxWeight);
...@@ -306,11 +297,10 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList ...@@ -306,11 +297,10 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
// Set the initial network type and bitrate estimate // Set the initial network type and bitrate estimate
networkType = context == null ? C.NETWORK_TYPE_UNKNOWN : Util.getNetworkType(context); networkType = context == null ? C.NETWORK_TYPE_UNKNOWN : Util.getNetworkType(context);
bitrateEstimate = getInitialBitrateEstimateForNetworkType(networkType); bitrateEstimate = getInitialBitrateEstimateForNetworkType(networkType);
// Register to receive connectivity actions if possible. // Register to receive network change information if possible.
if (context != null && resetOnNetworkTypeChange) { if (context != null && resetOnNetworkTypeChange) {
ConnectivityActionReceiver connectivityActionReceiver = NetworkTypeObserver networkTypeObserver = NetworkTypeObserver.getInstance(context);
ConnectivityActionReceiver.getInstance(context); networkTypeObserver.register(/* listener= */ this::onNetworkTypeChanged);
connectivityActionReceiver.register(/* bandwidthMeter= */ this);
} }
} }
...@@ -325,7 +315,7 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList ...@@ -325,7 +315,7 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
public synchronized void setNetworkTypeOverride(@C.NetworkType int networkType) { public synchronized void setNetworkTypeOverride(@C.NetworkType int networkType) {
networkTypeOverride = networkType; networkTypeOverride = networkType;
networkTypeOverrideSet = true; networkTypeOverrideSet = true;
onConnectivityAction(); onNetworkTypeChanged(networkType);
} }
@Override @Override
...@@ -400,11 +390,10 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList ...@@ -400,11 +390,10 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
streamCount--; streamCount--;
} }
private synchronized void onConnectivityAction() { private synchronized void onNetworkTypeChanged(@C.NetworkType int networkType) {
int networkType = if (networkTypeOverrideSet) {
networkTypeOverrideSet networkType = networkTypeOverride;
? networkTypeOverride }
: (context == null ? C.NETWORK_TYPE_UNKNOWN : Util.getNetworkType(context));
if (this.networkType == networkType) { if (this.networkType == networkType) {
return; return;
} }
...@@ -455,70 +444,6 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList ...@@ -455,70 +444,6 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
return isNetwork && !dataSpec.isFlagSet(DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED); return isNetwork && !dataSpec.isFlagSet(DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED);
} }
/*
* Note: This class only holds a weak reference to DefaultBandwidthMeter instances. It should not
* be made non-static, since doing so adds a strong reference (i.e. DefaultBandwidthMeter.this).
*/
private static class ConnectivityActionReceiver extends BroadcastReceiver {
private static @MonotonicNonNull ConnectivityActionReceiver staticInstance;
private final Handler mainHandler;
private final ArrayList<WeakReference<DefaultBandwidthMeter>> 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(DefaultBandwidthMeter 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<DefaultBandwidthMeter> bandwidthMeterReference = bandwidthMeters.get(i);
DefaultBandwidthMeter bandwidthMeter = bandwidthMeterReference.get();
if (bandwidthMeter != null) {
updateBandwidthMeter(bandwidthMeter);
}
}
}
private void updateBandwidthMeter(DefaultBandwidthMeter bandwidthMeter) {
bandwidthMeter.onConnectivityAction();
}
private void removeClearedReferences() {
for (int i = bandwidthMeters.size() - 1; i >= 0; i--) {
WeakReference<DefaultBandwidthMeter> bandwidthMeterReference = bandwidthMeters.get(i);
DefaultBandwidthMeter bandwidthMeter = bandwidthMeterReference.get();
if (bandwidthMeter == null) {
bandwidthMeters.remove(i);
}
}
}
}
private static ImmutableListMultimap<String, Integer> private static ImmutableListMultimap<String, Integer>
createInitialBitrateCountryGroupAssignment() { createInitialBitrateCountryGroupAssignment() {
ImmutableListMultimap.Builder<String, Integer> countryGroupAssignment = ImmutableListMultimap.Builder<String, Integer> countryGroupAssignment =
......
/*
* Copyright 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.util;
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 java.lang.ref.WeakReference;
import java.util.concurrent.CopyOnWriteArrayList;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* Observer for network type changes.
*
* <p>{@link #register Registered} listeners are informed at registration and whenever the network
* type changes.
*
* <p>The current network type can also be {@link #getNetworkType queried} without registration.
*/
public final class NetworkTypeObserver {
/** A listener for network type changes. */
public interface Listener {
/**
* Called when the network type changed or when the listener is first registered.
*
* <p>This method is always called on the main thread.
*/
void onNetworkTypeChanged(@C.NetworkType int networkType);
}
private static @MonotonicNonNull NetworkTypeObserver staticInstance;
private final Context context;
private final Handler mainHandler;
// This class needs to hold weak references as it doesn't require listeners to unregister.
private final CopyOnWriteArrayList<WeakReference<Listener>> listeners;
/**
* Returns a network type observer instance.
*
* @param context A {@link Context}.
*/
public static synchronized NetworkTypeObserver getInstance(Context context) {
if (staticInstance == null) {
staticInstance = new NetworkTypeObserver(context);
}
return staticInstance;
}
private NetworkTypeObserver(Context context) {
this.context = context.getApplicationContext();
mainHandler = new Handler(Looper.getMainLooper());
listeners = new CopyOnWriteArrayList<>();
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(/* receiver= */ new Receiver(), filter);
}
/**
* Registers a listener.
*
* <p>The current network type will be reported to the listener after registration.
*
* @param listener The {@link Listener}.
*/
public void register(Listener listener) {
removeClearedReferences();
listeners.add(new WeakReference<>(listener));
// 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 listener).
mainHandler.post(() -> listener.onNetworkTypeChanged(getNetworkType()));
}
/** Returns the current network type. */
@C.NetworkType
public int getNetworkType() {
return Util.getNetworkType(context);
}
private void removeClearedReferences() {
for (WeakReference<Listener> listenerReference : listeners) {
if (listenerReference.get() == null) {
listeners.remove(listenerReference);
}
}
}
private final class Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (isInitialStickyBroadcast()) {
return;
}
@C.NetworkType int networkType = getNetworkType();
for (WeakReference<Listener> listenerReference : listeners) {
@Nullable Listener listener = listenerReference.get();
if (listener != null) {
listener.onNetworkTypeChanged(networkType);
} else {
listeners.remove(listenerReference);
}
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment