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 
17 package com.android.server.connectivity;
18 
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertTrue;
21 import static org.mockito.Mockito.any;
22 import static org.mockito.Mockito.anyBoolean;
23 import static org.mockito.Mockito.anyInt;
24 import static org.mockito.Mockito.doReturn;
25 import static org.mockito.Mockito.eq;
26 import static org.mockito.Mockito.never;
27 import static org.mockito.Mockito.reset;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31 
32 import android.app.PendingIntent;
33 import android.content.Context;
34 import android.content.pm.PackageManager;
35 import android.content.res.Resources;
36 import android.net.ConnectivityManager;
37 import android.net.IDnsResolver;
38 import android.net.INetd;
39 import android.net.LinkProperties;
40 import android.net.Network;
41 import android.net.NetworkAgentConfig;
42 import android.net.NetworkCapabilities;
43 import android.net.NetworkInfo;
44 import android.net.NetworkProvider;
45 import android.net.NetworkScore;
46 import android.os.Binder;
47 import android.os.Build;
48 import android.text.format.DateUtils;
49 
50 import androidx.test.filters.SmallTest;
51 
52 import com.android.connectivity.resources.R;
53 import com.android.server.ConnectivityService;
54 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
55 import com.android.testutils.DevSdkIgnoreRule;
56 import com.android.testutils.DevSdkIgnoreRunner;
57 
58 import org.junit.After;
59 import org.junit.Before;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.mockito.Mock;
63 import org.mockito.MockitoAnnotations;
64 
65 @RunWith(DevSdkIgnoreRunner.class)
66 @SmallTest
67 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
68 public class LingerMonitorTest {
69     static final String CELLULAR = "CELLULAR";
70     static final String WIFI     = "WIFI";
71 
72     static final long LOW_RATE_LIMIT = DateUtils.MINUTE_IN_MILLIS;
73     static final long HIGH_RATE_LIMIT = 0;
74 
75     static final int LOW_DAILY_LIMIT = 2;
76     static final int HIGH_DAILY_LIMIT = 1000;
77 
78     private static final int TEST_LINGER_DELAY_MS = 400;
79 
80     LingerMonitor mMonitor;
81 
82     @Mock ConnectivityService mConnService;
83     @Mock IDnsResolver mDnsResolver;
84     @Mock INetd mNetd;
85     @Mock Context mCtx;
86     @Mock NetworkNotificationManager mNotifier;
87     @Mock Resources mResources;
88     @Mock QosCallbackTracker mQosCallbackTracker;
89     @Mock PackageManager mPackageManager;
90 
91     @Before
setUp()92     public void setUp() {
93         MockitoAnnotations.initMocks(this);
94         when(mCtx.getResources()).thenReturn(mResources);
95         when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
96         doReturn(mPackageManager).when(mCtx).getPackageManager();
97         ConnectivityResources.setResourcesContextForTest(mCtx);
98 
99         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
100     }
101 
102     @After
tearDown()103     public void tearDown() {
104         ConnectivityResources.setResourcesContextForTest(null);
105     }
106 
107     @Test
testTransitions()108     public void testTransitions() {
109         setNotificationSwitch(transition(WIFI, CELLULAR));
110         NetworkAgentInfo nai1 = wifiNai(100);
111         NetworkAgentInfo nai2 = cellNai(101);
112 
113         assertTrue(mMonitor.isNotificationEnabled(nai1, nai2));
114         assertFalse(mMonitor.isNotificationEnabled(nai2, nai1));
115     }
116 
117     @Test
testNotificationOnLinger()118     public void testNotificationOnLinger() {
119         setNotificationSwitch(transition(WIFI, CELLULAR));
120         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
121         NetworkAgentInfo from = wifiNai(100);
122         NetworkAgentInfo to = cellNai(101);
123 
124         mMonitor.noteLingerDefaultNetwork(from, to);
125         verifyNotification(from, to);
126     }
127 
128     @Test
testToastOnLinger()129     public void testToastOnLinger() {
130         setNotificationSwitch(transition(WIFI, CELLULAR));
131         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
132         NetworkAgentInfo from = wifiNai(100);
133         NetworkAgentInfo to = cellNai(101);
134 
135         mMonitor.noteLingerDefaultNetwork(from, to);
136         verifyToast(from, to);
137     }
138 
139     @Test
testNotificationClearedAfterDisconnect()140     public void testNotificationClearedAfterDisconnect() {
141         setNotificationSwitch(transition(WIFI, CELLULAR));
142         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
143         NetworkAgentInfo from = wifiNai(100);
144         NetworkAgentInfo to = cellNai(101);
145 
146         mMonitor.noteLingerDefaultNetwork(from, to);
147         verifyNotification(from, to);
148 
149         mMonitor.noteDisconnect(to);
150         verify(mNotifier, times(1)).clearNotification(100);
151     }
152 
153     @Test
testNotificationClearedAfterSwitchingBack()154     public void testNotificationClearedAfterSwitchingBack() {
155         setNotificationSwitch(transition(WIFI, CELLULAR));
156         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
157         NetworkAgentInfo from = wifiNai(100);
158         NetworkAgentInfo to = cellNai(101);
159 
160         mMonitor.noteLingerDefaultNetwork(from, to);
161         verifyNotification(from, to);
162 
163         mMonitor.noteLingerDefaultNetwork(to, from);
164         verify(mNotifier, times(1)).clearNotification(100);
165     }
166 
167     @Test
testUniqueToast()168     public void testUniqueToast() {
169         setNotificationSwitch(transition(WIFI, CELLULAR));
170         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
171         NetworkAgentInfo from = wifiNai(100);
172         NetworkAgentInfo to = cellNai(101);
173 
174         mMonitor.noteLingerDefaultNetwork(from, to);
175         verifyToast(from, to);
176 
177         mMonitor.noteLingerDefaultNetwork(to, from);
178         verify(mNotifier, times(1)).clearNotification(100);
179 
180         reset(mNotifier);
181         mMonitor.noteLingerDefaultNetwork(from, to);
182         verifyNoNotifications();
183     }
184 
185     @Test
testMultipleNotifications()186     public void testMultipleNotifications() {
187         setNotificationSwitch(transition(WIFI, CELLULAR));
188         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
189         NetworkAgentInfo wifi1 = wifiNai(100);
190         NetworkAgentInfo wifi2 = wifiNai(101);
191         NetworkAgentInfo cell = cellNai(102);
192 
193         mMonitor.noteLingerDefaultNetwork(wifi1, cell);
194         verifyNotification(wifi1, cell);
195 
196         mMonitor.noteLingerDefaultNetwork(cell, wifi2);
197         verify(mNotifier, times(1)).clearNotification(100);
198 
199         reset(mNotifier);
200         mMonitor.noteLingerDefaultNetwork(wifi2, cell);
201         verifyNotification(wifi2, cell);
202     }
203 
204     @Test
testRateLimiting()205     public void testRateLimiting() throws InterruptedException {
206         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, LOW_RATE_LIMIT);
207 
208         setNotificationSwitch(transition(WIFI, CELLULAR));
209         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
210         NetworkAgentInfo wifi1 = wifiNai(100);
211         NetworkAgentInfo wifi2 = wifiNai(101);
212         NetworkAgentInfo wifi3 = wifiNai(102);
213         NetworkAgentInfo cell = cellNai(103);
214 
215         mMonitor.noteLingerDefaultNetwork(wifi1, cell);
216         verifyNotification(wifi1, cell);
217         reset(mNotifier);
218 
219         Thread.sleep(50);
220         mMonitor.noteLingerDefaultNetwork(cell, wifi2);
221         mMonitor.noteLingerDefaultNetwork(wifi2, cell);
222         verifyNoNotifications();
223 
224         Thread.sleep(50);
225         mMonitor.noteLingerDefaultNetwork(cell, wifi3);
226         mMonitor.noteLingerDefaultNetwork(wifi3, cell);
227         verifyNoNotifications();
228     }
229 
230     @Test
testDailyLimiting()231     public void testDailyLimiting() throws InterruptedException {
232         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, LOW_DAILY_LIMIT, HIGH_RATE_LIMIT);
233 
234         setNotificationSwitch(transition(WIFI, CELLULAR));
235         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
236         NetworkAgentInfo wifi1 = wifiNai(100);
237         NetworkAgentInfo wifi2 = wifiNai(101);
238         NetworkAgentInfo wifi3 = wifiNai(102);
239         NetworkAgentInfo cell = cellNai(103);
240 
241         mMonitor.noteLingerDefaultNetwork(wifi1, cell);
242         verifyNotification(wifi1, cell);
243         reset(mNotifier);
244 
245         Thread.sleep(50);
246         mMonitor.noteLingerDefaultNetwork(cell, wifi2);
247         mMonitor.noteLingerDefaultNetwork(wifi2, cell);
248         verifyNotification(wifi2, cell);
249         reset(mNotifier);
250 
251         Thread.sleep(50);
252         mMonitor.noteLingerDefaultNetwork(cell, wifi3);
253         mMonitor.noteLingerDefaultNetwork(wifi3, cell);
254         verifyNoNotifications();
255     }
256 
257     @Test
testUniqueNotification()258     public void testUniqueNotification() {
259         setNotificationSwitch(transition(WIFI, CELLULAR));
260         setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION);
261         NetworkAgentInfo from = wifiNai(100);
262         NetworkAgentInfo to = cellNai(101);
263 
264         mMonitor.noteLingerDefaultNetwork(from, to);
265         verifyNotification(from, to);
266 
267         mMonitor.noteLingerDefaultNetwork(to, from);
268         verify(mNotifier, times(1)).clearNotification(100);
269 
270         mMonitor.noteLingerDefaultNetwork(from, to);
271         verifyNotification(from, to);
272     }
273 
274     @Test
testIgnoreNeverValidatedNetworks()275     public void testIgnoreNeverValidatedNetworks() {
276         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
277         setNotificationSwitch(transition(WIFI, CELLULAR));
278         NetworkAgentInfo from = wifiNai(100, false /* setEverValidated */);
279         NetworkAgentInfo to = cellNai(101);
280 
281         mMonitor.noteLingerDefaultNetwork(from, to);
282         verifyNoNotifications();
283     }
284 
285     @Test
testIgnoreCurrentlyValidatedNetworks()286     public void testIgnoreCurrentlyValidatedNetworks() {
287         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
288         setNotificationSwitch(transition(WIFI, CELLULAR));
289         NetworkAgentInfo from = wifiNai(100);
290         NetworkAgentInfo to = cellNai(101);
291         from.setValidated(true);
292 
293         mMonitor.noteLingerDefaultNetwork(from, to);
294         verifyNoNotifications();
295     }
296 
297     @Test
testNoNotificationType()298     public void testNoNotificationType() {
299         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
300         setNotificationSwitch();
301         NetworkAgentInfo from = wifiNai(100);
302         NetworkAgentInfo to = cellNai(101);
303 
304         mMonitor.noteLingerDefaultNetwork(from, to);
305         verifyNoNotifications();
306     }
307 
308     @Test
testNoTransitionToNotify()309     public void testNoTransitionToNotify() {
310         setNotificationType(LingerMonitor.NOTIFY_TYPE_NONE);
311         setNotificationSwitch(transition(WIFI, CELLULAR));
312         NetworkAgentInfo from = wifiNai(100);
313         NetworkAgentInfo to = cellNai(101);
314 
315         mMonitor.noteLingerDefaultNetwork(from, to);
316         verifyNoNotifications();
317     }
318 
319     @Test
testDifferentTransitionToNotify()320     public void testDifferentTransitionToNotify() {
321         setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST);
322         setNotificationSwitch(transition(CELLULAR, WIFI));
323         NetworkAgentInfo from = wifiNai(100);
324         NetworkAgentInfo to = cellNai(101);
325 
326         mMonitor.noteLingerDefaultNetwork(from, to);
327         verifyNoNotifications();
328     }
329 
setNotificationSwitch(String... transitions)330     void setNotificationSwitch(String... transitions) {
331         when(mResources.getStringArray(R.array.config_networkNotifySwitches))
332                 .thenReturn(transitions);
333     }
334 
transition(String from, String to)335     String transition(String from, String to) {
336         return from + "-" + to;
337     }
338 
setNotificationType(int type)339     void setNotificationType(int type) {
340         when(mResources.getInteger(R.integer.config_networkNotifySwitchType)).thenReturn(type);
341     }
342 
verifyNoToast()343     void verifyNoToast() {
344         verify(mNotifier, never()).showToast(any(), any());
345     }
346 
verifyNoNotification()347     void verifyNoNotification() {
348         verify(mNotifier, never())
349                 .showNotification(anyInt(), any(), any(), any(), any(), anyBoolean());
350     }
351 
verifyNoNotifications()352     void verifyNoNotifications() {
353         verifyNoToast();
354         verifyNoNotification();
355     }
356 
verifyToast(NetworkAgentInfo from, NetworkAgentInfo to)357     void verifyToast(NetworkAgentInfo from, NetworkAgentInfo to) {
358         verifyNoNotification();
359         verify(mNotifier, times(1)).showToast(from, to);
360     }
361 
verifyNotification(NetworkAgentInfo from, NetworkAgentInfo to)362     void verifyNotification(NetworkAgentInfo from, NetworkAgentInfo to) {
363         verifyNoToast();
364         verify(mNotifier, times(1)).showNotification(eq(from.network.netId),
365                 eq(NotificationType.NETWORK_SWITCH), eq(from), eq(to), any(), eq(true));
366     }
367 
nai(int netId, int transport, int networkType, String networkTypeName, boolean setEverValidated)368     NetworkAgentInfo nai(int netId, int transport, int networkType, String networkTypeName,
369             boolean setEverValidated) {
370         NetworkInfo info = new NetworkInfo(networkType, 0, networkTypeName, "");
371         NetworkCapabilities caps = new NetworkCapabilities();
372         caps.addCapability(0);
373         caps.addTransportType(transport);
374         NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
375                 new LinkProperties(), caps, null /* localNetworkConfiguration */,
376                 new NetworkScore.Builder().setLegacyInt(50).build(), mCtx, null,
377                 new NetworkAgentConfig.Builder().build(), mConnService, mNetd, mDnsResolver,
378                 NetworkProvider.ID_NONE, Binder.getCallingUid(), TEST_LINGER_DELAY_MS,
379                 mQosCallbackTracker, new ConnectivityService.Dependencies());
380         if (setEverValidated) {
381             // As tests in this class deal with testing lingering, most tests are interested
382             // in networks that can be lingered, and therefore must have validated in the past.
383             // Thus, pretend the network validated once, then became invalidated.
384             nai.setValidated(true);
385             nai.setValidated(false);
386         }
387         return nai;
388     }
389 
wifiNai(int netId)390     NetworkAgentInfo wifiNai(int netId) {
391         return wifiNai(netId, true /* setEverValidated */);
392     }
393 
wifiNai(int netId, boolean setEverValidated)394     NetworkAgentInfo wifiNai(int netId, boolean setEverValidated) {
395         return nai(netId, NetworkCapabilities.TRANSPORT_WIFI,
396                 ConnectivityManager.TYPE_WIFI, WIFI, setEverValidated);
397     }
398 
cellNai(int netId)399     NetworkAgentInfo cellNai(int netId) {
400         return cellNai(netId, true /* setEverValidated */);
401     }
402 
cellNai(int netId, boolean setEverValidated)403     NetworkAgentInfo cellNai(int netId, boolean setEverValidated) {
404         return nai(netId, NetworkCapabilities.TRANSPORT_CELLULAR,
405                 ConnectivityManager.TYPE_MOBILE, CELLULAR, setEverValidated);
406     }
407 
408     public static class TestableLingerMonitor extends LingerMonitor {
TestableLingerMonitor(Context c, NetworkNotificationManager n, int l, long r)409         public TestableLingerMonitor(Context c, NetworkNotificationManager n, int l, long r) {
410             super(c, n, l, r);
411         }
createNotificationIntent()412         @Override protected PendingIntent createNotificationIntent() {
413             return null;
414         }
415     }
416 }
417