android: initialize listener with isConnected=false (#4606)

This commit is contained in:
Eric Gribkoff 2018-06-30 09:24:47 -07:00 committed by GitHub
parent 8c52e138ee
commit 27a1c900a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 23 additions and 27 deletions

View File

@ -187,7 +187,15 @@ public final class AndroidChannelBuilder extends ForwardingChannelBuilder<Androi
if (context != null) { if (context != null) {
connectivityManager = connectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
try {
configureNetworkMonitoring(); configureNetworkMonitoring();
} catch (SecurityException e) {
Log.w(
LOG_TAG,
"Failed to configure network monitoring. Does app have ACCESS_NETWORK_STATE"
+ " permission?",
e);
}
} else { } else {
connectivityManager = null; connectivityManager = null;
} }
@ -195,29 +203,10 @@ public final class AndroidChannelBuilder extends ForwardingChannelBuilder<Androi
@GuardedBy("lock") @GuardedBy("lock")
private void configureNetworkMonitoring() { private void configureNetworkMonitoring() {
// Eagerly check current network state to verify app has required permissions
NetworkInfo currentNetwork;
try {
currentNetwork = connectivityManager.getActiveNetworkInfo();
} catch (SecurityException e) {
Log.w(
LOG_TAG,
"Failed to configure network monitoring. Does app have ACCESS_NETWORK_STATE"
+ " permission?",
e);
return;
}
// Android N added the registerDefaultNetworkCallback API to listen to changes in the device's // Android N added the registerDefaultNetworkCallback API to listen to changes in the device's
// default network. For earlier Android API levels, use the BroadcastReceiver API. // default network. For earlier Android API levels, use the BroadcastReceiver API.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && connectivityManager != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && connectivityManager != null) {
// The connection status may change before registration of the listener is complete, but final DefaultNetworkCallback defaultNetworkCallback = new DefaultNetworkCallback();
// this will at worst result in invoking resetConnectBackoff() instead of enterIdle() (or
// vice versa) on the first network change.
boolean isConnected = currentNetwork != null && currentNetwork.isConnected();
final DefaultNetworkCallback defaultNetworkCallback =
new DefaultNetworkCallback(isConnected);
connectivityManager.registerDefaultNetworkCallback(defaultNetworkCallback); connectivityManager.registerDefaultNetworkCallback(defaultNetworkCallback);
unregisterRunnable = unregisterRunnable =
new Runnable() { new Runnable() {
@ -313,12 +302,13 @@ public final class AndroidChannelBuilder extends ForwardingChannelBuilder<Androi
/** Respond to changes in the default network. Only used on API levels 24+. */ /** Respond to changes in the default network. Only used on API levels 24+. */
@TargetApi(Build.VERSION_CODES.N) @TargetApi(Build.VERSION_CODES.N)
private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback { private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
// Registering a listener may immediate invoke onAvailable/onLost: the API docs do not specify
// if the methods are always invoked once, then again on any change, or only on change. When
// onAvailable() is invoked immediately without an actual network change, it's preferable to
// (spuriously) resetConnectBackoff() rather than enterIdle(), as the former is a no-op if the
// channel has already moved to CONNECTING.
private boolean isConnected = false; private boolean isConnected = false;
private DefaultNetworkCallback(boolean isConnected) {
this.isConnected = isConnected;
}
@Override @Override
public void onAvailable(Network network) { public void onAvailable(Network network) {
if (isConnected) { if (isConnected) {

View File

@ -256,15 +256,21 @@ public final class AndroidChannelBuilderTest {
@Test @Test
@Config(sdk = 24) @Config(sdk = 24)
public void newChannelWithConnection_entersIdleOnConnectionChange_api24() { public void newChannelWithConnection_entersIdleOnSecondConnectionChange_api24() {
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED); shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED);
TestChannel delegateChannel = new TestChannel(); TestChannel delegateChannel = new TestChannel();
ManagedChannel androidChannel = ManagedChannel androidChannel =
new AndroidChannelBuilder.AndroidChannel( new AndroidChannelBuilder.AndroidChannel(
delegateChannel, RuntimeEnvironment.application.getApplicationContext()); delegateChannel, RuntimeEnvironment.application.getApplicationContext());
// The first onAvailable() may just signal that the device was connected when the callback is
// registered, rather than indicating a changed network, so we do not enter idle.
shadowConnectivityManager.setActiveNetworkInfo(WIFI_CONNECTED); shadowConnectivityManager.setActiveNetworkInfo(WIFI_CONNECTED);
assertThat(delegateChannel.resetCount).isEqualTo(0); assertThat(delegateChannel.resetCount).isEqualTo(1);
assertThat(delegateChannel.enterIdleCount).isEqualTo(0);
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED);
assertThat(delegateChannel.resetCount).isEqualTo(1);
assertThat(delegateChannel.enterIdleCount).isEqualTo(1); assertThat(delegateChannel.enterIdleCount).isEqualTo(1);
androidChannel.shutdown(); androidChannel.shutdown();