Merge "Support multiple caches in NetworkScoreService."
This commit is contained in:
@@ -56,16 +56,25 @@ interface INetworkScoreService
|
||||
void disableScoring();
|
||||
|
||||
/**
|
||||
* Register a network subsystem for scoring.
|
||||
* Register a cache to receive scoring updates.
|
||||
*
|
||||
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
|
||||
* @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
|
||||
* @throws SecurityException if the caller is not the system.
|
||||
* @throws IllegalArgumentException if a score cache is already registed for this type.
|
||||
* @hide
|
||||
*/
|
||||
void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache);
|
||||
|
||||
/**
|
||||
* Unregister a cache to receive scoring updates.
|
||||
*
|
||||
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
|
||||
* @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
|
||||
* @throws SecurityException if the caller is not the system.
|
||||
* @hide
|
||||
*/
|
||||
void unregisterNetworkScoreCache(int networkType, INetworkScoreCache scoreCache);
|
||||
|
||||
/**
|
||||
* Request a recommendation for the best network to connect to
|
||||
* taking into account the inputs from the {@link RecommendationRequest}.
|
||||
|
||||
@@ -278,6 +278,24 @@ public class NetworkScoreManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a network score cache.
|
||||
*
|
||||
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
|
||||
* @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
|
||||
* @throws SecurityException if the caller does not hold the
|
||||
* {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
|
||||
* @throws IllegalArgumentException if a score cache is already registered for this type.
|
||||
* @hide
|
||||
*/
|
||||
public void unregisterNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
|
||||
try {
|
||||
mService.unregisterNetworkScoreCache(networkType, scoreCache);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a recommendation for which network to connect to.
|
||||
*
|
||||
|
||||
@@ -36,10 +36,12 @@ import android.net.ScoredNetwork;
|
||||
import android.net.wifi.WifiConfiguration;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.R;
|
||||
@@ -52,11 +54,11 @@ import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Backing service for {@link android.net.NetworkScoreManager}.
|
||||
@@ -68,7 +70,8 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
|
||||
|
||||
private final Context mContext;
|
||||
private final NetworkScorerAppManager mNetworkScorerAppManager;
|
||||
private final Map<Integer, INetworkScoreCache> mScoreCaches;
|
||||
@GuardedBy("mScoreCaches")
|
||||
private final Map<Integer, RemoteCallbackList<INetworkScoreCache>> mScoreCaches;
|
||||
/** Lock used to update mPackageMonitor when scorer package changes occur. */
|
||||
private final Object mPackageMonitorLock = new Object[0];
|
||||
|
||||
@@ -166,7 +169,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
|
||||
NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager) {
|
||||
mContext = context;
|
||||
mNetworkScorerAppManager = networkScoreAppManager;
|
||||
mScoreCaches = new HashMap<>();
|
||||
mScoreCaches = new ArrayMap<>();
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
|
||||
// TODO: Need to update when we support per-user scorers. http://b/23422763
|
||||
mContext.registerReceiverAsUser(
|
||||
@@ -276,7 +279,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
|
||||
}
|
||||
|
||||
// Separate networks by type.
|
||||
Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
|
||||
Map<Integer, List<ScoredNetwork>> networksByType = new ArrayMap<>();
|
||||
for (ScoredNetwork network : networks) {
|
||||
List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
|
||||
if (networkList == null) {
|
||||
@@ -287,19 +290,32 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
|
||||
}
|
||||
|
||||
// Pass the scores of each type down to the appropriate network scorer.
|
||||
for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
|
||||
INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
|
||||
if (scoreCache != null) {
|
||||
try {
|
||||
scoreCache.updateScores(entry.getValue());
|
||||
} catch (RemoteException e) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
|
||||
for (final Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
|
||||
final RemoteCallbackList<INetworkScoreCache> callbackList;
|
||||
final boolean isEmpty;
|
||||
synchronized (mScoreCaches) {
|
||||
callbackList = mScoreCaches.get(entry.getKey());
|
||||
isEmpty = callbackList == null || callbackList.getRegisteredCallbackCount() == 0;
|
||||
}
|
||||
if (isEmpty) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
sendCallback(new Consumer<INetworkScoreCache>() {
|
||||
@Override
|
||||
public void accept(INetworkScoreCache networkScoreCache) {
|
||||
try {
|
||||
networkScoreCache.updateScores(entry.getValue());
|
||||
} catch (RemoteException e) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
|
||||
}
|
||||
}, Collections.singleton(callbackList));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -394,28 +410,52 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
|
||||
|
||||
/** Clear scores. Callers are responsible for checking permissions as appropriate. */
|
||||
private void clearInternal() {
|
||||
Set<INetworkScoreCache> cachesToClear = getScoreCaches();
|
||||
|
||||
for (INetworkScoreCache scoreCache : cachesToClear) {
|
||||
try {
|
||||
scoreCache.clearScores();
|
||||
} catch (RemoteException e) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "Unable to clear scores", e);
|
||||
sendCallback(new Consumer<INetworkScoreCache>() {
|
||||
@Override
|
||||
public void accept(INetworkScoreCache networkScoreCache) {
|
||||
try {
|
||||
networkScoreCache.clearScores();
|
||||
} catch (RemoteException e) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "Unable to clear scores", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, getScoreCacheLists());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
|
||||
mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
|
||||
synchronized (mScoreCaches) {
|
||||
if (mScoreCaches.containsKey(networkType)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Score cache already registered for type " + networkType);
|
||||
RemoteCallbackList<INetworkScoreCache> callbackList = mScoreCaches.get(networkType);
|
||||
if (callbackList == null) {
|
||||
callbackList = new RemoteCallbackList<>();
|
||||
mScoreCaches.put(networkType, callbackList);
|
||||
}
|
||||
if (!callbackList.register(scoreCache)) {
|
||||
if (callbackList.getRegisteredCallbackCount() == 0) {
|
||||
mScoreCaches.remove(networkType);
|
||||
}
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "Unable to register NetworkScoreCache for type " + networkType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
|
||||
mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
|
||||
synchronized (mScoreCaches) {
|
||||
RemoteCallbackList<INetworkScoreCache> callbackList = mScoreCaches.get(networkType);
|
||||
if (callbackList == null || !callbackList.unregister(scoreCache)) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "Unable to unregister NetworkScoreCache for type " + networkType);
|
||||
}
|
||||
} else if (callbackList.getRegisteredCallbackCount() == 0) {
|
||||
mScoreCaches.remove(networkType);
|
||||
}
|
||||
mScoreCaches.put(networkType, scoreCache);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +470,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||
protected void dump(final FileDescriptor fd, final PrintWriter writer, final String[] args) {
|
||||
mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
|
||||
NetworkScorerAppData currentScorer = mNetworkScorerAppManager.getActiveScorer();
|
||||
if (currentScorer == null) {
|
||||
@@ -439,13 +479,17 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
|
||||
}
|
||||
writer.println("Current scorer: " + currentScorer.mPackageName);
|
||||
|
||||
for (INetworkScoreCache scoreCache : getScoreCaches()) {
|
||||
try {
|
||||
TransferPipe.dumpAsync(scoreCache.asBinder(), fd, args);
|
||||
} catch (IOException | RemoteException e) {
|
||||
writer.println("Failed to dump score cache: " + e);
|
||||
sendCallback(new Consumer<INetworkScoreCache>() {
|
||||
@Override
|
||||
public void accept(INetworkScoreCache networkScoreCache) {
|
||||
try {
|
||||
TransferPipe.dumpAsync(networkScoreCache.asBinder(), fd, args);
|
||||
} catch (IOException | RemoteException e) {
|
||||
writer.println("Failed to dump score cache: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, getScoreCacheLists());
|
||||
|
||||
if (mServiceConnection != null) {
|
||||
mServiceConnection.dump(fd, writer, args);
|
||||
} else {
|
||||
@@ -455,14 +499,30 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all score caches that are currently active.
|
||||
* Returns a {@link Collection} of all {@link RemoteCallbackList}s that are currently active.
|
||||
*
|
||||
* <p>May be used to perform an action on all score caches without potentially strange behavior
|
||||
* if a new scorer is registered during that action's execution.
|
||||
*/
|
||||
private Set<INetworkScoreCache> getScoreCaches() {
|
||||
private Collection<RemoteCallbackList<INetworkScoreCache>> getScoreCacheLists() {
|
||||
synchronized (mScoreCaches) {
|
||||
return new HashSet<>(mScoreCaches.values());
|
||||
return new ArrayList<>(mScoreCaches.values());
|
||||
}
|
||||
}
|
||||
|
||||
private void sendCallback(Consumer<INetworkScoreCache> consumer,
|
||||
Collection<RemoteCallbackList<INetworkScoreCache>> remoteCallbackLists) {
|
||||
for (RemoteCallbackList<INetworkScoreCache> callbackList : remoteCallbackLists) {
|
||||
synchronized (callbackList) { // Ensure only one active broadcast per RemoteCallbackList
|
||||
final int count = callbackList.beginBroadcast();
|
||||
try {
|
||||
for (int i = 0; i < count; i++) {
|
||||
consumer.accept(callbackList.getBroadcastItem(i));
|
||||
}
|
||||
} finally {
|
||||
callbackList.finishBroadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
* 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 junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
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.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.when;
|
||||
|
||||
import android.Manifest.permission;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.net.INetworkScoreCache;
|
||||
import android.net.NetworkKey;
|
||||
import android.net.NetworkScoreManager;
|
||||
import android.net.NetworkScorerAppManager;
|
||||
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
|
||||
import android.net.ScoredNetwork;
|
||||
import android.net.WifiKey;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.Global;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.MediumTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.server.devicepolicy.MockUtils;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests for {@link NetworkScoreService}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class NetworkScoreServiceTest {
|
||||
private static final ScoredNetwork SCORED_NETWORK =
|
||||
new ScoredNetwork(new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00")),
|
||||
null /* rssiCurve*/);
|
||||
private static final NetworkScorerAppData PREV_SCORER = new NetworkScorerAppData(
|
||||
"prevPackageName", 0, "prevScorerName", null /* configurationActivityClassName */,
|
||||
"prevScoringServiceClass");
|
||||
private static final NetworkScorerAppData NEW_SCORER = new NetworkScorerAppData(
|
||||
"newPackageName", 1, "newScorerName", null /* configurationActivityClassName */,
|
||||
"newScoringServiceClass");
|
||||
|
||||
@Mock private PackageManager mPackageManager;
|
||||
@Mock private NetworkScorerAppManager mNetworkScorerAppManager;
|
||||
@Mock private Context mContext;
|
||||
@Mock private Resources mResources;
|
||||
@Mock private INetworkScoreCache.Stub mNetworkScoreCache, mNetworkScoreCache2;
|
||||
@Mock private IBinder mIBinder, mIBinder2;
|
||||
@Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor;
|
||||
|
||||
private ContentResolver mContentResolver;
|
||||
private NetworkScoreService mNetworkScoreService;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mNetworkScoreCache.asBinder()).thenReturn(mIBinder);
|
||||
when(mNetworkScoreCache2.asBinder()).thenReturn(mIBinder2);
|
||||
mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
|
||||
when(mContext.getContentResolver()).thenReturn(mContentResolver);
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemReady_networkScorerProvisioned() throws Exception {
|
||||
Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 1);
|
||||
|
||||
mNetworkScoreService.systemReady();
|
||||
|
||||
verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemReady_networkScorerNotProvisioned_defaultScorer() throws Exception {
|
||||
Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 0);
|
||||
|
||||
when(mResources.getString(R.string.config_defaultNetworkScorerPackageName))
|
||||
.thenReturn(NEW_SCORER.mPackageName);
|
||||
|
||||
mNetworkScoreService.systemReady();
|
||||
|
||||
verify(mNetworkScorerAppManager).setActiveScorer(NEW_SCORER.mPackageName);
|
||||
assertEquals(1,
|
||||
Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemReady_networkScorerNotProvisioned_noDefaultScorer() throws Exception {
|
||||
Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 0);
|
||||
|
||||
when(mResources.getString(R.string.config_defaultNetworkScorerPackageName))
|
||||
.thenReturn(null);
|
||||
|
||||
mNetworkScoreService.systemReady();
|
||||
|
||||
verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
|
||||
assertEquals(1,
|
||||
Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemRunning() {
|
||||
when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
|
||||
|
||||
mNetworkScoreService.systemRunning();
|
||||
|
||||
verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
|
||||
new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
|
||||
any(ServiceConnection.class),
|
||||
eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
|
||||
eq(UserHandle.SYSTEM));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateScores_notActiveScorer() {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
|
||||
|
||||
try {
|
||||
mNetworkScoreService.updateScores(new ScoredNetwork[0]);
|
||||
fail("SecurityException expected");
|
||||
} catch (SecurityException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateScores_oneRegisteredCache() throws RemoteException {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
|
||||
|
||||
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
|
||||
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 {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
|
||||
|
||||
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
mNetworkScoreService.registerNetworkScoreCache(
|
||||
NetworkKey.TYPE_WIFI, mNetworkScoreCache2);
|
||||
|
||||
// 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});
|
||||
|
||||
verifyNoMoreInteractions(mNetworkScoreCache, mNetworkScoreCache2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearScores_notActiveScorer_noBroadcastNetworkPermission() {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
|
||||
when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
|
||||
.thenReturn(PackageManager.PERMISSION_DENIED);
|
||||
try {
|
||||
mNetworkScoreService.clearScores();
|
||||
fail("SecurityException expected");
|
||||
} catch (SecurityException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearScores_activeScorer_noBroadcastNetworkPermission() {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
|
||||
when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
|
||||
.thenReturn(PackageManager.PERMISSION_DENIED);
|
||||
|
||||
mNetworkScoreService.clearScores();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearScores_activeScorer() throws RemoteException {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
|
||||
|
||||
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
mNetworkScoreService.clearScores();
|
||||
|
||||
verify(mNetworkScoreCache).clearScores();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearScores_notActiveScorer_hasBroadcastNetworkPermission()
|
||||
throws RemoteException {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
|
||||
when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
|
||||
.thenReturn(PackageManager.PERMISSION_GRANTED);
|
||||
|
||||
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
mNetworkScoreService.clearScores();
|
||||
|
||||
verify(mNetworkScoreCache).clearScores();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetActiveScorer_noScoreNetworksPermission() {
|
||||
doThrow(new SecurityException()).when(mContext)
|
||||
.enforceCallingOrSelfPermission(eq(permission.SCORE_NETWORKS), anyString());
|
||||
|
||||
try {
|
||||
mNetworkScoreService.setActiveScorer(null);
|
||||
fail("SecurityException expected");
|
||||
} catch (SecurityException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetActiveScorer_failure() throws RemoteException {
|
||||
when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER);
|
||||
when(mNetworkScorerAppManager.setActiveScorer(NEW_SCORER.mPackageName)).thenReturn(false);
|
||||
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
|
||||
boolean success = mNetworkScoreService.setActiveScorer(NEW_SCORER.mPackageName);
|
||||
|
||||
assertFalse(success);
|
||||
verify(mNetworkScoreCache).clearScores();
|
||||
verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
|
||||
new ComponentName(PREV_SCORER.mPackageName, PREV_SCORER.mScoringServiceClassName))),
|
||||
any(ServiceConnection.class),
|
||||
eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
|
||||
eq(UserHandle.SYSTEM));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetActiveScorer_success() throws RemoteException {
|
||||
when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, NEW_SCORER);
|
||||
when(mNetworkScorerAppManager.setActiveScorer(NEW_SCORER.mPackageName)).thenReturn(true);
|
||||
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
|
||||
boolean success = mNetworkScoreService.setActiveScorer(NEW_SCORER.mPackageName);
|
||||
|
||||
assertTrue(success);
|
||||
verify(mNetworkScoreCache).clearScores();
|
||||
verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
|
||||
new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
|
||||
any(ServiceConnection.class),
|
||||
eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
|
||||
eq(UserHandle.SYSTEM));
|
||||
verify(mContext, times(2)).sendBroadcastAsUser(
|
||||
MockUtils.checkIntentAction(NetworkScoreManager.ACTION_SCORER_CHANGED),
|
||||
eq(UserHandle.SYSTEM));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisableScoring_notActiveScorer_noBroadcastNetworkPermission() {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
|
||||
when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
|
||||
.thenReturn(PackageManager.PERMISSION_DENIED);
|
||||
|
||||
try {
|
||||
mNetworkScoreService.disableScoring();
|
||||
fail("SecurityException expected");
|
||||
} catch (SecurityException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisableScoring_activeScorer() throws RemoteException {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
|
||||
when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, null);
|
||||
when(mNetworkScorerAppManager.setActiveScorer(null)).thenReturn(true);
|
||||
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
|
||||
mNetworkScoreService.disableScoring();
|
||||
|
||||
verify(mNetworkScoreCache).clearScores();
|
||||
verify(mContext).sendBroadcastAsUser(
|
||||
MockUtils.checkIntent(new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED)
|
||||
.setPackage(PREV_SCORER.mPackageName)),
|
||||
eq(UserHandle.SYSTEM));
|
||||
verify(mContext, never()).bindServiceAsUser(any(Intent.class),
|
||||
any(ServiceConnection.class), anyInt(), any(UserHandle.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisableScoring_notActiveScorer_hasBroadcastNetworkPermission()
|
||||
throws RemoteException {
|
||||
when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
|
||||
when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
|
||||
.thenReturn(PackageManager.PERMISSION_GRANTED);
|
||||
when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, null);
|
||||
when(mNetworkScorerAppManager.setActiveScorer(null)).thenReturn(true);
|
||||
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
|
||||
mNetworkScoreService.disableScoring();
|
||||
|
||||
verify(mNetworkScoreCache).clearScores();
|
||||
verify(mContext).sendBroadcastAsUser(
|
||||
MockUtils.checkIntent(new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED)
|
||||
.setPackage(PREV_SCORER.mPackageName)),
|
||||
eq(UserHandle.SYSTEM));
|
||||
verify(mContext, never()).bindServiceAsUser(any(Intent.class),
|
||||
any(ServiceConnection.class), anyInt(), any(UserHandle.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterNetworkScoreCache_noBroadcastNetworkPermission() {
|
||||
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
|
||||
eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
|
||||
|
||||
try {
|
||||
mNetworkScoreService.registerNetworkScoreCache(
|
||||
NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
fail("SecurityException expected");
|
||||
} catch (SecurityException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnregisterNetworkScoreCache_noBroadcastNetworkPermission() {
|
||||
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
|
||||
eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
|
||||
|
||||
try {
|
||||
mNetworkScoreService.unregisterNetworkScoreCache(
|
||||
NetworkKey.TYPE_WIFI, mNetworkScoreCache);
|
||||
fail("SecurityException expected");
|
||||
} catch (SecurityException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDump_noDumpPermission() {
|
||||
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
|
||||
eq(permission.DUMP), anyString());
|
||||
|
||||
try {
|
||||
mNetworkScoreService.dump(
|
||||
new FileDescriptor(), new PrintWriter(new StringWriter()), new String[0]);
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,21 @@ public class MockUtils {
|
||||
return Mockito.argThat(m);
|
||||
}
|
||||
|
||||
public static Intent checkIntent(final Intent intent) {
|
||||
final Matcher<Intent> m = new BaseMatcher<Intent>() {
|
||||
@Override
|
||||
public boolean matches(Object item) {
|
||||
if (item == null) return false;
|
||||
return intent.filterEquals((Intent) item);
|
||||
}
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText(intent.toString());
|
||||
}
|
||||
};
|
||||
return Mockito.argThat(m);
|
||||
}
|
||||
|
||||
public static Bundle checkUserRestrictions(String... keys) {
|
||||
final Bundle expected = DpmTestUtils.newRestrictions(Preconditions.checkNotNull(keys));
|
||||
final Matcher<Bundle> m = new BaseMatcher<Bundle>() {
|
||||
|
||||
Reference in New Issue
Block a user