NetworkStatsObserversTest.java revision 6965c1869aa8499706522d057b5143bbc240178b
1/*
2 * Copyright (C) 2016 The Android Open Source Project
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
17package com.android.server.net;
18
19import static android.net.ConnectivityManager.TYPE_MOBILE;
20import static android.net.ConnectivityManager.TYPE_WIFI;
21import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
22import static org.mockito.Matchers.any;
23import static org.mockito.Matchers.anyInt;
24import static org.mockito.Matchers.isA;
25import static org.mockito.Mockito.when;
26
27import static android.net.NetworkStats.SET_DEFAULT;
28import static android.net.NetworkStats.ROAMING_NO;
29import static android.net.NetworkStats.TAG_NONE;
30import static android.net.NetworkTemplate.buildTemplateMobileAll;
31import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
32import static android.net.TrafficStats.MB_IN_BYTES;
33import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
34
35import android.app.usage.NetworkStatsManager;
36import android.net.DataUsageRequest;
37import android.net.NetworkIdentity;
38import android.net.NetworkStats;
39import android.net.NetworkTemplate;
40import android.os.Handler;
41import android.os.HandlerThread;
42import android.os.IBinder;
43import android.os.Process;
44
45import android.os.ConditionVariable;
46import android.os.Looper;
47import android.os.Messenger;
48import android.os.Message;
49import android.os.UserHandle;
50import android.telephony.TelephonyManager;
51import android.util.ArrayMap;
52
53import com.android.internal.net.VpnInfo;
54import com.android.server.net.NetworkStatsService;
55import com.android.server.net.NetworkStatsServiceTest.IdleableHandlerThread;
56import com.android.server.net.NetworkStatsServiceTest.LatchedHandler;
57
58import java.util.ArrayList;
59import java.util.Objects;
60import java.util.List;
61
62import junit.framework.TestCase;
63
64import org.mockito.Mock;
65import org.mockito.Mockito;
66import org.mockito.MockitoAnnotations;
67
68/**
69 * Tests for {@link NetworkStatsObservers}.
70 */
71public class NetworkStatsObserversTest extends TestCase {
72    private static final String TEST_IFACE = "test0";
73    private static final String TEST_IFACE2 = "test1";
74    private static final long TEST_START = 1194220800000L;
75
76    private static final String IMSI_1 = "310004";
77    private static final String IMSI_2 = "310260";
78    private static final String TEST_SSID = "AndroidAP";
79
80    private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard();
81    private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
82    private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
83
84    private static final int UID_RED = UserHandle.PER_USER_RANGE + 1;
85    private static final int UID_BLUE = UserHandle.PER_USER_RANGE + 2;
86    private static final int UID_GREEN = UserHandle.PER_USER_RANGE + 3;
87    private static final int UID_ANOTHER_USER = 2 * UserHandle.PER_USER_RANGE + 4;
88
89    private static final long WAIT_TIMEOUT = 500;  // 1/2 sec
90    private static final long THRESHOLD_BYTES = 2 * MB_IN_BYTES;
91    private static final long BASE_BYTES = 7 * MB_IN_BYTES;
92    private static final int INVALID_TYPE = -1;
93
94    private static final VpnInfo[] VPN_INFO = new VpnInfo[0];
95
96    private long mElapsedRealtime;
97
98    private IdleableHandlerThread mObserverHandlerThread;
99    private Handler mObserverNoopHandler;
100
101    private LatchedHandler mHandler;
102    private ConditionVariable mCv;
103
104    private NetworkStatsObservers mStatsObservers;
105    private Messenger mMessenger;
106    private ArrayMap<String, NetworkIdentitySet> mActiveIfaces;
107    private ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces;
108
109    @Mock private IBinder mockBinder;
110
111    @Override
112    public void setUp() throws Exception {
113        super.setUp();
114        MockitoAnnotations.initMocks(this);
115
116        mObserverHandlerThread = new IdleableHandlerThread("HandlerThread");
117        mObserverHandlerThread.start();
118        final Looper observerLooper = mObserverHandlerThread.getLooper();
119        mStatsObservers = new NetworkStatsObservers() {
120            @Override
121            protected Looper getHandlerLooperLocked() {
122                return observerLooper;
123            }
124        };
125
126        mCv = new ConditionVariable();
127        mHandler = new LatchedHandler(Looper.getMainLooper(), mCv);
128        mMessenger = new Messenger(mHandler);
129
130        mActiveIfaces = new ArrayMap<>();
131        mActiveUidIfaces = new ArrayMap<>();
132    }
133
134    public void testRegister_thresholdTooLow_setsDefaultThreshold() throws Exception {
135        long thresholdTooLowBytes = 1L;
136        DataUsageRequest inputRequest = new DataUsageRequest(
137                DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, thresholdTooLowBytes);
138
139        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
140                Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
141        assertTrue(request.requestId > 0);
142        assertTrue(Objects.equals(sTemplateWifi, request.template));
143        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
144    }
145
146    public void testRegister_highThreshold_accepted() throws Exception {
147        long highThresholdBytes = 2 * THRESHOLD_BYTES;
148        DataUsageRequest inputRequest = new DataUsageRequest(
149                DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, highThresholdBytes);
150
151        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
152                Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
153        assertTrue(request.requestId > 0);
154        assertTrue(Objects.equals(sTemplateWifi, request.template));
155        assertEquals(highThresholdBytes, request.thresholdInBytes);
156    }
157
158    public void testRegister_twoRequests_twoIds() throws Exception {
159        DataUsageRequest inputRequest = new DataUsageRequest(
160                DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, THRESHOLD_BYTES);
161
162        DataUsageRequest request1 = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
163                Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
164        assertTrue(request1.requestId > 0);
165        assertTrue(Objects.equals(sTemplateWifi, request1.template));
166        assertEquals(THRESHOLD_BYTES, request1.thresholdInBytes);
167
168        DataUsageRequest request2 = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
169                Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
170        assertTrue(request2.requestId > request1.requestId);
171        assertTrue(Objects.equals(sTemplateWifi, request2.template));
172        assertEquals(THRESHOLD_BYTES, request2.thresholdInBytes);
173    }
174
175    public void testUnregister_unknownRequest_noop() throws Exception {
176        DataUsageRequest unknownRequest = new DataUsageRequest(
177                123456 /* id */, sTemplateWifi, THRESHOLD_BYTES);
178
179        mStatsObservers.unregister(unknownRequest, UID_RED);
180    }
181
182    public void testUnregister_knownRequest_releasesCaller() throws Exception {
183        DataUsageRequest inputRequest = new DataUsageRequest(
184                DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
185
186        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
187                Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
188        assertTrue(request.requestId > 0);
189        assertTrue(Objects.equals(sTemplateImsi1, request.template));
190        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
191        Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
192
193        mStatsObservers.unregister(request, Process.SYSTEM_UID);
194        waitForObserverToIdle();
195
196        Mockito.verify(mockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
197    }
198
199    public void testUnregister_knownRequest_invalidUid_doesNotUnregister() throws Exception {
200        DataUsageRequest inputRequest = new DataUsageRequest(
201                DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
202
203        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
204                UID_RED, NetworkStatsAccess.Level.DEVICE);
205        assertTrue(request.requestId > 0);
206        assertTrue(Objects.equals(sTemplateImsi1, request.template));
207        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
208        Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
209
210        mStatsObservers.unregister(request, UID_BLUE);
211        waitForObserverToIdle();
212
213        Mockito.verifyZeroInteractions(mockBinder);
214    }
215
216    public void testUpdateStats_initialSample_doesNotNotify() throws Exception {
217        DataUsageRequest inputRequest = new DataUsageRequest(
218                DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
219
220        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
221                Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
222        assertTrue(request.requestId > 0);
223        assertTrue(Objects.equals(sTemplateImsi1, request.template));
224        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
225
226        NetworkIdentitySet identSet = new NetworkIdentitySet();
227        identSet.add(new NetworkIdentity(
228                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
229                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
230        mActiveIfaces.put(TEST_IFACE, identSet);
231
232        // Baseline
233        NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
234                .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
235        NetworkStats uidSnapshot = null;
236
237        mStatsObservers.updateStats(
238                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
239                VPN_INFO, TEST_START);
240        waitForObserverToIdle();
241
242        assertTrue(mCv.block(WAIT_TIMEOUT));
243        assertEquals(INVALID_TYPE, mHandler.mLastMessageType);
244    }
245
246    public void testUpdateStats_belowThreshold_doesNotNotify() throws Exception {
247        DataUsageRequest inputRequest = new DataUsageRequest(
248                DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
249
250        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
251                Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
252        assertTrue(request.requestId > 0);
253        assertTrue(Objects.equals(sTemplateImsi1, request.template));
254        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
255
256        NetworkIdentitySet identSet = new NetworkIdentitySet();
257        identSet.add(new NetworkIdentity(
258                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
259                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
260        mActiveIfaces.put(TEST_IFACE, identSet);
261
262        // Baseline
263        NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
264                .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
265        NetworkStats uidSnapshot = null;
266        mStatsObservers.updateStats(
267                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
268                VPN_INFO, TEST_START);
269
270        // Delta
271        xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
272                .addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
273        mStatsObservers.updateStats(
274                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
275                VPN_INFO, TEST_START);
276        waitForObserverToIdle();
277
278        assertTrue(mCv.block(WAIT_TIMEOUT));
279        mCv.block(WAIT_TIMEOUT);
280        assertEquals(INVALID_TYPE, mHandler.mLastMessageType);
281    }
282
283    public void testUpdateStats_deviceAccess_notifies() throws Exception {
284        DataUsageRequest inputRequest = new DataUsageRequest(
285                DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
286
287        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
288                Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
289        assertTrue(request.requestId > 0);
290        assertTrue(Objects.equals(sTemplateImsi1, request.template));
291        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
292
293        NetworkIdentitySet identSet = new NetworkIdentitySet();
294        identSet.add(new NetworkIdentity(
295                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
296                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
297        mActiveIfaces.put(TEST_IFACE, identSet);
298
299        // Baseline
300        NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
301                .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
302        NetworkStats uidSnapshot = null;
303        mStatsObservers.updateStats(
304                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
305                VPN_INFO, TEST_START);
306
307        // Delta
308        xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
309                .addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
310                        BASE_BYTES + THRESHOLD_BYTES, 22L);
311        mStatsObservers.updateStats(
312                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
313                VPN_INFO, TEST_START);
314        waitForObserverToIdle();
315
316        assertTrue(mCv.block(WAIT_TIMEOUT));
317        assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
318    }
319
320    public void testUpdateStats_defaultAccess_notifiesSameUid() throws Exception {
321        DataUsageRequest inputRequest = new DataUsageRequest(
322                DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
323
324        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
325                UID_RED, NetworkStatsAccess.Level.DEFAULT);
326        assertTrue(request.requestId > 0);
327        assertTrue(Objects.equals(sTemplateImsi1, request.template));
328        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
329
330        NetworkIdentitySet identSet = new NetworkIdentitySet();
331        identSet.add(new NetworkIdentity(
332                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
333                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
334        mActiveUidIfaces.put(TEST_IFACE, identSet);
335
336        // Baseline
337        NetworkStats xtSnapshot = null;
338        NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
339                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
340                        BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
341        mStatsObservers.updateStats(
342                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
343                VPN_INFO, TEST_START);
344
345        // Delta
346        uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
347                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
348                        BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
349        mStatsObservers.updateStats(
350                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
351                VPN_INFO, TEST_START);
352        waitForObserverToIdle();
353
354        assertTrue(mCv.block(WAIT_TIMEOUT));
355        assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
356    }
357
358    public void testUpdateStats_defaultAccess_usageOtherUid_doesNotNotify() throws Exception {
359        DataUsageRequest inputRequest = new DataUsageRequest(
360                DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
361
362        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
363                UID_BLUE, NetworkStatsAccess.Level.DEFAULT);
364        assertTrue(request.requestId > 0);
365        assertTrue(Objects.equals(sTemplateImsi1, request.template));
366        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
367
368        NetworkIdentitySet identSet = new NetworkIdentitySet();
369        identSet.add(new NetworkIdentity(
370                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
371                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
372        mActiveUidIfaces.put(TEST_IFACE, identSet);
373
374        // Baseline
375        NetworkStats xtSnapshot = null;
376        NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
377                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
378                        BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
379        mStatsObservers.updateStats(
380                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
381                VPN_INFO, TEST_START);
382
383        // Delta
384        uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
385                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
386                        BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
387        mStatsObservers.updateStats(
388                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
389                VPN_INFO, TEST_START);
390        waitForObserverToIdle();
391
392        assertTrue(mCv.block(WAIT_TIMEOUT));
393        assertEquals(INVALID_TYPE, mHandler.mLastMessageType);
394    }
395
396    public void testUpdateStats_userAccess_usageSameUser_notifies() throws Exception {
397        DataUsageRequest inputRequest = new DataUsageRequest(
398                DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
399
400        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
401                UID_BLUE, NetworkStatsAccess.Level.USER);
402        assertTrue(request.requestId > 0);
403        assertTrue(Objects.equals(sTemplateImsi1, request.template));
404        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
405
406        NetworkIdentitySet identSet = new NetworkIdentitySet();
407        identSet.add(new NetworkIdentity(
408                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
409                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
410        mActiveUidIfaces.put(TEST_IFACE, identSet);
411
412        // Baseline
413        NetworkStats xtSnapshot = null;
414        NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
415                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
416                        BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
417        mStatsObservers.updateStats(
418                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
419                VPN_INFO, TEST_START);
420
421        // Delta
422        uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
423                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
424                        BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
425        mStatsObservers.updateStats(
426                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
427                VPN_INFO, TEST_START);
428        waitForObserverToIdle();
429
430        assertTrue(mCv.block(WAIT_TIMEOUT));
431        assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
432    }
433
434    public void testUpdateStats_userAccess_usageAnotherUser_doesNotNotify() throws Exception {
435        DataUsageRequest inputRequest = new DataUsageRequest(
436                DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
437
438        DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
439                UID_RED, NetworkStatsAccess.Level.USER);
440        assertTrue(request.requestId > 0);
441        assertTrue(Objects.equals(sTemplateImsi1, request.template));
442        assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
443
444        NetworkIdentitySet identSet = new NetworkIdentitySet();
445        identSet.add(new NetworkIdentity(
446                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
447                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
448        mActiveUidIfaces.put(TEST_IFACE, identSet);
449
450        // Baseline
451        NetworkStats xtSnapshot = null;
452        NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
453                .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_NO,
454                        BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
455        mStatsObservers.updateStats(
456                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
457                VPN_INFO, TEST_START);
458
459        // Delta
460        uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
461                .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_NO,
462                        BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
463        mStatsObservers.updateStats(
464                xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
465                VPN_INFO, TEST_START);
466        waitForObserverToIdle();
467
468        assertTrue(mCv.block(WAIT_TIMEOUT));
469        assertEquals(INVALID_TYPE, mHandler.mLastMessageType);
470    }
471
472    private void waitForObserverToIdle() {
473        // Send dummy message to make sure that any previous message has been handled
474        mHandler.sendMessage(mHandler.obtainMessage(-1));
475        mObserverHandlerThread.waitForIdle(WAIT_TIMEOUT);
476    }
477}
478