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