NetworkScoreServiceTest.java revision 36d4c48496cc35c5d4f07e1793cedf4249d85826
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        mRecommendationRequest = new RecommendationRequest.Builder()
126            .setCurrentRecommendedWifiConfig(configuration).build();
127    }
128
129    @Test
130    public void testSystemRunning() {
131        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
132
133        mNetworkScoreService.systemRunning();
134
135        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
136                new ComponentName(NEW_SCORER.packageName,
137                    NEW_SCORER.recommendationServiceClassName))),
138                any(ServiceConnection.class),
139                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
140                eq(UserHandle.SYSTEM));
141    }
142
143    @Test
144    public void testRequestScores_noPermission() throws Exception {
145        doThrow(new SecurityException()).when(mContext)
146            .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
147                anyString());
148        try {
149            mNetworkScoreService.requestScores(null);
150            fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
151        } catch (SecurityException e) {
152            // expected
153        }
154    }
155
156    @Test
157    public void testRequestScores_providerNotConnected() throws Exception {
158        assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
159        verifyZeroInteractions(mRecommendationProvider);
160    }
161
162    @Test
163    public void testRequestScores_providerThrowsRemoteException() throws Exception {
164        injectProvider();
165        doThrow(new RemoteException()).when(mRecommendationProvider)
166            .requestScores(any(NetworkKey[].class));
167
168        assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
169    }
170
171    @Test
172    public void testRequestScores_providerAvailable() throws Exception {
173        injectProvider();
174
175        final NetworkKey[] networks = new NetworkKey[0];
176        assertTrue(mNetworkScoreService.requestScores(networks));
177        verify(mRecommendationProvider).requestScores(networks);
178    }
179
180    @Test
181    public void testRequestRecommendation_noPermission() throws Exception {
182        doThrow(new SecurityException()).when(mContext)
183            .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
184                anyString());
185        try {
186            mNetworkScoreService.requestRecommendation(mRecommendationRequest);
187            fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
188        } catch (SecurityException e) {
189            // expected
190        }
191    }
192
193    @Test
194    public void testRequestRecommendation_mainThread() throws Exception {
195        when(mContext.getMainLooper()).thenReturn(Looper.myLooper());
196        try {
197            mNetworkScoreService.requestRecommendation(mRecommendationRequest);
198            fail("requestRecommendation run on main thread.");
199        } catch (RuntimeException e) {
200            // expected
201        }
202    }
203
204    @Test
205    public void testRequestRecommendation_providerNotConnected() throws Exception {
206        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
207
208        final RecommendationResult result =
209                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
210        assertNotNull(result);
211        assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
212                result.getWifiConfiguration());
213    }
214
215    @Test
216    public void testRequestRecommendation_providerThrowsRemoteException() throws Exception {
217        injectProvider();
218        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
219        doThrow(new RemoteException()).when(mRecommendationProvider)
220                .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
221                        anyInt());
222
223        final RecommendationResult result =
224                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
225        assertNotNull(result);
226        assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
227                result.getWifiConfiguration());
228    }
229
230    @Test
231    public void testRequestRecommendation_resultReturned() throws Exception {
232        injectProvider();
233        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
234        final WifiConfiguration wifiConfiguration = new WifiConfiguration();
235        wifiConfiguration.SSID = "testRequestRecommendation_resultReturned";
236        final RecommendationResult providerResult =
237                new RecommendationResult(wifiConfiguration);
238        final Bundle bundle = new Bundle();
239        bundle.putParcelable(EXTRA_RECOMMENDATION_RESULT, providerResult);
240        doAnswer(invocation -> {
241            bundle.putInt(EXTRA_SEQUENCE, invocation.getArgumentAt(2, int.class));
242            invocation.getArgumentAt(1, IRemoteCallback.class).sendResult(bundle);
243            return null;
244        }).when(mRecommendationProvider)
245                .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
246                        anyInt());
247
248        final RecommendationResult result =
249                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
250        assertNotNull(result);
251        assertEquals(providerResult.getWifiConfiguration().SSID,
252                result.getWifiConfiguration().SSID);
253    }
254
255    @Test
256    public void testUpdateScores_notActiveScorer() {
257        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
258
259        try {
260            mNetworkScoreService.updateScores(new ScoredNetwork[0]);
261            fail("SecurityException expected");
262        } catch (SecurityException e) {
263            // expected
264        }
265    }
266
267    @Test
268    public void testUpdateScores_oneRegisteredCache() throws RemoteException {
269        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
270
271        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
272                mNetworkScoreCache, CACHE_FILTER_NONE);
273
274        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
275
276        verify(mNetworkScoreCache).updateScores(mScoredNetworkCaptor.capture());
277
278        assertEquals(1, mScoredNetworkCaptor.getValue().size());
279        assertEquals(SCORED_NETWORK, mScoredNetworkCaptor.getValue().get(0));
280    }
281
282    @Test
283    public void testUpdateScores_twoRegisteredCaches() throws RemoteException {
284        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
285
286        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
287                mNetworkScoreCache, CACHE_FILTER_NONE);
288        mNetworkScoreService.registerNetworkScoreCache(
289                NetworkKey.TYPE_WIFI, mNetworkScoreCache2, CACHE_FILTER_NONE);
290
291        // updateScores should update both caches
292        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
293
294        verify(mNetworkScoreCache).updateScores(anyListOf(ScoredNetwork.class));
295        verify(mNetworkScoreCache2).updateScores(anyListOf(ScoredNetwork.class));
296
297        mNetworkScoreService.unregisterNetworkScoreCache(
298                NetworkKey.TYPE_WIFI, mNetworkScoreCache2);
299
300        // updateScores should only update the first cache since the 2nd has been unregistered
301        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
302
303        verify(mNetworkScoreCache, times(2)).updateScores(anyListOf(ScoredNetwork.class));
304
305        mNetworkScoreService.unregisterNetworkScoreCache(
306                NetworkKey.TYPE_WIFI, mNetworkScoreCache);
307
308        // updateScores should not update any caches since they are both unregistered
309        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
310
311        // The register and unregister calls grab the binder from the score cache.
312        verify(mNetworkScoreCache, atLeastOnce()).asBinder();
313        verify(mNetworkScoreCache2, atLeastOnce()).asBinder();
314        verifyNoMoreInteractions(mNetworkScoreCache, mNetworkScoreCache2);
315    }
316
317    @Test
318    public void testClearScores_notActiveScorer_noBroadcastNetworkPermission() {
319        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
320        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
321            .thenReturn(PackageManager.PERMISSION_DENIED);
322        try {
323            mNetworkScoreService.clearScores();
324            fail("SecurityException expected");
325        } catch (SecurityException e) {
326            // expected
327        }
328    }
329
330    @Test
331    public void testClearScores_activeScorer_noBroadcastNetworkPermission() {
332        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
333        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
334            .thenReturn(PackageManager.PERMISSION_DENIED);
335
336        mNetworkScoreService.clearScores();
337    }
338
339    @Test
340    public void testClearScores_activeScorer() throws RemoteException {
341        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
342
343        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
344                CACHE_FILTER_NONE);
345        mNetworkScoreService.clearScores();
346
347        verify(mNetworkScoreCache).clearScores();
348    }
349
350    @Test
351    public void testClearScores_notActiveScorer_hasBroadcastNetworkPermission()
352            throws RemoteException {
353        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
354        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
355                .thenReturn(PackageManager.PERMISSION_GRANTED);
356
357        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
358                CACHE_FILTER_NONE);
359        mNetworkScoreService.clearScores();
360
361        verify(mNetworkScoreCache).clearScores();
362    }
363
364    @Test
365    public void testSetActiveScorer_noScoreNetworksPermission() {
366        doThrow(new SecurityException()).when(mContext)
367                .enforceCallingOrSelfPermission(eq(permission.SCORE_NETWORKS), anyString());
368
369        try {
370            mNetworkScoreService.setActiveScorer(null);
371            fail("SecurityException expected");
372        } catch (SecurityException e) {
373            // expected
374        }
375    }
376
377    @Test
378    public void testDisableScoring_notActiveScorer_noBroadcastNetworkPermission() {
379        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
380        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
381                .thenReturn(PackageManager.PERMISSION_DENIED);
382
383        try {
384            mNetworkScoreService.disableScoring();
385            fail("SecurityException expected");
386        } catch (SecurityException e) {
387            // expected
388        }
389    }
390
391    @Test
392    public void testRegisterNetworkScoreCache_noBroadcastNetworkPermission() {
393        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
394                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
395
396        try {
397            mNetworkScoreService.registerNetworkScoreCache(
398                NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE);
399            fail("SecurityException expected");
400        } catch (SecurityException e) {
401            // expected
402        }
403    }
404
405    @Test
406    public void testUnregisterNetworkScoreCache_noBroadcastNetworkPermission() {
407        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
408                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
409
410        try {
411            mNetworkScoreService.unregisterNetworkScoreCache(
412                    NetworkKey.TYPE_WIFI, mNetworkScoreCache);
413            fail("SecurityException expected");
414        } catch (SecurityException e) {
415            // expected
416        }
417    }
418
419    @Test
420    public void testDump_noDumpPermission() {
421        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
422                eq(permission.DUMP), anyString());
423
424        try {
425            mNetworkScoreService.dump(
426                    new FileDescriptor(), new PrintWriter(new StringWriter()), new String[0]);
427            fail("SecurityException expected");
428        } catch (SecurityException e) {
429            // expected
430        }
431    }
432
433    @Test
434    public void testDump_doesNotCrash() {
435        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
436        StringWriter stringWriter = new StringWriter();
437
438        mNetworkScoreService.dump(
439                new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
440
441        assertFalse(stringWriter.toString().isEmpty());
442    }
443
444    // "injects" the mock INetworkRecommendationProvider into the NetworkScoreService.
445    private void injectProvider() {
446        final ComponentName componentName = new ComponentName(NEW_SCORER.packageName,
447                NEW_SCORER.recommendationServiceClassName);
448        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
449        when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
450                isA(UserHandle.class))).thenAnswer(new Answer<Boolean>() {
451            @Override
452            public Boolean answer(InvocationOnMock invocation) throws Throwable {
453                IBinder mockBinder = mock(IBinder.class);
454                when(mockBinder.queryLocalInterface(anyString()))
455                        .thenReturn(mRecommendationProvider);
456                invocation.getArgumentAt(1, ServiceConnection.class)
457                        .onServiceConnected(componentName, mockBinder);
458                return true;
459            }
460        });
461        mNetworkScoreService.systemRunning();
462    }
463}
464