Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa4f08ec authored by Jeremy Joslin's avatar Jeremy Joslin
Browse files

Implement the discovery of a network recommendation provider.

Updated the NetworkScorerAppManager to examine the list of configured
network recommendation providers and to select the first valid
provider.

As part of this update the old logic of looking for a valid network
scorer has been removed. Scorers/recommendation providers are only
selected from the configured list now. The setActiveScorer() method
has been deprecated as a result.

The NetworkScoreService has been updated to monitor the list of
potential recommendation providers and to reevaluate the binding
whenever they change. It also monitors the new setting for
NETWORK_RECOMMENDATIONS_ENABLED to connect or disconnect from the
provider as needed.

Test: runtest frameworks-services -c com.android.server.NetworkScoreServiceTest
BUG: 33158362
Change-Id: I42aeb5223da794f71f7e58cb1bdf18817200cbf2
parent 96d5f472
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -183,7 +183,7 @@ public class NetworkScoreManager {
        if (app == null) {
            return null;
        }
        return app.mPackageName;
        return app.packageName;
    }

    /**
+127 −134
Original line number Diff line number Diff line
@@ -19,160 +19,176 @@ package android.net;
import android.Manifest;
import android.Manifest.permission;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;

import com.android.internal.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * Internal class for managing the primary network scorer application.
 *
 * TODO: Rename this to something more generic.
 * Internal class for discovering and managing the network scorer/recommendation application.
 *
 * @hide
 */
public class NetworkScorerAppManager {
    private static final String TAG = "NetworkScorerAppManager";

