Skip to content

Commit 8eb2e7e

Browse files
authored
Fix offline transport handling. (#537)
When the device is offline its active NetworkInfo is null, this causes an NPE and fails to record events.
1 parent 3b5a1f4 commit 8eb2e7e

File tree

2 files changed

+79
-5
lines changed

2 files changed

+79
-5
lines changed

transport/transport-backend-cct/src/main/java/com/google/android/datatransport/cct/CctTransportBackend.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.android.datatransport.cct.proto.LogResponse;
2828
import com.google.android.datatransport.cct.proto.NetworkConnectionInfo;
2929
import com.google.android.datatransport.cct.proto.NetworkConnectionInfo.MobileSubtype;
30+
import com.google.android.datatransport.cct.proto.NetworkConnectionInfo.NetworkType;
3031
import com.google.android.datatransport.cct.proto.QosTierConfiguration;
3132
import com.google.android.datatransport.runtime.EventInternal;
3233
import com.google.android.datatransport.runtime.backends.BackendRequest;
@@ -65,6 +66,9 @@ final class CctTransportBackend implements TransportBackend {
6566
private static final String CONTENT_TYPE_HEADER_KEY = "Content-Type";
6667
private static final String PROTOBUF_CONTENT_TYPE = "application/x-protobuf";
6768

69+
@VisibleForTesting static final String KEY_NETWORK_TYPE = "net-type";
70+
@VisibleForTesting static final String KEY_MOBILE_SUBTYPE = "mobile-subtype";
71+
6872
private static final String KEY_SDK_VERSION = "sdk-version";
6973
private static final String KEY_MODEL = "model";
7074
private static final String KEY_HARDWARE = "hardware";
@@ -73,8 +77,6 @@ final class CctTransportBackend implements TransportBackend {
7377
private static final String KEY_OS_BUILD = "os-uild";
7478
private static final String KEY_MANUFACTURER = "manufacturer";
7579
private static final String KEY_FINGERPRINT = "fingerprint";
76-
private static final String KEY_NETWORK_TYPE = "net-type";
77-
private static final String KEY_MOBILE_SUBTYPE = "mobile-subtype";
7880
private static final String KEY_TIMEZONE_OFFSET = "tz-offset";
7981

8082
private final ConnectivityManager connectivityManager;
@@ -125,12 +127,25 @@ public EventInternal decorate(EventInternal eventInternal) {
125127
.addMetadata(KEY_MANUFACTURER, Build.MANUFACTURER)
126128
.addMetadata(KEY_FINGERPRINT, Build.FINGERPRINT)
127129
.addMetadata(KEY_TIMEZONE_OFFSET, getTzOffset())
128-
.addMetadata(KEY_NETWORK_TYPE, networkInfo.getType())
129-
.addMetadata(KEY_MOBILE_SUBTYPE, toSubtypeValue(networkInfo.getSubtype()))
130+
.addMetadata(KEY_NETWORK_TYPE, getNetTypeValue(networkInfo))
131+
.addMetadata(KEY_MOBILE_SUBTYPE, getNetSubtypeValue(networkInfo))
130132
.build();
131133
}
132134

133-
private int toSubtypeValue(int subtype) {
135+
private static int getNetTypeValue(NetworkInfo networkInfo) {
136+
// when the device is not connected networkInfo returned by ConnectivityManger is null.
137+
if (networkInfo == null) {
138+
return NetworkType.NONE_VALUE;
139+
}
140+
return networkInfo.getType();
141+
}
142+
143+
private static int getNetSubtypeValue(NetworkInfo networkInfo) {
144+
// when the device is not connected networkInfo returned by ConnectivityManger is null.
145+
if (networkInfo == null) {
146+
return MobileSubtype.UNKNOWN_MOBILE_SUBTYPE_VALUE;
147+
}
148+
int subtype = networkInfo.getSubtype();
134149
if (subtype == -1) {
135150
return MobileSubtype.COMBINED_VALUE;
136151
}

transport/transport-backend-cct/src/test/java/com/google/android/datatransport/cct/CctTransportBackendTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
2424
import static com.google.android.datatransport.cct.CctTransportBackend.getTzOffset;
2525
import static com.google.android.datatransport.cct.ProtoMatchers.protoMatcher;
26+
import static com.google.common.truth.Truth.assertThat;
2627
import static org.junit.Assert.assertEquals;
2728

2829
import android.content.Context;
@@ -47,6 +48,9 @@
4748
import org.junit.runner.RunWith;
4849
import org.robolectric.RobolectricTestRunner;
4950
import org.robolectric.RuntimeEnvironment;
51+
import org.robolectric.annotation.Config;
52+
import org.robolectric.annotation.Implementation;
53+
import org.robolectric.annotation.Implements;
5054

5155
@RunWith(RobolectricTestRunner.class)
5256
public class CctTransportBackendTest {
@@ -205,4 +209,59 @@ public void send_whenBackendResponseTimesOut_shouldReturnTransientError() {
205209

206210
assertEquals(response, BackendResponse.transientError());
207211
}
212+
213+
@Test
214+
public void decorate_whenOnline_shouldProperlyPopulateNetworkInfo() {
215+
CctTransportBackend backend =
216+
new CctTransportBackend(
217+
RuntimeEnvironment.application, TEST_ENDPOINT, wallClock, uptimeClock, 300);
218+
219+
EventInternal result =
220+
backend.decorate(
221+
EventInternal.builder()
222+
.setEventMillis(INITIAL_WALL_TIME)
223+
.setUptimeMillis(INITIAL_UPTIME)
224+
.setTransportName("3")
225+
.setPayload(PAYLOAD.toByteArray())
226+
.build());
227+
228+
assertThat(result.get(CctTransportBackend.KEY_NETWORK_TYPE))
229+
.isEqualTo(String.valueOf(NetworkConnectionInfo.NetworkType.MOBILE_VALUE));
230+
assertThat(result.get(CctTransportBackend.KEY_MOBILE_SUBTYPE))
231+
.isEqualTo(String.valueOf(NetworkConnectionInfo.MobileSubtype.EDGE_VALUE));
232+
}
233+
234+
@Test
235+
@Config(shadows = {OfflineConnectivityManagerShadow.class})
236+
public void decorate_whenOffline_shouldProperlyPopulateNetworkInfo() {
237+
CctTransportBackend backend =
238+
new CctTransportBackend(
239+
RuntimeEnvironment.application, TEST_ENDPOINT, wallClock, uptimeClock, 300);
240+
241+
EventInternal result =
242+
backend.decorate(
243+
EventInternal.builder()
244+
.setEventMillis(INITIAL_WALL_TIME)
245+
.setUptimeMillis(INITIAL_UPTIME)
246+
.setTransportName("3")
247+
.setPayload(PAYLOAD.toByteArray())
248+
.build());
249+
250+
assertThat(result.get(CctTransportBackend.KEY_NETWORK_TYPE))
251+
.isEqualTo(String.valueOf(NetworkConnectionInfo.NetworkType.NONE_VALUE));
252+
assertThat(result.get(CctTransportBackend.KEY_MOBILE_SUBTYPE))
253+
.isEqualTo(
254+
String.valueOf(NetworkConnectionInfo.MobileSubtype.UNKNOWN_MOBILE_SUBTYPE_VALUE));
255+
}
256+
257+
// When there is no active network, the ConnectivityManager returns null when
258+
// getActiveNetworkInfo() is called.
259+
@Implements(ConnectivityManager.class)
260+
public static class OfflineConnectivityManagerShadow {
261+
262+
@Implementation
263+
public NetworkInfo getActiveNetworkInfo() {
264+
return null;
265+
}
266+
}
208267
}

0 commit comments

Comments
 (0)