mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Move NetworkTypeObserver operations off main thread
PiperOrigin-RevId: 721291681
This commit is contained in:
parent
9af43c7381
commit
6b31b4620c
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2025 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 androidx.media3.common.util;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/** A utility class to obtain an {@link Executor} for background tasks. */
|
||||
@UnstableApi
|
||||
public final class BackgroundExecutor {
|
||||
|
||||
@SuppressWarnings("NonFinalStaticField")
|
||||
@Nullable
|
||||
private static Executor staticInstance;
|
||||
|
||||
/**
|
||||
* Returns an {@link Executor} for background tasks.
|
||||
*
|
||||
* <p>Must only be used for quick, high-priority tasks to ensure other background tasks are not
|
||||
* blocked.
|
||||
*/
|
||||
public static synchronized Executor get() {
|
||||
if (staticInstance == null) {
|
||||
staticInstance = Util.newSingleThreadExecutor("ExoPlayer:BackgroundExecutor");
|
||||
}
|
||||
return staticInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Executor} to be returned from {@link #get()}.
|
||||
*
|
||||
* @param executor An {@link Executor} that runs tasks on background threads and should only be
|
||||
* used for quick, high-priority tasks to ensure other background tasks are not blocked.
|
||||
*/
|
||||
public static synchronized void set(Executor executor) {
|
||||
staticInstance = executor;
|
||||
}
|
||||
|
||||
private BackgroundExecutor() {}
|
||||
}
|
@ -17,6 +17,7 @@ package androidx.media3.common.util;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@ -34,8 +35,11 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.media3.common.C;
|
||||
import com.google.errorprone.annotations.InlineMe;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Observer for network type changes.
|
||||
@ -61,14 +65,16 @@ public final class NetworkTypeObserver {
|
||||
|
||||
@Nullable private static NetworkTypeObserver staticInstance;
|
||||
|
||||
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;
|
||||
private final Object networkTypeLock;
|
||||
private final Executor backgroundExecutor;
|
||||
private final CopyOnWriteArrayList<ListenerHolder> listeners;
|
||||
private final Object lock;
|
||||
|
||||
@GuardedBy("networkTypeLock")
|
||||
@GuardedBy("lock")
|
||||
private @C.NetworkType int networkType;
|
||||
|
||||
@GuardedBy("lock")
|
||||
private boolean isInitialized;
|
||||
|
||||
/**
|
||||
* Returns a network type observer instance.
|
||||
*
|
||||
@ -88,13 +94,22 @@ public final class NetworkTypeObserver {
|
||||
}
|
||||
|
||||
private NetworkTypeObserver(Context context) {
|
||||
mainHandler = new Handler(Looper.getMainLooper());
|
||||
backgroundExecutor = BackgroundExecutor.get();
|
||||
listeners = new CopyOnWriteArrayList<>();
|
||||
networkTypeLock = new Object();
|
||||
lock = new Object();
|
||||
networkType = C.NETWORK_TYPE_UNKNOWN;
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
context.registerReceiver(new Receiver(), filter);
|
||||
backgroundExecutor.execute(() -> init(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #register(Listener, Executor)} instead.
|
||||
*/
|
||||
@InlineMe(
|
||||
replacement = "this.register(listener, new Handler(Looper.getMainLooper())::post)",
|
||||
imports = {"android.os.Handler", "android.os.Looper"})
|
||||
@Deprecated
|
||||
public void register(Listener listener) {
|
||||
register(listener, /* executor= */ new Handler(Looper.getMainLooper())::post);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,44 +118,68 @@ public final class NetworkTypeObserver {
|
||||
* <p>The current network type will be reported to the listener after registration.
|
||||
*
|
||||
* @param listener The {@link Listener}.
|
||||
* @param executor The {@link Executor} to call the {@code listener} on.
|
||||
*/
|
||||
public void register(Listener listener) {
|
||||
public void register(Listener listener, Executor executor) {
|
||||
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()));
|
||||
boolean isInitialized;
|
||||
ListenerHolder listenerHolder = new ListenerHolder(listener, executor);
|
||||
synchronized (lock) {
|
||||
listeners.add(listenerHolder);
|
||||
isInitialized = this.isInitialized;
|
||||
}
|
||||
if (isInitialized) {
|
||||
// Simulate an initial update (like the sticky broadcast we'd receive if we were to register a
|
||||
// separate broadcast receiver for each listener).
|
||||
listenerHolder.callOnNetworkTypeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the current network type. */
|
||||
public @C.NetworkType int getNetworkType() {
|
||||
synchronized (networkTypeLock) {
|
||||
synchronized (lock) {
|
||||
return networkType;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnprotectedReceiver") // Protected system broadcasts must not specify protection.
|
||||
private void init(Context context) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
context.registerReceiver(new Receiver(), filter);
|
||||
}
|
||||
|
||||
private void removeClearedReferences() {
|
||||
for (WeakReference<Listener> listenerReference : listeners) {
|
||||
if (listenerReference.get() == null) {
|
||||
listeners.remove(listenerReference);
|
||||
for (ListenerHolder listener : listeners) {
|
||||
if (listener.canBeRemoved()) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleConnectivityActionBroadcast(Context context) {
|
||||
@C.NetworkType int networkType = getNetworkTypeFromConnectivityManager(context);
|
||||
if (Util.SDK_INT >= 31 && networkType == C.NETWORK_TYPE_4G) {
|
||||
// Delay update of the network type to check whether this is actually 5G-NSA.
|
||||
Api31.disambiguate4gAnd5gNsa(context, /* instance= */ NetworkTypeObserver.this);
|
||||
} else {
|
||||
updateNetworkType(networkType);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNetworkType(@C.NetworkType int networkType) {
|
||||
synchronized (networkTypeLock) {
|
||||
if (this.networkType == networkType) {
|
||||
removeClearedReferences();
|
||||
Iterator<ListenerHolder> currentListeners;
|
||||
synchronized (lock) {
|
||||
if (isInitialized && this.networkType == networkType) {
|
||||
return;
|
||||
}
|
||||
isInitialized = true;
|
||||
this.networkType = networkType;
|
||||
currentListeners = listeners.iterator();
|
||||
}
|
||||
for (WeakReference<Listener> listenerReference : listeners) {
|
||||
@Nullable Listener listener = listenerReference.get();
|
||||
if (listener != null) {
|
||||
listener.onNetworkTypeChanged(networkType);
|
||||
} else {
|
||||
listeners.remove(listenerReference);
|
||||
}
|
||||
while (currentListeners.hasNext()) {
|
||||
currentListeners.next().callOnNetworkTypeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,13 +253,7 @@ public final class NetworkTypeObserver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@C.NetworkType int networkType = getNetworkTypeFromConnectivityManager(context);
|
||||
if (Util.SDK_INT >= 31 && networkType == C.NETWORK_TYPE_4G) {
|
||||
// Delay update of the network type to check whether this is actually 5G-NSA.
|
||||
Api31.disambiguate4gAnd5gNsa(context, /* instance= */ NetworkTypeObserver.this);
|
||||
} else {
|
||||
updateNetworkType(networkType);
|
||||
}
|
||||
backgroundExecutor.execute(() -> handleConnectivityActionBroadcast(context));
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,7 +265,7 @@ public final class NetworkTypeObserver {
|
||||
TelephonyManager telephonyManager =
|
||||
checkNotNull((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
|
||||
DisplayInfoCallback callback = new DisplayInfoCallback(instance);
|
||||
telephonyManager.registerTelephonyCallback(context.getMainExecutor(), callback);
|
||||
telephonyManager.registerTelephonyCallback(instance.backgroundExecutor, callback);
|
||||
// We are only interested in the initial response with the current state, so unregister
|
||||
// the listener immediately.
|
||||
telephonyManager.unregisterTelephonyCallback(callback);
|
||||
@ -262,4 +295,30 @@ public final class NetworkTypeObserver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class ListenerHolder {
|
||||
|
||||
// This class needs to hold weak references as it doesn't require listeners to unregister.
|
||||
private final WeakReference<Listener> listener;
|
||||
private final Executor executor;
|
||||
|
||||
public ListenerHolder(Listener listener, Executor executor) {
|
||||
this.listener = new WeakReference<>(listener);
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public boolean canBeRemoved() {
|
||||
return listener.get() == null;
|
||||
}
|
||||
|
||||
public void callOnNetworkTypeChanged() {
|
||||
executor.execute(
|
||||
() -> {
|
||||
Listener listener = this.listener.get();
|
||||
if (listener != null) {
|
||||
listener.onNetworkTypeChanged(getNetworkType());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright 2025 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 androidx.media3.common.util;
|
||||
|
||||
import static android.net.NetworkInfo.State.CONNECTED;
|
||||
import static android.net.NetworkInfo.State.DISCONNECTED;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.telephony.TelephonyDisplayInfo;
|
||||
import android.telephony.TelephonyManager;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowLooper;
|
||||
import org.robolectric.shadows.ShadowNetworkInfo;
|
||||
import org.robolectric.shadows.ShadowTelephonyManager;
|
||||
|
||||
/** Unit test for {@link NetworkTypeObserver}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(sdk = Config.ALL_SDKS) // Test all SDKs because network detection logic changed over time.
|
||||
public class NetworkTypeObserverTest {
|
||||
|
||||
private HandlerThread backgroundThread;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
NetworkTypeObserver.resetForTests();
|
||||
backgroundThread = new HandlerThread("NetworkTypeObserverTest");
|
||||
backgroundThread.start();
|
||||
BackgroundExecutor.set(new Handler(backgroundThread.getLooper())::post);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
backgroundThread.quit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_immediatelyAfterObtainingStaticInstance_callsOnNetworkTypeChanged() {
|
||||
setActiveNetworkInfo(getWifiNetworkInfo());
|
||||
NetworkTypeObserver.Listener listener = mock(NetworkTypeObserver.Listener.class);
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
// Do not wait for pending operations here.
|
||||
|
||||
networkTypeObserver.register(listener, directExecutor());
|
||||
waitForPendingOperations();
|
||||
|
||||
verify(listener).onNetworkTypeChanged(C.NETWORK_TYPE_WIFI);
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_afterStaticInstanceIsInitialized_callsOnNetworkTypeChanged() {
|
||||
setActiveNetworkInfo(getWifiNetworkInfo());
|
||||
NetworkTypeObserver.Listener listener = mock(NetworkTypeObserver.Listener.class);
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
|
||||
waitForPendingOperations();
|
||||
networkTypeObserver.register(listener, directExecutor());
|
||||
waitForPendingOperations();
|
||||
|
||||
verify(listener).onNetworkTypeChanged(C.NETWORK_TYPE_WIFI);
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_withCustomExecutor_callsOnNetworkTypeChangedOnExecutor() {
|
||||
setActiveNetworkInfo(getWifiNetworkInfo());
|
||||
AtomicReference<Looper> actualListenerLooper = new AtomicReference<>();
|
||||
NetworkTypeObserver.Listener listener =
|
||||
networkType -> actualListenerLooper.set(Looper.myLooper());
|
||||
HandlerThread listenerThread = new HandlerThread("CustomListenerThread");
|
||||
listenerThread.start();
|
||||
Looper listenerThreadLooper = listenerThread.getLooper();
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
|
||||
networkTypeObserver.register(listener, new Handler(listenerThreadLooper)::post);
|
||||
waitForPendingOperations();
|
||||
shadowOf(listenerThreadLooper).idle();
|
||||
listenerThread.quit();
|
||||
|
||||
assertThat(actualListenerLooper.get()).isEqualTo(listenerThreadLooper);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_withChangeInNetworkType_callsOnNetworkTypeChangedAgain() {
|
||||
setActiveNetworkInfo(getWifiNetworkInfo());
|
||||
NetworkTypeObserver.Listener listener = mock(NetworkTypeObserver.Listener.class);
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
|
||||
networkTypeObserver.register(listener, directExecutor());
|
||||
waitForPendingOperations();
|
||||
setActiveNetworkInfo(get4gNetworkInfo());
|
||||
waitForPendingOperations();
|
||||
setActiveNetworkInfo(get4gNetworkInfo()); // Check same network type isn't reported twice.
|
||||
waitForPendingOperations();
|
||||
setActiveNetworkInfo(get3gNetworkInfo());
|
||||
waitForPendingOperations();
|
||||
|
||||
verify(listener).onNetworkTypeChanged(C.NETWORK_TYPE_WIFI);
|
||||
verify(listener).onNetworkTypeChanged(C.NETWORK_TYPE_4G);
|
||||
verify(listener).onNetworkTypeChanged(C.NETWORK_TYPE_3G);
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNetworkType_withWifiNetwork_returnsNetworkTypeWifi() {
|
||||
setActiveNetworkInfo(getWifiNetworkInfo());
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
waitForPendingOperations();
|
||||
|
||||
assertThat(networkTypeObserver.getNetworkType()).isEqualTo(C.NETWORK_TYPE_WIFI);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNetworkType_with2gNetwork_returnsNetworkType2g() {
|
||||
setActiveNetworkInfo(get2gNetworkInfo());
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
waitForPendingOperations();
|
||||
|
||||
assertThat(networkTypeObserver.getNetworkType()).isEqualTo(C.NETWORK_TYPE_2G);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNetworkType_with3gNetwork_returnsNetworkType3g() {
|
||||
setActiveNetworkInfo(get3gNetworkInfo());
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
waitForPendingOperations();
|
||||
|
||||
assertThat(networkTypeObserver.getNetworkType()).isEqualTo(C.NETWORK_TYPE_3G);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNetworkType_with4gNetwork_returnsNetworkType4g() {
|
||||
setActiveNetworkInfo(get4gNetworkInfo());
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
waitForPendingOperations();
|
||||
|
||||
assertThat(networkTypeObserver.getNetworkType()).isEqualTo(C.NETWORK_TYPE_4G);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = 31) // 5G-NSA detection is supported from API 31.
|
||||
public void getNetworkType_with5gNsaNetwork_returnsNetworkType5gNsa() {
|
||||
setActiveNetworkInfo(get4gNetworkInfo(), TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA);
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
waitForPendingOperations();
|
||||
|
||||
assertThat(networkTypeObserver.getNetworkType()).isEqualTo(C.NETWORK_TYPE_5G_NSA);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = 29) // 5G-SA detection is supported from API 29.
|
||||
public void getNetworkType_with5gSaNetwork_returnsNetworkType5gSa() {
|
||||
setActiveNetworkInfo(get5gSaNetworkInfo());
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
waitForPendingOperations();
|
||||
|
||||
assertThat(networkTypeObserver.getNetworkType()).isEqualTo(C.NETWORK_TYPE_5G_SA);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNetworkType_withEthernetNetwork_returnsNetworkTypeEthernet() {
|
||||
setActiveNetworkInfo(getEthernetNetworkInfo());
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
waitForPendingOperations();
|
||||
|
||||
assertThat(networkTypeObserver.getNetworkType()).isEqualTo(C.NETWORK_TYPE_ETHERNET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNetworkType_withOfflineNetwork_returnsNetworkTypeOffline() {
|
||||
setActiveNetworkInfo(getOfflineNetworkInfo());
|
||||
NetworkTypeObserver networkTypeObserver =
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
waitForPendingOperations();
|
||||
|
||||
assertThat(networkTypeObserver.getNetworkType()).isEqualTo(C.NETWORK_TYPE_OFFLINE);
|
||||
}
|
||||
|
||||
private void waitForPendingOperations() {
|
||||
ShadowLooper.idleMainLooper();
|
||||
shadowOf(backgroundThread.getLooper()).idle();
|
||||
}
|
||||
|
||||
private static void setActiveNetworkInfo(NetworkInfo networkInfo) {
|
||||
setActiveNetworkInfo(networkInfo, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
|
||||
}
|
||||
|
||||
// Adding the permission to the test AndroidManifest.xml doesn't work to appease lint.
|
||||
@SuppressWarnings({"StickyBroadcast", "MissingPermission"})
|
||||
private static void setActiveNetworkInfo(NetworkInfo networkInfo, int networkTypeOverride) {
|
||||
// Set network info in ConnectivityManager and TelephonyDisplayInfo in TelephonyManager.
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
ConnectivityManager connectivityManager =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
Shadows.shadowOf(connectivityManager).setActiveNetworkInfo(networkInfo);
|
||||
if (Util.SDK_INT >= 31) {
|
||||
TelephonyManager telephonyManager =
|
||||
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
Object displayInfo =
|
||||
ShadowTelephonyManager.createTelephonyDisplayInfo(
|
||||
networkInfo.getType(), networkTypeOverride);
|
||||
Shadows.shadowOf(telephonyManager).setTelephonyDisplayInfo(displayInfo);
|
||||
}
|
||||
// Create a sticky broadcast for the connectivity action because Robolectric isn't replying with
|
||||
// the current network state if a receiver for this intent is registered.
|
||||
context.sendStickyBroadcast(new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
}
|
||||
|
||||
private static NetworkInfo getWifiNetworkInfo() {
|
||||
return ShadowNetworkInfo.newInstance(
|
||||
NetworkInfo.DetailedState.CONNECTED,
|
||||
ConnectivityManager.TYPE_WIFI,
|
||||
/* subType= */ 0,
|
||||
/* isAvailable= */ true,
|
||||
CONNECTED);
|
||||
}
|
||||
|
||||
private static NetworkInfo get2gNetworkInfo() {
|
||||
return ShadowNetworkInfo.newInstance(
|
||||
NetworkInfo.DetailedState.CONNECTED,
|
||||
ConnectivityManager.TYPE_MOBILE,
|
||||
TelephonyManager.NETWORK_TYPE_GPRS,
|
||||
/* isAvailable= */ true,
|
||||
CONNECTED);
|
||||
}
|
||||
|
||||
private static NetworkInfo get3gNetworkInfo() {
|
||||
return ShadowNetworkInfo.newInstance(
|
||||
NetworkInfo.DetailedState.CONNECTED,
|
||||
ConnectivityManager.TYPE_MOBILE,
|
||||
TelephonyManager.NETWORK_TYPE_HSDPA,
|
||||
/* isAvailable= */ true,
|
||||
CONNECTED);
|
||||
}
|
||||
|
||||
private static NetworkInfo get4gNetworkInfo() {
|
||||
return ShadowNetworkInfo.newInstance(
|
||||
NetworkInfo.DetailedState.CONNECTED,
|
||||
ConnectivityManager.TYPE_MOBILE,
|
||||
TelephonyManager.NETWORK_TYPE_LTE,
|
||||
/* isAvailable= */ true,
|
||||
CONNECTED);
|
||||
}
|
||||
|
||||
private static NetworkInfo get5gSaNetworkInfo() {
|
||||
return ShadowNetworkInfo.newInstance(
|
||||
NetworkInfo.DetailedState.CONNECTED,
|
||||
ConnectivityManager.TYPE_MOBILE,
|
||||
TelephonyManager.NETWORK_TYPE_NR,
|
||||
/* isAvailable= */ true,
|
||||
CONNECTED);
|
||||
}
|
||||
|
||||
private static NetworkInfo getEthernetNetworkInfo() {
|
||||
return ShadowNetworkInfo.newInstance(
|
||||
NetworkInfo.DetailedState.CONNECTED,
|
||||
ConnectivityManager.TYPE_ETHERNET,
|
||||
/* subType= */ 0,
|
||||
/* isAvailable= */ true,
|
||||
CONNECTED);
|
||||
}
|
||||
|
||||
private static NetworkInfo getOfflineNetworkInfo() {
|
||||
return ShadowNetworkInfo.newInstance(
|
||||
NetworkInfo.DetailedState.DISCONNECTED,
|
||||
ConnectivityManager.TYPE_WIFI,
|
||||
/* subType= */ 0,
|
||||
/* isAvailable= */ false,
|
||||
DISCONNECTED);
|
||||
}
|
||||
}
|
@ -25,9 +25,12 @@ import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.NetworkInfo.DetailedState;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.telephony.TelephonyDisplayInfo;
|
||||
import android.telephony.TelephonyManager;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.BackgroundExecutor;
|
||||
import androidx.media3.common.util.NetworkTypeObserver;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.datasource.DataSource;
|
||||
@ -747,9 +750,10 @@ public final class DefaultBandwidthMeterTest {
|
||||
// the current network state if a receiver for this intent is registered.
|
||||
ApplicationProvider.getApplicationContext()
|
||||
.sendStickyBroadcast(new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
// Trigger initialization of static network type observer.
|
||||
// Trigger initialization of static network type observer using the main handler to ensure we
|
||||
// can wait for the initialization to be done.
|
||||
BackgroundExecutor.set(new Handler(Looper.getMainLooper())::post);
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
// Wait until all pending messages are handled and the network initialization is done.
|
||||
ShadowLooper.idleMainLooper();
|
||||
}
|
||||
|
||||
|
@ -25,9 +25,12 @@ import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.NetworkInfo.DetailedState;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.telephony.TelephonyDisplayInfo;
|
||||
import android.telephony.TelephonyManager;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.BackgroundExecutor;
|
||||
import androidx.media3.common.util.NetworkTypeObserver;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.datasource.DataSource;
|
||||
@ -757,9 +760,10 @@ public final class ExperimentalBandwidthMeterTest {
|
||||
// the current network state if a receiver for this intent is registered.
|
||||
ApplicationProvider.getApplicationContext()
|
||||
.sendStickyBroadcast(new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
// Trigger initialization of static network type observer.
|
||||
// Trigger initialization of static network type observer using the main handler to ensure we
|
||||
// can wait for the initialization to be done.
|
||||
BackgroundExecutor.set(new Handler(Looper.getMainLooper())::post);
|
||||
NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext());
|
||||
// Wait until all pending messages are handled and the network initialization is done.
|
||||
ShadowLooper.idleMainLooper();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user