NetworkScoreServiceTest.java revision f9d47b79b2a307da1a16a8e0c16b3f9847ff5414
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_SSID";
236        wifiConfiguration.BSSID = "testRequestRecommendation_resultReturned_BSSID";
237        final RecommendationResult providerResult = RecommendationResult
238                .createConnectRecommendation(wifiConfiguration);
239        final Bundle bundle = new Bundle();
240        bundle.putParcelable(EXTRA_RECOMMENDATION_RESULT, providerResult);
241        doAnswer(invocation -> {
242            bundle.putInt(EXTRA_SEQUENCE, invocation.getArgumentAt(2, int.class));
243            invocation.getArgumentAt(1, IRemoteCallback.class).sendResult(bundle);
244            return null;
245        }).when(mRecommendationProvider)
246                .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
247                        anyInt());
248
249        final RecommendationResult result =
250                mNetworkScoreService.requestRecommendation(mRecommendationRequest);
251        assertNotNull(result);
252        assertEquals(providerResult.getWifiConfiguration().SSID,
253                result.getWifiConfiguration().SSID);
254        assertEquals(providerResult.getWifiConfiguration().BSSID,
255                result.getWifiConfiguration().BSSID);
256    }
257
258    @Test
259    public void testUpdateScores_notActiveScorer() {
260        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
261
262        try {
263            mNetworkScoreService.updateScores(new ScoredNetwork[0]);
264            fail("SecurityException expected");
265        } catch (SecurityException e) {
266            // expected
267        }
268    }
269
270    @Test
271    public void testUpdateScores_oneRegisteredCache() throws RemoteException {
272        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
273
274        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
275                mNetworkScoreCache, CACHE_FILTER_NONE);
276
277        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
278
279        verify(mNetworkScoreCache).updateScores(mScoredNetworkCaptor.capture());
280
281        assertEquals(1, mScoredNetworkCaptor.getValue().size());
282        assertEquals(SCORED_NETWORK, mScoredNetworkCaptor.getValue().get(0));
283    }
284
285    @Test
286    public void testUpdateScores_twoRegisteredCaches() throws RemoteException {
287        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
288
289        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
290                mNetworkScoreCache, CACHE_FILTER_NONE);
291        mNetworkScoreService.registerNetworkScoreCache(
292                NetworkKey.TYPE_WIFI, mNetworkScoreCache2, CACHE_FILTER_NONE);
293
294        // updateScores should update both caches
295        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
296
297        verify(mNetworkScoreCache).updateScores(anyListOf(ScoredNetwork.class));
298        verify(mNetworkScoreCache2).updateScores(anyListOf(ScoredNetwork.class));
299
300        mNetworkScoreService.unregisterNetworkScoreCache(
301                NetworkKey.TYPE_WIFI, mNetworkScoreCache2);
302
303        // updateScores should only update the first cache since the 2nd has been unregistered
304        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
305
306        verify(mNetworkScoreCache, times(2)).updateScores(anyListOf(ScoredNetwork.class));
307
308        mNetworkScoreService.unregisterNetworkScoreCache(
309                NetworkKey.TYPE_WIFI, mNetworkScoreCache);
310
311        // updateScores should not update any caches since they are both unregistered
312        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
313
314        // The register and unregister calls grab the binder from the score cache.
315        verify(mNetworkScoreCache, atLeastOnce()).asBinder();
316        verify(mNetworkScoreCache2, atLeastOnce()).asBinder();
317        verifyNoMoreInteractions(mNetworkScoreCache, mNetworkScoreCache2);
318    }
319
320    @Test
321    public void testClearScores_notActiveScorer_noBroadcastNetworkPermission() {
322        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
323        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
324            .thenReturn(PackageManager.PERMISSION_DENIED);
325        try {
326            mNetworkScoreService.clearScores();
327            fail("SecurityException expected");
328        } catch (SecurityException e) {
329            // expected
330        }
331    }
332
333    @Test
334    public void testClearScores_activeScorer_noBroadcastNetworkPermission() {
335        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
336        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
337            .thenReturn(PackageManager.PERMISSION_DENIED);
338
339        mNetworkScoreService.clearScores();
340    }
341
342    @Test
343    public void testClearScores_activeScorer() throws RemoteException {
344        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
345
346        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
347                CACHE_FILTER_NONE);
348        mNetworkScoreService.clearScores();
349
350        verify(mNetworkScoreCache).clearScores();
351    }
352
353    @Test
354    public void testClearScores_notActiveScorer_hasBroadcastNetworkPermission()
355            throws RemoteException {
356        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
357        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
358                .thenReturn(PackageManager.PERMISSION_GRANTED);
359
360        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
361                CACHE_FILTER_NONE);
362        mNetworkScoreService.clearScores();
363
364        verify(mNetworkScoreCache).clearScores();
365    }
366
367    @Test
368    public void testSetActiveScorer_noScoreNetworksPermission() {
369        doThrow(new SecurityException()).when(mContext)
370                .enforceCallingOrSelfPermission(eq(permission.SCORE_NETWORKS), anyString());
371
372        try {
373            mNetworkScoreService.setActiveScorer(null);
374            fail("SecurityException expected");
375        } catch (SecurityException e) {
376            // expected
377        }
378    }
379
380    @Test
381    public void testDisableScoring_notActiveScorer_noBroadcastNetworkPermission() {
382        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
383        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
384                .thenReturn(PackageManager.PERMISSION_DENIED);
385
386        try {
387            mNetworkScoreService.disableScoring();
388            fail("SecurityException expected");
389        } catch (SecurityException e) {
390            // expected
391        }
392    }
393
394    @Test
395    public void testRegisterNetworkScoreCache_noBroadcastNetworkPermission() {
396        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
397                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
398
399        try {
400            mNetworkScoreService.registerNetworkScoreCache(
401                NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE);
402            fail("SecurityException expected");
403        } catch (SecurityException e) {
404            // expected
405        }
406    }
407
408    @Test
409    public void testUnregisterNetworkScoreCache_noBroadcastNetworkPermission() {
410        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
411                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
412
413        try {
414            mNetworkScoreService.unregisterNetworkScoreCache(
415                    NetworkKey.TYPE_WIFI, mNetworkScoreCache);
416            fail("SecurityException expected");
417        } catch (SecurityException e) {
418            // expected
419        }
420    }
421
422    @Test
423    public void testDump_noDumpPermission() {
424        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
425                eq(permission.DUMP), anyString());
426
427        try {
428            mNetworkScoreService.dump(
429                    new FileDescriptor(), new PrintWriter(new StringWriter()), new String[0]);
430            fail("SecurityException expected");
431        } catch (SecurityException e) {
432            // expected
433        }
434    }
435
436    @Test
437    public void testDump_doesNotCrash() {
438        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
439        StringWriter stringWriter = new StringWriter();
440
441        mNetworkScoreService.dump(
442                new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
443
444        assertFalse(stringWriter.toString().isEmpty());
445    }
446
447    // "injects" the mock INetworkRecommendationProvider into the NetworkScoreService.
448    private void injectProvider() {
449        final ComponentName componentName = new ComponentName(NEW_SCORER.packageName,
450                NEW_SCORER.recommendationServiceClassName);
451        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
452        when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
453                isA(UserHandle.class))).thenAnswer(new Answer<Boolean>() {
454            @Override
455            public Boolean answer(InvocationOnMock invocation) throws Throwable {
456                IBinder mockBinder = mock(IBinder.class);
457                when(mockBinder.queryLocalInterface(anyString()))
458                        .thenReturn(mRecommendationProvider);
459                invocation.getArgumentAt(1, ServiceConnection.class)
460                        .onServiceConnected(componentName, mockBinder);
461                return true;
462            }
463        });
464        mNetworkScoreService.systemRunning();
465    }
466}
467