NetworkScoreServiceTest.java revision 8f5521aa679f21bc106dec90ecf21cd2283747a7
1/*
2 * Copyright (C) 2012 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;
18
19import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
20import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE;
21import static android.net.NetworkScoreManager.CACHE_FILTER_NONE;
22
23import static junit.framework.Assert.assertEquals;
24import static junit.framework.Assert.assertFalse;
25import static junit.framework.Assert.assertNotNull;
26import static junit.framework.Assert.assertTrue;
27import static junit.framework.Assert.fail;
28
29import static org.mockito.Matchers.any;
30import static org.mockito.Matchers.anyInt;
31import static org.mockito.Matchers.anyListOf;
32import static org.mockito.Matchers.anyString;
33import static org.mockito.Matchers.eq;
34import static org.mockito.Matchers.isA;
35import static org.mockito.Mockito.atLeastOnce;
36import static org.mockito.Mockito.doAnswer;
37import static org.mockito.Mockito.doThrow;
38import static org.mockito.Mockito.mock;
39import static org.mockito.Mockito.times;
40import static org.mockito.Mockito.verify;
41import static org.mockito.Mockito.verifyNoMoreInteractions;
42import static org.mockito.Mockito.verifyZeroInteractions;
43import static org.mockito.Mockito.when;
44
45import android.Manifest.permission;
46import android.content.ComponentName;
47import android.content.ContentResolver;
48import android.content.Context;
49import android.content.Intent;
50import android.content.ServiceConnection;
51import android.content.pm.PackageManager;
52import android.content.res.Resources;
53import android.net.INetworkRecommendationProvider;
54import android.net.INetworkScoreCache;
55import android.net.NetworkKey;
56import android.net.NetworkScorerAppManager;
57import android.net.NetworkScorerAppManager.NetworkScorerAppData;
58import android.net.RecommendationRequest;
59import android.net.RecommendationResult;
60import android.net.ScoredNetwork;
61import android.net.WifiKey;
62import android.net.wifi.WifiConfiguration;
63import android.os.Bundle;
64import android.os.IBinder;
65import android.os.IRemoteCallback;
66import android.os.Looper;
67import android.os.RemoteException;
68import android.os.UserHandle;
69import android.support.test.InstrumentationRegistry;
70import android.support.test.filters.MediumTest;
71import android.support.test.runner.AndroidJUnit4;
72
73import com.android.server.devicepolicy.MockUtils;
74
75import org.junit.Before;
76import org.junit.Test;
77import org.junit.runner.RunWith;
78import org.mockito.ArgumentCaptor;
79import org.mockito.Captor;
80import org.mockito.Mock;
81import org.mockito.MockitoAnnotations;
82import org.mockito.invocation.InvocationOnMock;
83import org.mockito.stubbing.Answer;
84
85import java.io.FileDescriptor;
86import java.io.PrintWriter;
87import java.io.StringWriter;
88import java.util.List;
89
90/**
91 * Tests for {@link NetworkScoreService}.
92 */
93@RunWith(AndroidJUnit4.class)
94@MediumTest
95public class NetworkScoreServiceTest {
96    private static final ScoredNetwork SCORED_NETWORK =
97            new ScoredNetwork(new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00")),
98                    null /* rssiCurve*/);
99    private static final NetworkScorerAppData NEW_SCORER =
100        new NetworkScorerAppData("newPackageName", 1, "newScoringServiceClass");
101
102    @Mock private PackageManager mPackageManager;
103    @Mock private NetworkScorerAppManager mNetworkScorerAppManager;
104    @Mock private Context mContext;
105    @Mock private Resources mResources;
106    @Mock private INetworkScoreCache.Stub mNetworkScoreCache, mNetworkScoreCache2;
107    @Mock private IBinder mIBinder, mIBinder2;
108    @Mock private INetworkRecommendationProvider mRecommendationProvider;
109    @Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor;
110
111    private ContentResolver mContentResolver;
112    private NetworkScoreService mNetworkScoreService;
113    private RecommendationRequest mRecommendationRequest;
114
115    @Before
116    public void setUp() throws Exception {
117        MockitoAnnotations.initMocks(this);
118        when(mNetworkScoreCache.asBinder()).thenReturn(mIBinder);
119        when(mNetworkScoreCache2.asBinder()).thenReturn(mIBinder2);
120        mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
121        when(mContext.getContentResolver()).thenReturn(mContentResolver);
122        when(mContext.getResources()).thenReturn(mResources);
123        mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager);
124        WifiConfiguration configuration = new WifiConfiguration();
125        configuration.SSID = "NetworkScoreServiceTest_SSID";
126        configuration.BSSID = "NetworkScoreServiceTest_BSSID";
127        mRecommendationRequest = new RecommendationRequest.Builder()
128            .setCurrentRecommendedWifiConfig(configuration).build();
129    }
130
131    @Test
132    public void testSystemRunning() {
133        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
134
135        mNetworkScoreService.systemRunning();
136
137        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
138                new ComponentName(NEW_SCORER.packageName,
139                    NEW_SCORER.recommendationServiceClassName))),
140                any(ServiceConnection.class),
141                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
142                eq(UserHandle.SYSTEM));
143    }
144
145    @Test
146    public void testRequestScores_noPermission() throws Exception {
147        doThrow(new SecurityException()).when(mContext)
148            .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
149                anyString());
150        try {
151            mNetworkScoreService.requestScores(null);
152            fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
153        } catch (SecurityException e) {
154            // expected
155        }
156    }
157
158    @Test
159    public void testRequestScores_providerNotConnected() throws Exception {
160        assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
161        verifyZeroInteractions(mRecommendationProvider);
162    }
163
164    @Test
165    public void testRequestScores_providerThrowsRemoteException() throws Exception {
166        injectProvider();
167        doThrow(new RemoteException()).when(mRecommendationProvider)
168            .requestScores(any(NetworkKey[].class));
169
170        assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
171    }
172
173    @Test
174    public void testRequestScores_providerAvailable() throws Exception {
175        injectProvider();
176
177        final NetworkKey[] networks = new NetworkKey[0];
178        assertTrue(mNetworkScoreService.requestScores(networks));
179        verify(mRecommendationProvider).requestScores(networks);
180    }
181
182    @Test
183    public void testRequestRecommendation_noPermission() throws Exception {
184        doThrow(new SecurityException()).when(mContext)
185            .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
186                anyString());
187        try {
188            mNetworkScoreService.requestRecommendation(mRecommendationRequest);
189            fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
190        } catch (SecurityException e) {
191            // expected
192        }
193    }
194
195    @Test
196    public void testRequestRecommendation_mainThread() throws Exception {
197        when(mContext.getMainLooper()).thenReturn(Looper.myLooper());
198        try {
199            mNetworkScoreService.requestRecommendation(mRecommendationRequest);
200            fail("requestRecommendation run on main thread.");
201        } catch (RuntimeException e) {
202            // expected
203        }
204    }
205
206    @Test
207    public void testRequestRecommendation_providerNotConnected() throws Exception {
208        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
209
210        final RecommendationResult result =
211                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
212        assertNotNull(result);
213        assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
214                result.getWifiConfiguration());
215    }
216
217    @Test
218    public void testRequestRecommendation_providerThrowsRemoteException() throws Exception {
219        injectProvider();
220        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
221        doThrow(new RemoteException()).when(mRecommendationProvider)
222                .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
223                        anyInt());
224
225        final RecommendationResult result =
226                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
227        assertNotNull(result);
228        assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
229                result.getWifiConfiguration());
230    }
231
232    @Test
233    public void testRequestRecommendation_resultReturned() throws Exception {
234        injectProvider();
235        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
236        final WifiConfiguration wifiConfiguration = new WifiConfiguration();
237        wifiConfiguration.SSID = "testRequestRecommendation_resultReturned_SSID";
238        wifiConfiguration.BSSID = "testRequestRecommendation_resultReturned_BSSID";
239        final RecommendationResult providerResult = RecommendationResult
240                .createConnectRecommendation(wifiConfiguration);
241        final Bundle bundle = new Bundle();
242        bundle.putParcelable(EXTRA_RECOMMENDATION_RESULT, providerResult);
243        doAnswer(invocation -> {
244            bundle.putInt(EXTRA_SEQUENCE, invocation.getArgumentAt(2, int.class));
245            invocation.getArgumentAt(1, IRemoteCallback.class).sendResult(bundle);
246            return null;
247        }).when(mRecommendationProvider)
248                .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
249                        anyInt());
250
251        final RecommendationResult result =
252                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
253        assertNotNull(result);
254        assertEquals(providerResult.getWifiConfiguration().SSID,
255                result.getWifiConfiguration().SSID);
256        assertEquals(providerResult.getWifiConfiguration().BSSID,
257                result.getWifiConfiguration().BSSID);
258    }
259
260    @Test
261    public void testUpdateScores_notActiveScorer() {
262        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
263
264        try {
265            mNetworkScoreService.updateScores(new ScoredNetwork[0]);
266            fail("SecurityException expected");
267        } catch (SecurityException e) {
268            // expected
269        }
270    }
271
272    @Test
273    public void testUpdateScores_oneRegisteredCache() throws RemoteException {
274        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
275
276        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
277                mNetworkScoreCache, CACHE_FILTER_NONE);
278
279        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
280
281        verify(mNetworkScoreCache).updateScores(mScoredNetworkCaptor.capture());
282
283        assertEquals(1, mScoredNetworkCaptor.getValue().size());
284        assertEquals(SCORED_NETWORK, mScoredNetworkCaptor.getValue().get(0));
285    }
286
287    @Test
288    public void testUpdateScores_twoRegisteredCaches() throws RemoteException {
289        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
290
291        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
292                mNetworkScoreCache, CACHE_FILTER_NONE);
293        mNetworkScoreService.registerNetworkScoreCache(
294                NetworkKey.TYPE_WIFI, mNetworkScoreCache2, CACHE_FILTER_NONE);
295
296        // updateScores should update both caches
297        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
298
299        verify(mNetworkScoreCache).updateScores(anyListOf(ScoredNetwork.class));
300        verify(mNetworkScoreCache2).updateScores(anyListOf(ScoredNetwork.class));
301
302        mNetworkScoreService.unregisterNetworkScoreCache(
303                NetworkKey.TYPE_WIFI, mNetworkScoreCache2);
304
305        // updateScores should only update the first cache since the 2nd has been unregistered
306        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
307
308        verify(mNetworkScoreCache, times(2)).updateScores(anyListOf(ScoredNetwork.class));
309
310        mNetworkScoreService.unregisterNetworkScoreCache(
311                NetworkKey.TYPE_WIFI, mNetworkScoreCache);
312
313        // updateScores should not update any caches since they are both unregistered
314        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
315
316        // The register and unregister calls grab the binder from the score cache.
317        verify(mNetworkScoreCache, atLeastOnce()).asBinder();
318        verify(mNetworkScoreCache2, atLeastOnce()).asBinder();
319        verifyNoMoreInteractions(mNetworkScoreCache, mNetworkScoreCache2);
320    }
321
322    @Test
323    public void testClearScores_notActiveScorer_noBroadcastNetworkPermission() {
324        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
325        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
326            .thenReturn(PackageManager.PERMISSION_DENIED);
327        try {
328            mNetworkScoreService.clearScores();
329            fail("SecurityException expected");
330        } catch (SecurityException e) {
331            // expected
332        }
333    }
334
335    @Test
336    public void testClearScores_activeScorer_noBroadcastNetworkPermission() {
337        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
338        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
339            .thenReturn(PackageManager.PERMISSION_DENIED);
340
341        mNetworkScoreService.clearScores();
342    }
343
344    @Test
345    public void testClearScores_activeScorer() throws RemoteException {
346        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
347
348        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
349                CACHE_FILTER_NONE);
350        mNetworkScoreService.clearScores();
351
352        verify(mNetworkScoreCache).clearScores();
353    }
354
355    @Test
356    public void testClearScores_notActiveScorer_hasBroadcastNetworkPermission()
357            throws RemoteException {
358        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
359        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
360                .thenReturn(PackageManager.PERMISSION_GRANTED);
361
362        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
363                CACHE_FILTER_NONE);
364        mNetworkScoreService.clearScores();
365
366        verify(mNetworkScoreCache).clearScores();
367    }
368
369    @Test
370    public void testSetActiveScorer_noScoreNetworksPermission() {
371        doThrow(new SecurityException()).when(mContext)
372                .enforceCallingOrSelfPermission(eq(permission.SCORE_NETWORKS), anyString());
373
374        try {
375            mNetworkScoreService.setActiveScorer(null);
376            fail("SecurityException expected");
377        } catch (SecurityException e) {
378            // expected
379        }
380    }
381
382    @Test
383    public void testDisableScoring_notActiveScorer_noBroadcastNetworkPermission() {
384        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
385        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
386                .thenReturn(PackageManager.PERMISSION_DENIED);
387
388        try {
389            mNetworkScoreService.disableScoring();
390            fail("SecurityException expected");
391        } catch (SecurityException e) {
392            // expected
393        }
394    }
395
396    @Test
397    public void testRegisterNetworkScoreCache_noBroadcastNetworkPermission() {
398        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
399                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
400
401        try {
402            mNetworkScoreService.registerNetworkScoreCache(
403                NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE);
404            fail("SecurityException expected");
405        } catch (SecurityException e) {
406            // expected
407        }
408    }
409
410    @Test
411    public void testUnregisterNetworkScoreCache_noBroadcastNetworkPermission() {
412        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
413                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
414
415        try {
416            mNetworkScoreService.unregisterNetworkScoreCache(
417                    NetworkKey.TYPE_WIFI, mNetworkScoreCache);
418            fail("SecurityException expected");
419        } catch (SecurityException e) {
420            // expected
421        }
422    }
423
424    @Test
425    public void testDump_noDumpPermission() {
426        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
427                eq(permission.DUMP), anyString());
428
429        try {
430            mNetworkScoreService.dump(
431                    new FileDescriptor(), new PrintWriter(new StringWriter()), new String[0]);
432            fail("SecurityException expected");
433        } catch (SecurityException e) {
434            // expected
435        }
436    }
437
438    @Test
439    public void testDump_doesNotCrash() {
440        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
441        StringWriter stringWriter = new StringWriter();
442
443        mNetworkScoreService.dump(
444                new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
445
446        assertFalse(stringWriter.toString().isEmpty());
447    }
448
449    // "injects" the mock INetworkRecommendationProvider into the NetworkScoreService.
450    private void injectProvider() {
451        final ComponentName componentName = new ComponentName(NEW_SCORER.packageName,
452                NEW_SCORER.recommendationServiceClassName);
453        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
454        when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
455                isA(UserHandle.class))).thenAnswer(new Answer<Boolean>() {
456            @Override
457            public Boolean answer(InvocationOnMock invocation) throws Throwable {
458                IBinder mockBinder = mock(IBinder.class);
459                when(mockBinder.queryLocalInterface(anyString()))
460                        .thenReturn(mRecommendationProvider);
461                invocation.getArgumentAt(1, ServiceConnection.class)
462                        .onServiceConnected(componentName, mockBinder);
463                return true;
464            }
465        });
466        mNetworkScoreService.systemRunning();
467    }
468}
469