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

Commit af811c26 authored by Jeremy Joslin's avatar Jeremy Joslin Committed by android-build-merger
Browse files

Merge "Implement the discovery of a network recommendation provider." am: 470d2565

am: 8fbeb1d7

Change-Id: I322592326e0a409131a4dd33ab1f907b9262dbf2
parents e36f449c 8fbeb1d7
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