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