16a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson/*
26a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * Copyright (C) 2014 The Android Open Source Project
36a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson *
46a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * Licensed under the Apache License, Version 2.0 (the "License");
56a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * you may not use this file except in compliance with the License.
66a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * You may obtain a copy of the License at
76a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson *
86a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson *      http://www.apache.org/licenses/LICENSE-2.0
96a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson *
106a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * Unless required by applicable law or agreed to in writing, software
116a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * distributed under the License is distributed on an "AS IS" BASIS,
126a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * See the License for the specific language governing permissions and
146a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * limitations under the License
156a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson */
166a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
176a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonpackage com.android.server;
186a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
196a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.Manifest.permission;
207842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidsonimport android.content.BroadcastReceiver;
2156f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidsonimport android.content.ContentResolver;
226a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.content.Context;
23b096bdceaf2a4becffd2d930a870ccb57bfbe99cJeff Davidsonimport android.content.Intent;
247842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidsonimport android.content.IntentFilter;
256a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.content.pm.PackageManager;
2614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport android.net.INetworkScoreCache;
276a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.net.INetworkScoreService;
28b096bdceaf2a4becffd2d930a870ccb57bfbe99cJeff Davidsonimport android.net.NetworkScoreManager;
296a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.net.NetworkScorerAppManager;
30c741553644f8b19c63938ab9e36af1721c2cfa34Jeff Davidsonimport android.net.NetworkScorerAppManager.NetworkScorerAppData;
316a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.net.ScoredNetwork;
3226fd143326a11c9dd7942e31acca6df56288d194Jeff Davidsonimport android.os.Binder;
337842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidsonimport android.os.PatternMatcher;
3414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport android.os.RemoteException;
35ac7285dc1e13f30d59dad30fe2ad1116e5f676cbJeff Davidsonimport android.os.UserHandle;
3656f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidsonimport android.provider.Settings;
376a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.text.TextUtils;
3814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport android.util.Log;
396a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
406a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport com.android.internal.R;
417842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidsonimport com.android.internal.annotations.GuardedBy;
426a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
436a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport java.io.FileDescriptor;
446a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport java.io.PrintWriter;
4514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport java.util.ArrayList;
466a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport java.util.HashMap;
4714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport java.util.HashSet;
4814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport java.util.List;
496a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport java.util.Map;
5014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport java.util.Set;
516a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
526a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson/**
536a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * Backing service for {@link android.net.NetworkScoreManager}.
546a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * @hide
556a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson */
566a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonpublic class NetworkScoreService extends INetworkScoreService.Stub {
576a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    private static final String TAG = "NetworkScoreService";
586a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
596a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    private final Context mContext;
606a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
6114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    private final Map<Integer, INetworkScoreCache> mScoreCaches;
626a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
637842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    /** Lock used to update mReceiver when scorer package changes occur. */
647842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    private Object mReceiverLock = new Object[0];
657842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
667842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    /** Clears scores when the active scorer package is no longer valid. */
677842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    @GuardedBy("mReceiverLock")
687842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    private ScorerChangedReceiver mReceiver;
697842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
707842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    private class ScorerChangedReceiver extends BroadcastReceiver {
717842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        final String mRegisteredPackage;
727842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
737842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        ScorerChangedReceiver(String packageName) {
747842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            mRegisteredPackage = packageName;
757842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        }
767842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
777842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        @Override
787842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        public void onReceive(Context context, Intent intent) {
797842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            String action = intent.getAction();
807842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            if ((Intent.ACTION_PACKAGE_CHANGED.equals(action) ||
817842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                    Intent.ACTION_PACKAGE_REPLACED.equals(action) ||
827842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                    Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) &&
837842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                    NetworkScorerAppManager.getActiveScorer(mContext) == null) {
847842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                // Package change has invalidated a scorer.
857842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                Log.i(TAG, "Package " + mRegisteredPackage +
867842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                        " is no longer valid, disabling scoring");
877842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                setScorerInternal(null);
887842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            }
897842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        }
907842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    }
917842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
926a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    public NetworkScoreService(Context context) {
936a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        mContext = context;
9414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        mScoreCaches = new HashMap<>();
956a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
966a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
976a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    /** Called when the system is ready to run third-party code but before it actually does so. */
986a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    void systemReady() {
9956f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidson        ContentResolver cr = mContext.getContentResolver();
10056f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidson        if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
1016a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            // On first run, we try to initialize the scorer to the one configured at build time.
1026a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            // This will be a no-op if the scorer isn't actually valid.
1036a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            String defaultPackage = mContext.getResources().getString(
1046a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                    R.string.config_defaultNetworkScorerPackageName);
1056a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            if (!TextUtils.isEmpty(defaultPackage)) {
1066a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
1076a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            }
10856f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidson            Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
1096a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
1107842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
1117842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        registerPackageReceiverIfNeeded();
1127842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    }
1137842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
1147842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    private void registerPackageReceiverIfNeeded() {
1157842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext);
1167842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        synchronized (mReceiverLock) {
1177842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            // Unregister the receiver if the current scorer has changed since last registration.
1187842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            if (mReceiver != null) {
1197842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                if (Log.isLoggable(TAG, Log.VERBOSE)) {
1207842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                    Log.v(TAG, "Unregistering receiver for " + mReceiver.mRegisteredPackage);
1217842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                }
1227842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                mContext.unregisterReceiver(mReceiver);
1237842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                mReceiver = null;
1247842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            }
1257842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
1267842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            // Register receiver if a scorer is active.
1277842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            if (scorer != null) {
1287842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                IntentFilter filter = new IntentFilter();
1297842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1307842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
1317842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
1327842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                filter.addDataScheme("package");
1337842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                filter.addDataSchemeSpecificPart(scorer.mPackageName,
1347842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                        PatternMatcher.PATTERN_LITERAL);
1357842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                mReceiver = new ScorerChangedReceiver(scorer.mPackageName);
1367842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                // TODO: Need to update when we support per-user scorers.
1377842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter, null, null);
1387842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                if (Log.isLoggable(TAG, Log.VERBOSE)) {
1397842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                    Log.v(TAG, "Registered receiver for " + scorer.mPackageName);
1407842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                }
1417842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            }
1427842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        }
1436a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
1446a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
1456a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    @Override
1466a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    public boolean updateScores(ScoredNetwork[] networks) {
1476a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
1486a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            throw new SecurityException("Caller with UID " + getCallingUid() +
1496a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                    " is not the active scorer.");
1506a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
1516a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
15214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        // Separate networks by type.
15314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
1546a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        for (ScoredNetwork network : networks) {
15514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
15614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            if (networkList == null) {
15714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                networkList = new ArrayList<>();
15814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                networksByType.put(network.networkKey.type, networkList);
15914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            }
16014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            networkList.add(network);
16114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        }
16214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
16314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        // Pass the scores of each type down to the appropriate network scorer.
16414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
16514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
16614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            if (scoreCache != null) {
16714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                try {
16814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    scoreCache.updateScores(entry.getValue());
16914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                } catch (RemoteException e) {
17014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
17114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                        Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
17214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    }
17314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                }
17414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
17514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
17614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            }
1776a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
1786a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
1796a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        return true;
1806a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
1816a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
1826a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    @Override
1836a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    public boolean clearScores() {
184161977998feebf0a855ea56558464470877040cfJeff Davidson        // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
185161977998feebf0a855ea56558464470877040cfJeff Davidson        // should be allowed to flush all scores.
1866a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
187161977998feebf0a855ea56558464470877040cfJeff Davidson                mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
1886a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                        PackageManager.PERMISSION_GRANTED) {
1896a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            clearInternal();
1906a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            return true;
1916a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        } else {
1926a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            throw new SecurityException(
1936a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                    "Caller is neither the active scorer nor the scorer manager.");
1946a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
1956a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
1966a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
1976a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    @Override
1986a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    public boolean setActiveScorer(String packageName) {
199e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // TODO: For now, since SCORE_NETWORKS requires an app to be privileged, we allow such apps
200e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // to directly set the scorer app rather than having to use the consent dialog. The
201e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // assumption is that anyone bundling a scorer app with the system is trusted by the OEM to
202e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // do the right thing and not enable this feature without explaining it to the user.
203e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // In the future, should this API be opened to 3p apps, we will need to lock this down and
204e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // figure out another way to streamline the UX.
205e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson
206e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
207e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
208e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson
20926fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        return setScorerInternal(packageName);
21026fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    }
21126fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson
21226fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    @Override
21326fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    public void disableScoring() {
214161977998feebf0a855ea56558464470877040cfJeff Davidson        // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
215161977998feebf0a855ea56558464470877040cfJeff Davidson        // should be allowed to disable scoring.
21626fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
217161977998feebf0a855ea56558464470877040cfJeff Davidson                mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
21826fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson                        PackageManager.PERMISSION_GRANTED) {
21926fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // The return value is discarded here because at this point, the call should always
22026fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // succeed. The only reason for failure is if the new package is not a valid scorer, but
22126fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // we're disabling scoring altogether here.
22226fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            setScorerInternal(null /* packageName */);
22326fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        } else {
22426fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            throw new SecurityException(
22526fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson                    "Caller is neither the active scorer nor the scorer manager.");
22626fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        }
22726fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    }
22826fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson
22926fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
23026fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    private boolean setScorerInternal(String packageName) {
23126fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        long token = Binder.clearCallingIdentity();
23226fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        try {
23326fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // Preemptively clear scores even though the set operation could fail. We do this for
23426fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // safety as scores should never be compared across apps; in practice, Settings should
23526fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // only be allowing valid apps to be set as scorers, so failure here should be rare.
23626fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            clearInternal();
23726fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
23826fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            if (result) {
2397842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                registerPackageReceiverIfNeeded();
24026fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson                Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
24126fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson                intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
242ac7285dc1e13f30d59dad30fe2ad1116e5f676cbJeff Davidson                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
24326fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            }
24426fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            return result;
24526fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        } finally {
24626fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            Binder.restoreCallingIdentity(token);
247b096bdceaf2a4becffd2d930a870ccb57bfbe99cJeff Davidson        }
2486a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
2496a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
2506a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    /** Clear scores. Callers are responsible for checking permissions as appropriate. */
2516a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    private void clearInternal() {
25214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        Set<INetworkScoreCache> cachesToClear = getScoreCaches();
25314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
25414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        for (INetworkScoreCache scoreCache : cachesToClear) {
25514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            try {
25614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                scoreCache.clearScores();
25714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            } catch (RemoteException e) {
25814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                if (Log.isLoggable(TAG, Log.VERBOSE)) {
25914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    Log.v(TAG, "Unable to clear scores", e);
26014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                }
26114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            }
26214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        }
26314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    }
26414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
26514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    @Override
26614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
267161977998feebf0a855ea56558464470877040cfJeff Davidson        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
26814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        synchronized (mScoreCaches) {
26914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            if (mScoreCaches.containsKey(networkType)) {
27014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                throw new IllegalArgumentException(
27114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                        "Score cache already registered for type " + networkType);
27214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            }
27314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            mScoreCaches.put(networkType, scoreCache);
27414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        }
2756a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
2766a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
2776a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    @Override
2786a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2796a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
280c741553644f8b19c63938ab9e36af1721c2cfa34Jeff Davidson        NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
2816a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        if (currentScorer == null) {
2826a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            writer.println("Scoring is disabled.");
2836a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            return;
2846a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
285c741553644f8b19c63938ab9e36af1721c2cfa34Jeff Davidson        writer.println("Current scorer: " + currentScorer.mPackageName);
28668c46fd8dd88b906cf064f0d8d0408d4f60e2a4dJeff Davidson        writer.flush();
28714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
28814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        for (INetworkScoreCache scoreCache : getScoreCaches()) {
28914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            try {
29014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                scoreCache.asBinder().dump(fd, args);
29114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            } catch (RemoteException e) {
29214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                writer.println("Unable to dump score cache");
29314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                if (Log.isLoggable(TAG, Log.VERBOSE)) {
29414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    Log.v(TAG, "Unable to dump score cache", e);
29514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                }
2966a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            }
2976a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
2986a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
29914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
30014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    /**
30114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     * Returns a set of all score caches that are currently active.
30214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     *
30314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     * <p>May be used to perform an action on all score caches without potentially strange behavior
30414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     * if a new scorer is registered during that action's execution.
30514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     */
30614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    private Set<INetworkScoreCache> getScoreCaches() {
30714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        synchronized (mScoreCaches) {
30814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            return new HashSet<>(mScoreCaches.values());
30914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        }
31014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    }
3116a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson}
312