    private static final Intent SCORE_INTENT =
            new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);

    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
    private final Context mContext;

    public NetworkScorerAppManager(Context context) {
      mContext = context;
    }

    /**
     * Holds metadata about a discovered network scorer/recommendation application.
     */
    public static class NetworkScorerAppData {
        /** Package name of this scorer app. */
        public final String mPackageName;
        public final String packageName;

        /** UID of the scorer app. */
        public final int mPackageUid;

        /** Name of this scorer app for display. */
        public final CharSequence mScorerName;

        /**
         * Optional class name of a configuration activity. Null if none is set.
         *
         * @see NetworkScoreManager#ACTION_CUSTOM_ENABLE
         */
        public final String mConfigurationActivityClassName;
        public final int packageUid;

        /**
         * Optional class name of the scoring service we can bind to. Null if none is set.
         * Name of the recommendation service we can bind to.
         */
        public final String mScoringServiceClassName;
        public final String recommendationServiceClassName;

        public NetworkScorerAppData(String packageName, int packageUid, CharSequence scorerName,
                @Nullable String configurationActivityClassName,
                @Nullable String scoringServiceClassName) {
            mScorerName = scorerName;
            mPackageName = packageName;
            mPackageUid = packageUid;
            mConfigurationActivityClassName = configurationActivityClassName;
            mScoringServiceClassName = scoringServiceClassName;
        public NetworkScorerAppData(String packageName, int packageUid,
                String recommendationServiceClassName) {
            this.packageName = packageName;
            this.packageUid = packageUid;
            this.recommendationServiceClassName = recommendationServiceClassName;
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("NetworkScorerAppData{");
            sb.append("mPackageName='").append(mPackageName).append('\'');
            sb.append(", mPackageUid=").append(mPackageUid);
            sb.append(", mScorerName=").append(mScorerName);
            sb.append(", mConfigurationActivityClassName='").append(mConfigurationActivityClassName)
                    .append('\'');
            sb.append(", mScoringServiceClassName='").append(mScoringServiceClassName).append('\'');
            sb.append("mPackageName='").append(packageName).append('\'');
            sb.append(", packageUid=").append(packageUid);
            sb.append(", recommendationServiceClassName='")
                    .append(recommendationServiceClassName).append('\'');
            sb.append('}');
            return sb.toString();
        }
    }

    /**
     * Returns the list of available scorer apps.
     * @return A {@link NetworkScorerAppData} instance containing information about the
     *         best configured network recommendation provider installed or {@code null}
     *         if none of the configured packages can recommend networks.
     *
     * <p>A network scorer is any application which:
     * <p>A network recommendation provider is any application which:
     * <ul>
     * <li>Is listed in the <code>config_networkRecommendationPackageNames</code> config.
     * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
     * <li>Includes a receiver for {@link NetworkScoreManager#ACTION_SCORE_NETWORKS} guarded by the
     *     {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
     * <li>Includes a Service for {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS}.
     * </ul>
     *
     * @return the list of scorers, or the empty list if there are no valid scorers.
     */
    public Collection<NetworkScorerAppData> getAllValidScorers() {
        // Network scorer apps can only run as the primary user so exit early if we're not the
        // primary user.
    public NetworkScorerAppData getNetworkRecommendationProviderData() {
        // Network recommendation apps can only run as the primary user right now.
        // http://b/23422763
        if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
            return null;
        }

        final List<String> potentialPkgs = getPotentialRecommendationProviderPackages();
        if (potentialPkgs.isEmpty()) {
            if (DEBUG) {
                Log.d(TAG, "No Network Recommendation Providers specified.");
            }
            return null;
        }

        final PackageManager pm = mContext.getPackageManager();
        for (int i = 0; i < potentialPkgs.size(); i++) {
            final String potentialPkg = potentialPkgs.get(i);

            // Look for the recommendation service class and required receiver.
            final ResolveInfo resolveServiceInfo = findRecommendationService(potentialPkg);
            if (resolveServiceInfo != null) {
                return new NetworkScorerAppData(potentialPkg,
                    resolveServiceInfo.serviceInfo.applicationInfo.uid,
                    resolveServiceInfo.serviceInfo.name);
            } else {
                if (DEBUG) {
                    Log.d(TAG, potentialPkg + " does not have the required components, skipping.");
                }
            }
        }

        // None of the configured packages are valid.
        return null;
    }

    /**
     * @return A priority order list of package names that have been granted the
     *         permission needed for them to act as a network recommendation provider.
     *         The packages in the returned list may not contain the other required
     *         network recommendation provider components so additional checks are required
     *         before making a package the network recommendation provider.
     */
    public List<String> getPotentialRecommendationProviderPackages() {
        final String[] packageArray = mContext.getResources().getStringArray(
                R.array.config_networkRecommendationPackageNames);
        if (packageArray == null || packageArray.length == 0) {
            if (DEBUG) {
                Log.d(TAG, "No Network Recommendation Providers specified.");
            }
            return Collections.emptyList();
        }

        List<NetworkScorerAppData> scorers = new ArrayList<>();
        PackageManager pm = mContext.getPackageManager();
        // Only apps installed under the primary user of the device can be scorers.
        // TODO: http://b/23422763
        List<ResolveInfo> receivers =
                pm.queryBroadcastReceiversAsUser(SCORE_INTENT, 0 /* flags */, UserHandle.USER_SYSTEM);
        for (ResolveInfo receiver : receivers) {
            // This field is a misnomer, see android.content.pm.ResolveInfo#activityInfo
            final ActivityInfo receiverInfo = receiver.activityInfo;
            if (receiverInfo == null) {
                // Should never happen with queryBroadcastReceivers, but invalid nonetheless.
                continue;
            }
            if (!permission.BROADCAST_NETWORK_PRIVILEGED.equals(receiverInfo.permission)) {
                // Receiver doesn't require the BROADCAST_NETWORK_PRIVILEGED permission, which
                // means anyone could trigger network scoring and flood the framework with score
                // requests.
                continue;
            }
            if (pm.checkPermission(permission.SCORE_NETWORKS, receiverInfo.packageName) !=
                    PackageManager.PERMISSION_GRANTED) {
                // Application doesn't hold the SCORE_NETWORKS permission, so the user never
                // approved it as a network scorer.
                continue;
            }

            // Optionally, this package may specify a configuration activity.
            String configurationActivityClassName = null;
            Intent intent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE);
            intent.setPackage(receiverInfo.packageName);
            List<ResolveInfo> configActivities = pm.queryIntentActivities(intent, 0 /* flags */);
            if (configActivities != null && !configActivities.isEmpty()) {
                ActivityInfo activityInfo = configActivities.get(0).activityInfo;
                if (activityInfo != null) {
                    configurationActivityClassName = activityInfo.name;
                }
            }

            // Find the scoring service class we can bind to, if any.
            String scoringServiceClassName = null;
            Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
            serviceIntent.setPackage(receiverInfo.packageName);
            ResolveInfo resolveServiceInfo = pm.resolveService(serviceIntent, 0 /* flags */);
            if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) {
                scoringServiceClassName = resolveServiceInfo.serviceInfo.name;
        if (VERBOSE) {
            Log.d(TAG, "Configured packages: " + TextUtils.join(", ", packageArray));
        }

        List<String> packages = new ArrayList<>();
        final PackageManager pm = mContext.getPackageManager();
        for (String potentialPkg : packageArray) {
            if (pm.checkPermission(permission.SCORE_NETWORKS, potentialPkg)
                    == PackageManager.PERMISSION_GRANTED) {
                packages.add(potentialPkg);
            } else {
                if (DEBUG) {
                    Log.d(TAG, potentialPkg + " has not been granted " + permission.SCORE_NETWORKS
                            + ", skipping.");
                }
            }
        }

            // NOTE: loadLabel will attempt to load the receiver's label and fall back to the
            // app label if none is present.
            scorers.add(new NetworkScorerAppData(receiverInfo.packageName,
                    receiverInfo.applicationInfo.uid, receiverInfo.loadLabel(pm),
                    configurationActivityClassName, scoringServiceClassName));
        return packages;
    }

        return scorers;
    private ResolveInfo findRecommendationService(String packageName) {
        final PackageManager pm = mContext.getPackageManager();
        final int resolveFlags = 0;

        final Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
        serviceIntent.setPackage(packageName);
        final ResolveInfo resolveServiceInfo =
                pm.resolveService(serviceIntent, resolveFlags);

        if (VERBOSE) {
            Log.d(TAG, "Resolved " + serviceIntent + " to " + resolveServiceInfo);
        }

        if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) {
            return resolveServiceInfo;
        }

        if (VERBOSE) {
            Log.v(TAG, packageName + " does not have a service for " + serviceIntent);
        }
        return null;
    }

    /**
@@ -182,10 +198,15 @@ public class NetworkScorerAppManager {
     *     selected) or if the previously-set scorer is no longer a valid scorer app (e.g. because
     *     it was disabled or uninstalled).
     */
    @Nullable
    public NetworkScorerAppData getActiveScorer() {
        String scorerPackage = Settings.Global.getString(mContext.getContentResolver(),
                Settings.Global.NETWORK_SCORER_APP);
        return getScorer(scorerPackage);
        if (isNetworkRecommendationsDisabled()) {
            // If recommendations are disabled then there can't be an active scorer.
            return null;
        }

        // Otherwise return the recommendation provider (which may be null).
        return getNetworkRecommendationProviderData();
    }

    /**
@@ -195,34 +216,14 @@ public class NetworkScorerAppManager {
     *
     * @param packageName the packageName of the new scorer to use. If null, scoring will be
     *     disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
     * @return true if the scorer was changed, or false if the package is not a valid scorer.
     * @return true if the scorer was changed, or false if the package is not a valid scorer or
     *         a valid network recommendation provider exists.
     * @deprecated Scorers are now selected from a configured list.
     */
    @Deprecated
    public boolean setActiveScorer(String packageName) {
        String oldPackageName = Settings.Global.getString(mContext.getContentResolver(),
                Settings.Global.NETWORK_SCORER_APP);
        if (TextUtils.equals(oldPackageName, packageName)) {
            // No change.
            return true;
        }

        Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);

        if (packageName == null) {
            Settings.Global.putString(mContext.getContentResolver(),
                    Settings.Global.NETWORK_SCORER_APP, null);
            return true;
        } else {
            // We only make the change if the new package is valid.
            if (getScorer(packageName) != null) {
                Settings.Global.putString(mContext.getContentResolver(),
                        Settings.Global.NETWORK_SCORER_APP, packageName);
                return true;
            } else {
                Log.w(TAG, "Requested network scorer is not valid: " + packageName);
        return false;
    }
        }
    }

    /** Determine whether the application with the given UID is the enabled scorer. */
    public boolean isCallerActiveScorer(int callingUid) {
@@ -230,7 +231,7 @@ public class NetworkScorerAppManager {
        if (defaultApp == null) {
            return false;
        }
        if (callingUid != defaultApp.mPackageUid) {
        if (callingUid != defaultApp.packageUid) {
            return false;
        }
        // To be extra safe, ensure the caller holds the SCORE_NETWORKS permission. It always
@@ -239,17 +240,9 @@ public class NetworkScorerAppManager {
                PackageManager.PERMISSION_GRANTED;
    }

    /** Returns the {@link NetworkScorerAppData} for the given app, or null if it's not a scorer. */
    public NetworkScorerAppData getScorer(String packageName) {
        if (TextUtils.isEmpty(packageName)) {
            return null;
        }
        Collection<NetworkScorerAppData> applications = getAllValidScorers();
        for (NetworkScorerAppData app : applications) {
            if (packageName.equals(app.mPackageName)) {
                return app;
            }
        }
        return null;
    private boolean isNetworkRecommendationsDisabled() {
        final ContentResolver cr = mContext.getContentResolver();
        // A value of 1 indicates enabled.
        return Settings.Global.getInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) != 1;
    }
}
+151 −143

File changed.

Preview size limit exceeded, changes collapsed.

+61 −84

File changed.

Preview size limit exceeded, changes collapsed.

+4 −137
Original line number Diff line number Diff line
@@ -17,12 +17,9 @@
package com.android.server;

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.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;
@@ -30,7 +27,6 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
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;
@@ -46,7 +42,6 @@ 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;
@@ -54,15 +49,10 @@ 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;
@@ -73,7 +63,6 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

/**
@@ -85,12 +74,8 @@ 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");
    private static final NetworkScorerAppData NEW_SCORER =
        new NetworkScorerAppData("newPackageName", 1, "newScoringServiceClass");

    @Mock private PackageManager mPackageManager;
    @Mock private NetworkScorerAppManager mNetworkScorerAppManager;
@@ -114,44 +99,6 @@ public class NetworkScoreServiceTest {
        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);
@@ -159,7 +106,8 @@ public class NetworkScoreServiceTest {
        mNetworkScoreService.systemRunning();

        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
                new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
                new ComponentName(NEW_SCORER.packageName,
                    NEW_SCORER.recommendationServiceClassName))),
                any(ServiceConnection.class),
                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
                eq(UserHandle.SYSTEM));
@@ -287,45 +235,6 @@ public class NetworkScoreServiceTest {
        }
    }

    @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,
                CACHE_FILTER_NONE);

        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,
                CACHE_FILTER_NONE);

        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);
@@ -338,48 +247,6 @@ public class NetworkScoreServiceTest {
        } 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,
                CACHE_FILTER_NONE);

        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,
                CACHE_FILTER_NONE);

        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