/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server; import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT; import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE; import static android.net.NetworkScoreManager.CACHE_FILTER_NONE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyListOf; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.Manifest.permission; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.INetworkRecommendationProvider; import android.net.INetworkScoreCache; import android.net.NetworkKey; import android.net.NetworkScoreManager; import android.net.NetworkScorerAppData; import android.net.RecommendationRequest; import android.net.RecommendationResult; import android.net.ScoredNetwork; import android.net.Uri; import android.net.WifiKey; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiSsid; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; import com.android.server.devicepolicy.MockUtils; import com.google.android.collect.Lists; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.UnaryOperator; /** * Tests for {@link NetworkScoreService}. */ @RunWith(AndroidJUnit4.class) @MediumTest public class NetworkScoreServiceTest { private static final String SSID = "ssid"; private static final String SSID_2 = "ssid_2"; private static final String SSID_3 = "ssid_3"; private static final String INVALID_BSSID = "invalid_bssid"; private static final ComponentName RECOMMENDATION_SERVICE_COMP = new ComponentName("newPackageName", "newScoringServiceClass"); private static final String RECOMMENDATION_SERVICE_LABEL = "Test Recommendation Service"; private static final ComponentName USE_WIFI_ENABLE_ACTIVITY_COMP = new ComponentName("useWifiPackageName", "enableUseWifiActivityClass"); private static final ScoredNetwork SCORED_NETWORK = new ScoredNetwork(new NetworkKey(new WifiKey(quote(SSID), "00:00:00:00:00:00")), null /* rssiCurve*/); private static final ScoredNetwork SCORED_NETWORK_2 = new ScoredNetwork(new NetworkKey(new WifiKey(quote(SSID_2), "00:00:00:00:00:00")), null /* rssiCurve*/); private static final String NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID = "networkAvailableNotificationChannelId"; private static final NetworkScorerAppData NEW_SCORER = new NetworkScorerAppData( 1, RECOMMENDATION_SERVICE_COMP, RECOMMENDATION_SERVICE_LABEL, USE_WIFI_ENABLE_ACTIVITY_COMP, NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID); @Mock private NetworkScorerAppManager mNetworkScorerAppManager; @Mock private Context mContext; @Mock private Resources mResources; @Mock private INetworkScoreCache.Stub mNetworkScoreCache, mNetworkScoreCache2; @Mock private IBinder mScoreCacheIBinder, mScoreCacheIBinder2; @Mock private IBinder mServiceConnectionIBinder; @Mock private INetworkRecommendationProvider mRecommendationProvider; @Mock private UnaryOperator> mCurrentNetworkFilter; @Mock private UnaryOperator> mScanResultsFilter; @Mock private WifiInfo mWifiInfo; @Mock private NetworkScoreService.ScoringServiceConnection mServiceConnection; @Captor private ArgumentCaptor> mScoredNetworkCaptor; private ContentResolver mContentResolver; private NetworkScoreService mNetworkScoreService; private RecommendationRequest mRecommendationRequest; private RemoteCallback mRemoteCallback; private OnResultListener mOnResultListener; private HandlerThread mHandlerThread; private List mScanResults; private static String quote(String str) { return String.format("\"%s\"", str); } @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mNetworkScoreCache.asBinder()).thenReturn(mScoreCacheIBinder); when(mNetworkScoreCache2.asBinder()).thenReturn(mScoreCacheIBinder2); when(mServiceConnectionIBinder.queryLocalInterface(anyString())) .thenReturn(mRecommendationProvider); mContentResolver = InstrumentationRegistry.getContext().getContentResolver(); when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mContext.getResources()).thenReturn(mResources); when(mWifiInfo.getSSID()).thenReturn(SCORED_NETWORK.networkKey.wifiKey.ssid); when(mWifiInfo.getBSSID()).thenReturn(SCORED_NETWORK.networkKey.wifiKey.bssid); when(mServiceConnection.getAppData()).thenReturn(NEW_SCORER); when(mServiceConnection.getRecommendationProvider()).thenReturn(mRecommendationProvider); when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER); mHandlerThread = new HandlerThread("NetworkScoreServiceTest"); mHandlerThread.start(); mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager, networkScorerAppData -> mServiceConnection, mHandlerThread.getLooper()); WifiConfiguration configuration = new WifiConfiguration(); configuration.SSID = "NetworkScoreServiceTest_SSID"; configuration.BSSID = "NetworkScoreServiceTest_BSSID"; mRecommendationRequest = new RecommendationRequest.Builder() .setDefaultWifiConfig(configuration).build(); mOnResultListener = new OnResultListener(); mRemoteCallback = new RemoteCallback(mOnResultListener); Settings.Global.putLong(mContentResolver, Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, -1L); mNetworkScoreService.refreshRecommendationRequestTimeoutMs(); populateScanResults(); } private void populateScanResults() { mScanResults = new ArrayList<>(); mScanResults.add(createScanResult(SSID, SCORED_NETWORK.networkKey.wifiKey.bssid)); mScanResults.add(createScanResult(SSID_2, SCORED_NETWORK_2.networkKey.wifiKey.bssid)); mScanResults.add(createScanResult(SSID_3, "10:10:00:00:10:10")); } private ScanResult createScanResult(String ssid, String bssid) { ScanResult result = new ScanResult(); result.wifiSsid = WifiSsid.createFromAsciiEncoded(ssid); result.BSSID = bssid; return result; } @After public void tearDown() throws Exception { mHandlerThread.quitSafely(); } @Test public void testOnUserUnlocked() { when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER); mNetworkScoreService.onUserUnlocked(0); verify(mNetworkScorerAppManager).updateState(); verify(mNetworkScorerAppManager).migrateNetworkScorerAppSettingIfNeeded(); verify(mServiceConnection).bind(mContext); } @Test public void testRequestScores_noPermission() throws Exception { doThrow(new SecurityException()).when(mContext) .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES), anyString()); try { mNetworkScoreService.requestScores(null); fail("REQUEST_NETWORK_SCORES not enforced."); } catch (SecurityException e) { // expected } } @Test public void testRequestScores_providerNotConnected() throws Exception { assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0])); verifyZeroInteractions(mRecommendationProvider); } @Test public void testRequestScores_providerThrowsRemoteException() throws Exception { doThrow(new RemoteException()).when(mRecommendationProvider) .requestScores(any(NetworkKey[].class)); mNetworkScoreService.onUserUnlocked(0); assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0])); } @Test public void testRequestScores_providerAvailable() throws Exception { mNetworkScoreService.onUserUnlocked(0); final NetworkKey[] networks = new NetworkKey[0]; assertTrue(mNetworkScoreService.requestScores(networks)); verify(mRecommendationProvider).requestScores(networks); } @Test public void testRequestRecommendation_noPermission() throws Exception { doThrow(new SecurityException()).when(mContext) .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES), anyString()); try { mNetworkScoreService.requestRecommendation(mRecommendationRequest); fail("REQUEST_NETWORK_SCORES not enforced."); } catch (SecurityException e) { // expected } } @Test public void testRequestRecommendation_mainThread() throws Exception { when(mContext.getMainLooper()).thenReturn(Looper.myLooper()); try { mNetworkScoreService.requestRecommendation(mRecommendationRequest); fail("requestRecommendation run on main thread."); } catch (RuntimeException e) { // expected } } @Test public void testRequestRecommendation_providerNotConnected() throws Exception { when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); final RecommendationResult result = mNetworkScoreService.requestRecommendation(mRecommendationRequest); assertNotNull(result); assertEquals(mRecommendationRequest.getDefaultWifiConfig(), result.getWifiConfiguration()); } @Test public void testRequestRecommendation_providerThrowsRemoteException() throws Exception { when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); doThrow(new RemoteException()).when(mRecommendationProvider) .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class), anyInt()); mNetworkScoreService.onUserUnlocked(0); final RecommendationResult result = mNetworkScoreService.requestRecommendation(mRecommendationRequest); assertNotNull(result); assertEquals(mRecommendationRequest.getDefaultWifiConfig(), result.getWifiConfiguration()); } @Test public void testRequestRecommendation_resultReturned() throws Exception { when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); final WifiConfiguration wifiConfiguration = new WifiConfiguration(); wifiConfiguration.SSID = "testRequestRecommendation_resultReturned_SSID"; wifiConfiguration.BSSID = "testRequestRecommendation_resultReturned_BSSID"; final RecommendationResult providerResult = RecommendationResult .createConnectRecommendation(wifiConfiguration); final Bundle bundle = new Bundle(); bundle.putParcelable(EXTRA_RECOMMENDATION_RESULT, providerResult); doAnswer(invocation -> { bundle.putInt(EXTRA_SEQUENCE, invocation.getArgument(2)); invocation.getArgument(1).sendResult(bundle); return null; }).when(mRecommendationProvider) .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class), anyInt()); mNetworkScoreService.onUserUnlocked(0); final RecommendationResult result = mNetworkScoreService.requestRecommendation(mRecommendationRequest); assertNotNull(result); assertEquals(providerResult.getWifiConfiguration().SSID, result.getWifiConfiguration().SSID); assertEquals(providerResult.getWifiConfiguration().BSSID, result.getWifiConfiguration().BSSID); } @Test public void testRequestRecommendationAsync_noPermission() throws Exception { doThrow(new SecurityException()).when(mContext) .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES), anyString()); try { mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest, mRemoteCallback); fail("REQUEST_NETWORK_SCORES not enforced."); } catch (SecurityException e) { // expected } } @Test public void testRequestRecommendationAsync_providerNotConnected() throws Exception { mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest, mRemoteCallback); boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS); assertTrue(callbackRan); verifyZeroInteractions(mRecommendationProvider); } @Test public void testRequestRecommendationAsync_requestTimesOut() throws Exception { mNetworkScoreService.onUserUnlocked(0); Settings.Global.putLong(mContentResolver, Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, 1L); mNetworkScoreService.refreshRecommendationRequestTimeoutMs(); mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest, mRemoteCallback); boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS); assertTrue(callbackRan); verify(mRecommendationProvider).requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.Stub.class), anyInt()); assertTrue(mOnResultListener.receivedBundle.containsKey(EXTRA_RECOMMENDATION_RESULT)); RecommendationResult result = mOnResultListener.receivedBundle.getParcelable(EXTRA_RECOMMENDATION_RESULT); assertTrue(result.hasRecommendation()); assertEquals(mRecommendationRequest.getDefaultWifiConfig().SSID, result.getWifiConfiguration().SSID); } @Test public void testRequestRecommendationAsync_requestSucceeds() throws Exception { mNetworkScoreService.onUserUnlocked(0); final Bundle bundle = new Bundle(); doAnswer(invocation -> { invocation.getArgument(1).sendResult(bundle); return null; }).when(mRecommendationProvider) .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class), anyInt()); mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest, mRemoteCallback); boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS); assertTrue(callbackRan); // If it's not the same instance then something else ran the callback. assertSame(bundle, mOnResultListener.receivedBundle); } @Test public void testRequestRecommendationAsync_requestThrowsRemoteException() throws Exception { mNetworkScoreService.onUserUnlocked(0); doThrow(new RemoteException()).when(mRecommendationProvider) .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class), anyInt()); mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest, mRemoteCallback); boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS); assertTrue(callbackRan); } @Test public void dispatchingContentObserver_nullUri() throws Exception { NetworkScoreService.DispatchingContentObserver observer = new NetworkScoreService.DispatchingContentObserver(mContext, null /*handler*/); observer.onChange(false, null); // nothing to assert or verify but since we passed in a null handler we'd see a NPE // if it were interacted with. } @Test public void dispatchingContentObserver_dispatchUri() throws Exception { final CountDownHandler handler = new CountDownHandler(mHandlerThread.getLooper()); NetworkScoreService.DispatchingContentObserver observer = new NetworkScoreService.DispatchingContentObserver(mContext, handler); Uri uri = Uri.parse("content://settings/global/network_score_service_test"); int expectedWhat = 24; observer.observe(uri, expectedWhat); observer.onChange(false, uri); final boolean msgHandled = handler.latch.await(3, TimeUnit.SECONDS); assertTrue(msgHandled); assertEquals(expectedWhat, handler.receivedWhat); } @Test public void oneTimeCallback_multipleCallbacks() throws Exception { NetworkScoreService.OneTimeCallback callback = new NetworkScoreService.OneTimeCallback(mRemoteCallback); callback.sendResult(null); callback.sendResult(null); assertEquals(1, mOnResultListener.resultCount); } @Test public void testUpdateScores_notActiveScorer() { bindToScorer(false /*callerIsScorer*/); try { mNetworkScoreService.updateScores(new ScoredNetwork[0]); fail("SecurityException expected"); } catch (SecurityException e) { // expected } } @Test public void testUpdateScores_oneRegisteredCache() throws RemoteException { bindToScorer(true /*callerIsScorer*/); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE); mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK}); verify(mNetworkScoreCache).updateScores(mScoredNetworkCaptor.capture()); assertEquals(1, mScoredNetworkCaptor.getValue().size()); assertEquals(SCORED_NETWORK, mScoredNetworkCaptor.getValue().get(0)); } @Test public void testUpdateScores_twoRegisteredCaches() throws RemoteException { bindToScorer(true /*callerIsScorer*/); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE); mNetworkScoreService.registerNetworkScoreCache( NetworkKey.TYPE_WIFI, mNetworkScoreCache2, CACHE_FILTER_NONE); // updateScores should update both caches mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK}); verify(mNetworkScoreCache).updateScores(anyListOf(ScoredNetwork.class)); verify(mNetworkScoreCache2).updateScores(anyListOf(ScoredNetwork.class)); mNetworkScoreService.unregisterNetworkScoreCache( NetworkKey.TYPE_WIFI, mNetworkScoreCache2); // updateScores should only update the first cache since the 2nd has been unregistered mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK}); verify(mNetworkScoreCache, times(2)).updateScores(anyListOf(ScoredNetwork.class)); mNetworkScoreService.unregisterNetworkScoreCache( NetworkKey.TYPE_WIFI, mNetworkScoreCache); // updateScores should not update any caches since they are both unregistered mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK}); // The register and unregister calls grab the binder from the score cache. verify(mNetworkScoreCache, atLeastOnce()).asBinder(); verify(mNetworkScoreCache2, atLeastOnce()).asBinder(); verifyNoMoreInteractions(mNetworkScoreCache, mNetworkScoreCache2); } @Test public void testClearScores_notActiveScorer_noRequestNetworkScoresPermission() { bindToScorer(false /*callerIsScorer*/); when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); try { mNetworkScoreService.clearScores(); fail("SecurityException expected"); } catch (SecurityException e) { // expected } } @Test public void testClearScores_activeScorer_noRequestNetworkScoresPermission() { bindToScorer(true /*callerIsScorer*/); when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); mNetworkScoreService.clearScores(); } @Test public void testClearScores_activeScorer() throws RemoteException { bindToScorer(true /*callerIsScorer*/); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE); mNetworkScoreService.clearScores(); verify(mNetworkScoreCache).clearScores(); } @Test public void testClearScores_notActiveScorer_hasRequestNetworkScoresPermission() throws RemoteException { bindToScorer(false /*callerIsScorer*/); when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_GRANTED); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE); mNetworkScoreService.clearScores(); verify(mNetworkScoreCache).clearScores(); } @Test public void testSetActiveScorer_noRequestNetworkScoresPermission() { when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); try { mNetworkScoreService.setActiveScorer(null); fail("SecurityException expected"); } catch (SecurityException e) { // expected } } @Test public void testSetActiveScorer_requestNetworkScoresPermission() { when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_GRANTED); mNetworkScoreService.setActiveScorer(null); } @Test public void testDisableScoring_notActiveScorer_noRequestNetworkScoresPermission() { bindToScorer(false /*callerIsScorer*/); when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); try { mNetworkScoreService.disableScoring(); fail("SecurityException expected"); } catch (SecurityException e) { // expected } } @Test public void testDisableScoring_activeScorer_noRequestNetworkScoresPermission() { bindToScorer(true /*callerIsScorer*/); when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); mNetworkScoreService.disableScoring(); } @Test public void testGetAllValidScorer_noRequestNetworkScoresPermission() { when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); try { mNetworkScoreService.getAllValidScorers(); fail("SecurityException expected"); } catch (SecurityException e) { // expected } } @Test public void testGetAllValidScorer_requestNetworkScoresPermission() { when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_GRANTED); mNetworkScoreService.getAllValidScorers(); } @Test public void testRegisterNetworkScoreCache_noRequestNetworkScoresPermission() { doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( eq(permission.REQUEST_NETWORK_SCORES), anyString()); try { mNetworkScoreService.registerNetworkScoreCache( NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE); fail("SecurityException expected"); } catch (SecurityException e) { // expected } } @Test public void testUnregisterNetworkScoreCache_noRequestNetworkScoresPermission() { doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( eq(permission.REQUEST_NETWORK_SCORES), anyString()); try { mNetworkScoreService.unregisterNetworkScoreCache( NetworkKey.TYPE_WIFI, mNetworkScoreCache); fail("SecurityException expected"); } catch (SecurityException e) { // expected } } @Test public void testDump_doesNotCrash() { when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER); StringWriter stringWriter = new StringWriter(); mNetworkScoreService.dump( new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); assertFalse(stringWriter.toString().isEmpty()); } @Test public void testIsCallerActiveScorer_noBoundService() throws Exception { // No active scorer. when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(null); mNetworkScoreService.onUserUnlocked(0); mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid()); verify(mServiceConnection, never()).getAppData(); } @Test public void testIsCallerActiveScorer_boundServiceIsNotCaller() throws Exception { bindToScorer(false /*callerIsScorer*/); assertFalse(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid())); } @Test public void testIsCallerActiveScorer_boundServiceIsCaller() throws Exception { bindToScorer(true /*callerIsScorer*/); assertTrue(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid())); } @Test public void testGetActiveScorerPackage_notActive() throws Exception { // No active scorer. when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(null); mNetworkScoreService.onUserUnlocked(0); mNetworkScoreService.getActiveScorerPackage(); verify(mServiceConnection, never()).getPackageName(); } @Test public void testGetActiveScorerPackage_active() throws Exception { when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER); mNetworkScoreService.onUserUnlocked(0); mNetworkScoreService.getActiveScorerPackage(); verify(mServiceConnection).getPackageName(); } @Test public void testCacheUpdatingConsumer_nullFilter() throws Exception { List scoredNetworkList = Lists.newArrayList(SCORED_NETWORK); NetworkScoreService.FilteringCacheUpdatingConsumer consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext, new ArrayList<>(scoredNetworkList), NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter); consumer.accept(mNetworkScoreCache, null /*cookie*/); verify(mNetworkScoreCache).updateScores(scoredNetworkList); verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter); } @Test public void testCacheUpdatingConsumer_noneFilter() throws Exception { List scoredNetworkList = Lists.newArrayList(SCORED_NETWORK); NetworkScoreService.FilteringCacheUpdatingConsumer consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext, new ArrayList<>(scoredNetworkList), NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter); consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE); verify(mNetworkScoreCache).updateScores(scoredNetworkList); verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter); } @Test public void testCacheUpdatingConsumer_unknownFilter() throws Exception { List scoredNetworkList = Lists.newArrayList(SCORED_NETWORK); NetworkScoreService.FilteringCacheUpdatingConsumer consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext, new ArrayList<>(scoredNetworkList), NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter); consumer.accept(mNetworkScoreCache, -1 /*cookie*/); verify(mNetworkScoreCache).updateScores(scoredNetworkList); verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter); } @Test public void testCacheUpdatingConsumer_nonIntFilter() throws Exception { List scoredNetworkList = Lists.newArrayList(SCORED_NETWORK); NetworkScoreService.FilteringCacheUpdatingConsumer consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext, new ArrayList<>(scoredNetworkList), NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter); consumer.accept(mNetworkScoreCache, "not an int" /*cookie*/); verify(mNetworkScoreCache).updateScores(scoredNetworkList); verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter); } @Test public void testCacheUpdatingConsumer_emptyScoreList() throws Exception { NetworkScoreService.FilteringCacheUpdatingConsumer consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext, Collections.emptyList(), NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter); consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE); verifyZeroInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter); } @Test public void testCacheUpdatingConsumer_currentNetworkFilter() throws Exception { List scoredNetworkList = Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2); NetworkScoreService.FilteringCacheUpdatingConsumer consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext, new ArrayList<>(scoredNetworkList), NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter); List filteredList = new ArrayList<>(scoredNetworkList); filteredList.remove(SCORED_NETWORK); when(mCurrentNetworkFilter.apply(scoredNetworkList)).thenReturn(filteredList); consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK); verify(mNetworkScoreCache).updateScores(filteredList); verifyZeroInteractions(mScanResultsFilter); } @Test public void testCacheUpdatingConsumer_scanResultsFilter() throws Exception { List scoredNetworkList = Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2); NetworkScoreService.FilteringCacheUpdatingConsumer consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext, new ArrayList<>(scoredNetworkList), NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter); List filteredList = new ArrayList<>(scoredNetworkList); filteredList.remove(SCORED_NETWORK); when(mScanResultsFilter.apply(scoredNetworkList)).thenReturn(filteredList); consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS); verify(mNetworkScoreCache).updateScores(filteredList); verifyZeroInteractions(mCurrentNetworkFilter); } @Test public void testCurrentNetworkScoreCacheFilter_nullWifiInfo() throws Exception { NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter = new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> null /*WifiInfo*/); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2)); assertTrue(actualList.isEmpty()); } @Test public void testCurrentNetworkScoreCacheFilter_invalidWifiInfo_nullSsid() throws Exception { when(mWifiInfo.getSSID()).thenReturn(null); NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter = new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> mWifiInfo); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2)); assertTrue(actualList.isEmpty()); } @Test public void testCurrentNetworkScoreCacheFilter_invalidWifiInfo_noneSsid() throws Exception { when(mWifiInfo.getSSID()).thenReturn(WifiSsid.NONE); NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter = new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> mWifiInfo); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2)); assertTrue(actualList.isEmpty()); } @Test public void testCurrentNetworkScoreCacheFilter_invalidWifiInfo_emptySsid() throws Exception { when(mWifiInfo.getSSID()).thenReturn(""); NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter = new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> mWifiInfo); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2)); assertTrue(actualList.isEmpty()); } @Test public void testCurrentNetworkScoreCacheFilter_invalidWifiInfo_invalidBssid() throws Exception { when(mWifiInfo.getBSSID()).thenReturn(INVALID_BSSID); NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter = new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> mWifiInfo); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2)); assertTrue(actualList.isEmpty()); } @Test public void testCurrentNetworkScoreCacheFilter_scoreFiltered() throws Exception { NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter = new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> mWifiInfo); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2)); List expectedList = Collections.singletonList(SCORED_NETWORK); assertEquals(expectedList, actualList); } @Test public void testCurrentNetworkScoreCacheFilter_currentNetworkNotInList() throws Exception { when(mWifiInfo.getSSID()).thenReturn("\"notInList\""); NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter = new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> mWifiInfo); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2)); assertTrue(actualList.isEmpty()); } @Test public void testScanResultsScoreCacheFilter_emptyScanResults() throws Exception { NetworkScoreService.ScanResultsScoreCacheFilter cacheFilter = new NetworkScoreService.ScanResultsScoreCacheFilter(Collections::emptyList); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2)); assertTrue(actualList.isEmpty()); } @Test public void testScanResultsScoreCacheFilter_invalidScanResults() throws Exception { List invalidScanResults = Lists.newArrayList( new ScanResult(), createScanResult("", SCORED_NETWORK.networkKey.wifiKey.bssid), createScanResult(WifiSsid.NONE, SCORED_NETWORK.networkKey.wifiKey.bssid), createScanResult(SSID, null), createScanResult(SSID, INVALID_BSSID) ); NetworkScoreService.ScanResultsScoreCacheFilter cacheFilter = new NetworkScoreService.ScanResultsScoreCacheFilter(() -> invalidScanResults); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2)); assertTrue(actualList.isEmpty()); } @Test public void testScanResultsScoreCacheFilter_scoresFiltered() throws Exception { NetworkScoreService.ScanResultsScoreCacheFilter cacheFilter = new NetworkScoreService.ScanResultsScoreCacheFilter(() -> mScanResults); ScoredNetwork unmatchedScore = new ScoredNetwork(new NetworkKey(new WifiKey(quote("newSsid"), "00:00:00:00:00:00")), null /* rssiCurve*/); List actualList = cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2, unmatchedScore)); List expectedList = Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2); assertEquals(expectedList, actualList); } @Test public void testGetActiveScorer_notConnected_canRequestScores() throws Exception { when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_GRANTED); when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(null); mNetworkScoreService.onUserUnlocked(0); mNetworkScoreService.getActiveScorer(); verify(mServiceConnection, never()).getAppData(); } @Test public void testGetActiveScorer_notConnected_canNotRequestScores() throws Exception { when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); try { mNetworkScoreService.getActiveScorer(); fail("SecurityException expected."); } catch (SecurityException e) { // expected } } @Test public void testGetActiveScorer_connected_canRequestScores() throws Exception { when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_GRANTED); mNetworkScoreService.onUserUnlocked(0); mNetworkScoreService.getActiveScorer(); verify(mServiceConnection).getAppData(); } @Test public void testGetActiveScorer_connected_canNotRequestScores() throws Exception { when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); bindToScorer(false); try { mNetworkScoreService.getActiveScorer(); fail("SecurityException expected."); } catch (SecurityException e) { // expected } } @Test public void testServiceConnection_bind_notBound() throws Exception { NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService .ScoringServiceConnection(NEW_SCORER); connection.bind(mContext); verify(mContext).bindServiceAsUser(MockUtils.checkIntent( new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS) .setComponent(RECOMMENDATION_SERVICE_COMP)), eq(connection), eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE), eq(UserHandle.SYSTEM)); } @Test public void testServiceConnection_bind_alreadyBound() throws Exception { NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService .ScoringServiceConnection(NEW_SCORER); when(mContext.bindServiceAsUser(MockUtils.checkIntent( new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS) .setComponent(RECOMMENDATION_SERVICE_COMP)), eq(connection), eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE), eq(UserHandle.SYSTEM))).thenReturn(true /*bound*/); // Calling bind more than once should only result in 1 bindService call. connection.bind(mContext); connection.bind(mContext); verify(mContext, times(1)).bindServiceAsUser(MockUtils.checkIntent( new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS) .setComponent(RECOMMENDATION_SERVICE_COMP)), eq(connection), eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE), eq(UserHandle.SYSTEM)); } @Test public void testServiceConnection_bindFails() throws Exception { NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService .ScoringServiceConnection(NEW_SCORER); when(mContext.bindServiceAsUser(MockUtils.checkIntent( new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS) .setComponent(RECOMMENDATION_SERVICE_COMP)), eq(connection), eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE), eq(UserHandle.SYSTEM))).thenReturn(false /*bound*/); connection.bind(mContext); verify(mContext).unbindService(connection); } @Test public void testServiceConnection_unbind_notBound() throws Exception { NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService .ScoringServiceConnection(NEW_SCORER); connection.unbind(mContext); verify(mContext, never()).unbindService(connection); } @Test public void testServiceConnection_unbind_alreadyBound() throws Exception { NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService .ScoringServiceConnection(NEW_SCORER); when(mContext.bindServiceAsUser(MockUtils.checkIntent( new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS) .setComponent(RECOMMENDATION_SERVICE_COMP)), eq(connection), eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE), eq(UserHandle.SYSTEM))).thenReturn(true /*bound*/); connection.bind(mContext); connection.unbind(mContext); verify(mContext).unbindService(connection); } @Test public void testServiceConnection_dump_doesNotCrash() throws Exception { NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService .ScoringServiceConnection(NEW_SCORER); final StringWriter stringWriter = new StringWriter(); connection.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); assertFalse(stringWriter.toString().isEmpty()); } private void bindToScorer(boolean callerIsScorer) { final int callingUid = callerIsScorer ? Binder.getCallingUid() : Binder.getCallingUid() + 1; NetworkScorerAppData appData = new NetworkScorerAppData(callingUid, RECOMMENDATION_SERVICE_COMP, RECOMMENDATION_SERVICE_LABEL, USE_WIFI_ENABLE_ACTIVITY_COMP, NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID); when(mServiceConnection.getAppData()).thenReturn(appData); mNetworkScoreService.onUserUnlocked(0); } private static class OnResultListener implements RemoteCallback.OnResultListener { private final CountDownLatch countDownLatch = new CountDownLatch(1); private int resultCount; private Bundle receivedBundle; @Override public void onResult(Bundle result) { countDownLatch.countDown(); resultCount++; receivedBundle = result; } } private static class CountDownHandler extends Handler { CountDownLatch latch = new CountDownLatch(1); int receivedWhat; CountDownHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { latch.countDown(); receivedWhat = msg.what; } } }