• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.libraries.mobiledatadownload.monitor;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
20 import static java.util.concurrent.TimeUnit.MILLISECONDS;
21 import static java.util.concurrent.TimeUnit.SECONDS;
22 
23 import android.content.Context;
24 import android.net.ConnectivityManager;
25 import android.net.NetworkInfo.DetailedState;
26 import android.net.Uri;
27 import android.os.Build;
28 import androidx.test.core.app.ApplicationProvider;
29 import com.google.mobiledatadownload.internal.MetadataProto.FileGroupLoggingState;
30 import com.google.mobiledatadownload.internal.MetadataProto.GroupKey;
31 import com.google.android.libraries.mobiledatadownload.file.common.testing.TemporaryUri;
32 import com.google.android.libraries.mobiledatadownload.file.spi.Monitor;
33 import com.google.android.libraries.mobiledatadownload.internal.logging.LoggingStateStore;
34 import com.google.android.libraries.mobiledatadownload.testing.FakeTimeSource;
35 import com.google.android.libraries.mobiledatadownload.testing.MddTestDependencies;
36 import com.google.common.base.Optional;
37 import java.util.List;
38 import java.util.Random;
39 import java.util.concurrent.Executor;
40 import org.junit.Before;
41 import org.junit.Rule;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 import org.robolectric.RobolectricTestRunner;
45 import org.robolectric.Shadows;
46 import org.robolectric.shadows.ShadowNetworkInfo;
47 
48 @RunWith(RobolectricTestRunner.class)
49 public class NetworkUsageMonitorTest {
50 
51   private static final Executor executor = directExecutor();
52   private static final String GROUP_NAME_1 = "group-name-1";
53   private static final String OWNER_PACKAGE_1 = "owner-package-1";
54   private static final String VARIANT_ID_1 = "variant-id-1";
55   private static final int VERSION_NUMBER_1 = 1;
56   private static final int BUILD_ID_1 = 123;
57 
58   private static final String GROUP_NAME_2 = "group-name-2";
59   private static final String OWNER_PACKAGE_2 = "owner-package-2";
60   private static final String VARIANT_ID_2 = "variant-id-2";
61 
62   private static final int VERSION_NUMBER_2 = 2;
63   private static final int BUILD_ID_2 = 456;
64 
65   private static final String FILE_URI_1 =
66       "android://com.google.android.gms/files/datadownload/shared/public/file_1";
67 
68   // Note: We can't make those android uris static variable since the Uri.parse will fail
69   // with initialization.
70   private final Uri uri1 = Uri.parse(FILE_URI_1);
71 
72   private static final String FILE_URI_2 =
73       "android://com.google.android.gms/files/datadownload/shared/public/file_2";
74   private final Uri uri2 = Uri.parse(FILE_URI_2);
75 
76   private static final String FILE_URI_3 =
77       "android://com.google.android.gms/files/datadownload/shared/public/file_3";
78   private final Uri uri3 = Uri.parse(FILE_URI_3);
79 
80   private NetworkUsageMonitor networkUsageMonitor;
81   private LoggingStateStore loggingStateStore;
82   private Context context;
83   private final FakeTimeSource clock = new FakeTimeSource();
84 
85   ConnectivityManager connectivityManager;
86 
87   @Rule public final TemporaryUri tmpUri = new TemporaryUri();
88 
89   @Before
setUp()90   public void setUp() throws Exception {
91     context = ApplicationProvider.getApplicationContext();
92 
93     loggingStateStore =
94         MddTestDependencies.LoggingStateStoreImpl.SHARED_PREFERENCES.loggingStateStore(
95             context, Optional.absent(), new FakeTimeSource(), executor, new Random());
96 
97     // TODO(b/177015303): use builder when available
98     networkUsageMonitor = new NetworkUsageMonitor(context, clock);
99 
100     this.connectivityManager =
101         (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
102   }
103 
setNetworkConnectivityType(int networkConnectivityType)104   private void setNetworkConnectivityType(int networkConnectivityType) {
105     Shadows.shadowOf(connectivityManager)
106         .setActiveNetworkInfo(
107             ShadowNetworkInfo.newInstance(
108                 DetailedState.CONNECTED,
109                 networkConnectivityType,
110                 0 /* subtype */,
111                 true /* isAvailable */,
112                 true /* isConnected */));
113   }
114 
115   @Test
testBytesWritten()116   public void testBytesWritten() throws Exception {
117     // Setup 2 FileGroups:
118     // FileGroup1: file1 and file2.
119     // FileGroup2: file3.
120 
121     GroupKey groupKey1 =
122         GroupKey.newBuilder()
123             .setOwnerPackage(OWNER_PACKAGE_1)
124             .setGroupName(GROUP_NAME_1)
125             .setVariantId(VARIANT_ID_1)
126             .build();
127     networkUsageMonitor.monitorUri(
128         uri1, groupKey1, BUILD_ID_1, VARIANT_ID_1, VERSION_NUMBER_1, loggingStateStore);
129     networkUsageMonitor.monitorUri(
130         uri2, groupKey1, BUILD_ID_1, VARIANT_ID_1, VERSION_NUMBER_1, loggingStateStore);
131 
132     GroupKey groupKey2 =
133         GroupKey.newBuilder()
134             .setOwnerPackage(OWNER_PACKAGE_2)
135             .setGroupName(GROUP_NAME_2)
136             .setVariantId(VARIANT_ID_2)
137             .build();
138 
139     networkUsageMonitor.monitorUri(
140         uri3, groupKey2, BUILD_ID_2, VARIANT_ID_2, VERSION_NUMBER_2, loggingStateStore);
141 
142     Monitor.OutputMonitor outputMonitor1 = networkUsageMonitor.monitorWrite(uri1);
143     Monitor.OutputMonitor outputMonitor2 = networkUsageMonitor.monitorWrite(uri2);
144     Monitor.OutputMonitor outputMonitor3 = networkUsageMonitor.monitorWrite(uri3);
145 
146     // outputMonitor1 is same as outputMonitor2 since they both monitor for FileGroup1.
147     assertThat(outputMonitor1).isSameInstanceAs(outputMonitor2);
148     assertThat(outputMonitor1).isNotSameInstanceAs(outputMonitor3);
149 
150     // First we have WIFI connection.
151     // Downloaded 1 bytes on WIFI for uri1
152     setNetworkConnectivityType(ConnectivityManager.TYPE_WIFI);
153     outputMonitor1.bytesWritten(new byte[1], 0, 1);
154 
155     // Downloaded 2 bytes on WIFI for uri1
156     outputMonitor1.bytesWritten(new byte[2], 0, 2);
157 
158     // Downloaded 4 bytes on WIFI for uri2
159     outputMonitor2.bytesWritten(new byte[4], 0, 4);
160 
161     // Downloaded 8 bytes on WIFI for uri3
162     outputMonitor3.bytesWritten(new byte[8], 0, 8);
163 
164     // Then we have CELLULAR connection.
165     // Downloaded 16 bytes on CELLULAR for uri1
166     setNetworkConnectivityType(ConnectivityManager.TYPE_MOBILE);
167     outputMonitor1.bytesWritten(new byte[16], 0, 16);
168 
169     // Downloaded 32 bytes on CELLULAR for uri2
170     outputMonitor2.bytesWritten(new byte[32], 0, 32);
171 
172     // Downloaded 64 bytes on CELLULAR for uri3
173     outputMonitor3.bytesWritten(new byte[64], 0, 64);
174 
175     // close() will trigger saving counters to LoggingStateStore.
176     outputMonitor1.close();
177     outputMonitor2.close();
178     outputMonitor3.close();
179 
180     // await executors idle here if we switch from directExecutor...
181 
182     List<FileGroupLoggingState> allLoggingState = loggingStateStore.getAndResetAllDataUsage().get();
183 
184     assertThat(allLoggingState)
185         .containsExactly(
186             FileGroupLoggingState.newBuilder()
187                 .setGroupKey(groupKey1)
188                 .setBuildId(BUILD_ID_1)
189                 .setVariantId(VARIANT_ID_1)
190                 .setFileGroupVersionNumber(VERSION_NUMBER_1)
191                 .setCellularUsage(16 + 32)
192                 .setWifiUsage(1 + 2 + 4)
193                 .build(),
194             FileGroupLoggingState.newBuilder()
195                 .setGroupKey(groupKey2)
196                 .setBuildId(BUILD_ID_2)
197                 .setVariantId(VARIANT_ID_2)
198                 .setFileGroupVersionNumber(VERSION_NUMBER_2)
199                 .setCellularUsage(64)
200                 .setWifiUsage(8)
201                 .build());
202   }
203 
204   @Test
testBytesWritten_multipleVersions()205   public void testBytesWritten_multipleVersions() throws Exception {
206     // Setup 2 versions of a FileGroup:
207     // FileGroup v1: file1 and file2.
208     // FileGroup v2: file2 and file3.
209     GroupKey groupKey1 =
210         GroupKey.newBuilder()
211             .setOwnerPackage(OWNER_PACKAGE_1)
212             .setGroupName(GROUP_NAME_1)
213             .setVariantId(VARIANT_ID_1)
214             .build();
215     networkUsageMonitor.monitorUri(
216         uri1, groupKey1, BUILD_ID_1, VARIANT_ID_1, VERSION_NUMBER_1, loggingStateStore);
217     networkUsageMonitor.monitorUri(
218         uri2, groupKey1, BUILD_ID_1, VARIANT_ID_1, VERSION_NUMBER_1, loggingStateStore);
219 
220     GroupKey groupKey2 =
221         GroupKey.newBuilder()
222             .setOwnerPackage(OWNER_PACKAGE_2)
223             .setGroupName(GROUP_NAME_2)
224             .setVariantId(VARIANT_ID_2)
225             .build();
226 
227     // This would update uri2 to belong to FileGroup v2.
228     networkUsageMonitor.monitorUri(
229         uri2, groupKey2, BUILD_ID_2, VARIANT_ID_2, VERSION_NUMBER_2, loggingStateStore);
230     networkUsageMonitor.monitorUri(
231         uri3, groupKey2, BUILD_ID_2, VARIANT_ID_2, VERSION_NUMBER_2, loggingStateStore);
232 
233     Monitor.OutputMonitor outputMonitor1 = networkUsageMonitor.monitorWrite(uri1);
234     Monitor.OutputMonitor outputMonitor2 = networkUsageMonitor.monitorWrite(uri2);
235     Monitor.OutputMonitor outputMonitor3 = networkUsageMonitor.monitorWrite(uri3);
236 
237     // outputMonitor2 is same as outputMonitor3 since they both monitor for the same version of the
238     // same FileGroup.
239     assertThat(outputMonitor1).isNotSameInstanceAs(outputMonitor2);
240     assertThat(outputMonitor2).isSameInstanceAs(outputMonitor3);
241 
242     // First we have WIFI connection.
243     // Downloaded 1 bytes on WIFI for uri1
244     setNetworkConnectivityType(ConnectivityManager.TYPE_WIFI);
245     outputMonitor1.bytesWritten(new byte[1], 0, 1);
246 
247     // Downloaded 2 bytes on WIFI for uri1
248     outputMonitor1.bytesWritten(new byte[2], 0, 2);
249 
250     // Downloaded 4 bytes on WIFI for uri2
251     outputMonitor2.bytesWritten(new byte[4], 0, 4);
252 
253     // Downloaded 8 bytes on WIFI for uri3
254     outputMonitor3.bytesWritten(new byte[8], 0, 8);
255 
256     // Then we have CELLULAR connection.
257     // Downloaded 16 bytes on CELLULAR for uri1
258     setNetworkConnectivityType(ConnectivityManager.TYPE_MOBILE);
259     outputMonitor1.bytesWritten(new byte[16], 0, 16);
260 
261     // Downloaded 32 bytes on CELLULAR for uri2
262     outputMonitor2.bytesWritten(new byte[32], 0, 32);
263 
264     // Downloaded 64 bytes on CELLULAR for uri3
265     outputMonitor3.bytesWritten(new byte[64], 0, 64);
266 
267     // close() will trigger saving counters to SharedPreference.
268     outputMonitor1.close();
269     outputMonitor2.close();
270     outputMonitor3.close();
271 
272     List<FileGroupLoggingState> allLoggingState = loggingStateStore.getAndResetAllDataUsage().get();
273 
274     assertThat(allLoggingState)
275         .containsExactly(
276             FileGroupLoggingState.newBuilder()
277                 .setGroupKey(groupKey1)
278                 .setBuildId(BUILD_ID_1)
279                 .setVariantId(VARIANT_ID_1)
280                 .setFileGroupVersionNumber(VERSION_NUMBER_1)
281                 .setCellularUsage(16)
282                 .setWifiUsage(1 + 2)
283                 .build(),
284             FileGroupLoggingState.newBuilder()
285                 .setGroupKey(groupKey2)
286                 .setBuildId(BUILD_ID_2)
287                 .setVariantId(VARIANT_ID_2)
288                 .setFileGroupVersionNumber(VERSION_NUMBER_2)
289                 .setCellularUsage(32 + 64)
290                 .setWifiUsage(4 + 8)
291                 .build());
292   }
293 
294   @Test
testBytesWritten_flush_interval()295   public void testBytesWritten_flush_interval() throws Exception {
296     // Setup 1 FileGroups:
297     // FileGroup1: file1
298 
299     GroupKey groupKey1 =
300         GroupKey.newBuilder()
301             .setOwnerPackage(OWNER_PACKAGE_1)
302             .setGroupName(GROUP_NAME_1)
303             .setVariantId(VARIANT_ID_1)
304             .build();
305     networkUsageMonitor.monitorUri(
306         uri1, groupKey1, BUILD_ID_1, VARIANT_ID_1, VERSION_NUMBER_1, loggingStateStore);
307 
308     Monitor.OutputMonitor outputMonitor1 = networkUsageMonitor.monitorWrite(uri1);
309 
310     // Advance time so counters are flushed
311     clock.advance(NetworkUsageMonitor.LOG_FREQUENCY_SECONDS + 1, SECONDS);
312 
313     // Downloaded 1 bytes on WIFI for uri1
314     setNetworkConnectivityType(ConnectivityManager.TYPE_WIFI);
315     outputMonitor1.bytesWritten(new byte[1], 0, 1);
316     assertThat(loggingStateStore.getAndResetAllDataUsage().get())
317         .containsExactly(
318             FileGroupLoggingState.newBuilder()
319                 .setGroupKey(groupKey1)
320                 .setBuildId(BUILD_ID_1)
321                 .setVariantId(VARIANT_ID_1)
322                 .setFileGroupVersionNumber(VERSION_NUMBER_1)
323                 .setCellularUsage(0)
324                 .setWifiUsage(1)
325                 .build());
326 
327     // Advance the clock by < LOG_FREQUENCY_SECONDS
328     clock.advance(1, MILLISECONDS);
329     outputMonitor1.bytesWritten(new byte[2], 0, 2);
330 
331     clock.advance(1, MILLISECONDS);
332     outputMonitor1.bytesWritten(new byte[16], 0, 4);
333 
334     // Only the 1st and 2nd chunks were saved.
335     assertThat(loggingStateStore.getAndResetAllDataUsage().get()).isEmpty();
336 
337     // Advance the clock by > LOG_FREQUENCY_SECONDS
338     clock.advance(NetworkUsageMonitor.LOG_FREQUENCY_SECONDS + 1, SECONDS);
339     outputMonitor1.bytesWritten(new byte[16], 0, 8);
340 
341     // All chunks were saved.
342     assertThat(loggingStateStore.getAndResetAllDataUsage().get())
343         .containsExactly(
344             FileGroupLoggingState.newBuilder()
345                 .setGroupKey(groupKey1)
346                 .setBuildId(BUILD_ID_1)
347                 .setVariantId(VARIANT_ID_1)
348                 .setFileGroupVersionNumber(VERSION_NUMBER_1)
349                 .setCellularUsage(0)
350                 .setWifiUsage(2 + 4 + 8)
351                 .build());
352   }
353 
354   @Test
testBytesWritten_mix_write_append()355   public void testBytesWritten_mix_write_append() throws Exception {
356     // Setup 2 FileGroups:
357     // FileGroup1: file1 and file2.
358     // FileGroup2: file3.
359 
360     GroupKey groupKey1 =
361         GroupKey.newBuilder()
362             .setOwnerPackage(OWNER_PACKAGE_1)
363             .setGroupName(GROUP_NAME_1)
364             .setVariantId(VARIANT_ID_1)
365             .build();
366     networkUsageMonitor.monitorUri(
367         uri1, groupKey1, BUILD_ID_1, VARIANT_ID_1, VERSION_NUMBER_1, loggingStateStore);
368     networkUsageMonitor.monitorUri(
369         uri2, groupKey1, BUILD_ID_1, VARIANT_ID_1, VERSION_NUMBER_1, loggingStateStore);
370 
371     GroupKey groupKey2 =
372         GroupKey.newBuilder()
373             .setOwnerPackage(OWNER_PACKAGE_2)
374             .setGroupName(GROUP_NAME_2)
375             .setVariantId(VARIANT_ID_2)
376             .build();
377 
378     networkUsageMonitor.monitorUri(
379         uri3, groupKey2, BUILD_ID_2, VARIANT_ID_2, VERSION_NUMBER_2, loggingStateStore);
380 
381     Monitor.OutputMonitor outputMonitor1 = networkUsageMonitor.monitorWrite(uri1);
382     Monitor.OutputMonitor outputMonitor2 = networkUsageMonitor.monitorAppend(uri2);
383     Monitor.OutputMonitor outputMonitor3 = networkUsageMonitor.monitorAppend(uri3);
384 
385     // outputMonitor1 is same as outputMonitor2 since they both monitor for FileGroup1.
386     assertThat(outputMonitor1).isSameInstanceAs(outputMonitor2);
387     assertThat(outputMonitor1).isNotSameInstanceAs(outputMonitor3);
388 
389     // First we have WIFI connection.
390     // Downloaded 1 bytes on WIFI for uri1
391     setNetworkConnectivityType(ConnectivityManager.TYPE_WIFI);
392     outputMonitor1.bytesWritten(new byte[1], 0, 1);
393 
394     // Downloaded 2 bytes on WIFI for uri1
395     outputMonitor1.bytesWritten(new byte[2], 0, 2);
396 
397     // Downloaded 4 bytes on WIFI for uri2
398     outputMonitor2.bytesWritten(new byte[4], 0, 4);
399 
400     // Downloaded 8 bytes on WIFI for uri3
401     outputMonitor3.bytesWritten(new byte[8], 0, 8);
402 
403     // Then we have CELLULAR connection.
404     // Downloaded 16 bytes on CELLULAR for uri1
405     setNetworkConnectivityType(ConnectivityManager.TYPE_MOBILE);
406     outputMonitor1.bytesWritten(new byte[16], 0, 16);
407 
408     // Downloaded 32 bytes on CELLULAR for uri2
409     outputMonitor2.bytesWritten(new byte[32], 0, 32);
410 
411     // Downloaded 64 bytes on CELLULAR for uri3
412     outputMonitor3.bytesWritten(new byte[64], 0, 64);
413 
414     // close() will trigger saving counters to SharedPreference.
415     outputMonitor1.close();
416     outputMonitor2.close();
417     outputMonitor3.close();
418 
419     // await executors idle here if we switch from directExecutor...
420 
421     List<FileGroupLoggingState> allLoggingState = loggingStateStore.getAndResetAllDataUsage().get();
422 
423     assertThat(allLoggingState)
424         .containsExactly(
425             FileGroupLoggingState.newBuilder()
426                 .setGroupKey(groupKey1)
427                 .setBuildId(BUILD_ID_1)
428                 .setVariantId(VARIANT_ID_1)
429                 .setFileGroupVersionNumber(VERSION_NUMBER_1)
430                 .setCellularUsage(16 + 32)
431                 .setWifiUsage(1 + 2 + 4)
432                 .build(),
433             FileGroupLoggingState.newBuilder()
434                 .setGroupKey(groupKey2)
435                 .setBuildId(BUILD_ID_2)
436                 .setVariantId(VARIANT_ID_2)
437                 .setFileGroupVersionNumber(VERSION_NUMBER_2)
438                 .setCellularUsage(64)
439                 .setWifiUsage(8)
440                 .build());
441   }
442 
443   @Test
getNetworkConnectivityType()444   public void getNetworkConnectivityType() {
445     setNetworkConnectivityType(ConnectivityManager.TYPE_WIFI);
446     assertThat(NetworkUsageMonitor.isCellular(context)).isFalse();
447 
448     setNetworkConnectivityType(ConnectivityManager.TYPE_ETHERNET);
449     assertThat(NetworkUsageMonitor.isCellular(context)).isFalse();
450 
451     setNetworkConnectivityType(ConnectivityManager.TYPE_VPN);
452     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
453       assertThat(NetworkUsageMonitor.isCellular(context)).isFalse();
454 
455     } else {
456       assertThat(NetworkUsageMonitor.isCellular(context)).isTrue();
457     }
458 
459     setNetworkConnectivityType(ConnectivityManager.TYPE_MOBILE);
460     assertThat(NetworkUsageMonitor.isCellular(context)).isTrue();
461 
462     // Fail to get NetworkInfo(return null) will return TYPE_WIFI.
463     Shadows.shadowOf(connectivityManager).setActiveNetworkInfo(null);
464     assertThat(NetworkUsageMonitor.isCellular(context)).isFalse();
465   }
466 
467   @Test
testNotRegisterUri()468   public void testNotRegisterUri() {
469     // Creating the outputMonitor before registering the uri through monitorUri will return
470     // null.
471     Monitor.OutputMonitor outputMonitor = networkUsageMonitor.monitorWrite(uri1);
472     assertThat(outputMonitor).isNull();
473 
474     outputMonitor = networkUsageMonitor.monitorAppend(uri1);
475     assertThat(outputMonitor).isNull();
476   }
477 }
478