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;
20967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslinimport android.content.BroadcastReceiver;
21dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslinimport android.content.ComponentName;
2256f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidsonimport android.content.ContentResolver;
236a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.content.Context;
24b096bdceaf2a4becffd2d930a870ccb57bfbe99cJeff Davidsonimport android.content.Intent;
25967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslinimport android.content.IntentFilter;
26dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslinimport android.content.ServiceConnection;
276a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.content.pm.PackageManager;
2814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport android.net.INetworkScoreCache;
296a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.net.INetworkScoreService;
30b096bdceaf2a4becffd2d930a870ccb57bfbe99cJeff Davidsonimport android.net.NetworkScoreManager;
316a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.net.NetworkScorerAppManager;
32c741553644f8b19c63938ab9e36af1721c2cfa34Jeff Davidsonimport android.net.NetworkScorerAppManager.NetworkScorerAppData;
336a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.net.ScoredNetwork;
3426fd143326a11c9dd7942e31acca6df56288d194Jeff Davidsonimport android.os.Binder;
35dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslinimport android.os.IBinder;
3614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport android.os.RemoteException;
37ac7285dc1e13f30d59dad30fe2ad1116e5f676cbJeff Davidsonimport android.os.UserHandle;
3856f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidsonimport android.provider.Settings;
396a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport android.text.TextUtils;
4014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport android.util.Log;
416a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
426a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport com.android.internal.R;
437842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidsonimport com.android.internal.annotations.GuardedBy;
441ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslinimport com.android.internal.content.PackageMonitor;
456a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
466a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport java.io.FileDescriptor;
476a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport java.io.PrintWriter;
4814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport java.util.ArrayList;
496a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport java.util.HashMap;
5014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport java.util.HashSet;
5114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport java.util.List;
526a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonimport java.util.Map;
5314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidsonimport java.util.Set;
546a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
556a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson/**
566a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * Backing service for {@link android.net.NetworkScoreManager}.
576a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson * @hide
586a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson */
596a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidsonpublic class NetworkScoreService extends INetworkScoreService.Stub {
606a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    private static final String TAG = "NetworkScoreService";
61dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    private static final boolean DBG = false;
626a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
636a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    private final Context mContext;
6414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    private final Map<Integer, INetworkScoreCache> mScoreCaches;
651ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin    /** Lock used to update mPackageMonitor when scorer package changes occur. */
661ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin    private final Object mPackageMonitorLock = new Object[0];
677842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
681ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin    @GuardedBy("mPackageMonitorLock")
691ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin    private NetworkScorerPackageMonitor mPackageMonitor;
70dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    private ScoringServiceConnection mServiceConnection;
717842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
72967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin    private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
73967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin        @Override
74967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin        public void onReceive(Context context, Intent intent) {
75967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin            final String action = intent.getAction();
76967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
77967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin            if (DBG) Log.d(TAG, "Received " + action + " for userId " + userId);
78967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin            if (userId == UserHandle.USER_NULL) return;
79967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin
80967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin            if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
81967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin                onUserUnlocked(userId);
82967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin            }
83967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin        }
84967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin    };
85967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin
861ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin    /**
871ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin     * Clears scores when the active scorer package is no longer valid and
881ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin     * manages the service connection.
891ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin     */
901ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin    private class NetworkScorerPackageMonitor extends PackageMonitor {
917842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        final String mRegisteredPackage;
927842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
931ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        private NetworkScorerPackageMonitor(String mRegisteredPackage) {
941ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            this.mRegisteredPackage = mRegisteredPackage;
951ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        }
961ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin
971ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        @Override
981ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        public void onPackageAdded(String packageName, int uid) {
991ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            evaluateBinding(packageName, true /* forceUnbind */);
1001ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        }
1011ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin
1021ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        @Override
1031ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        public void onPackageRemoved(String packageName, int uid) {
1041ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            evaluateBinding(packageName, true /* forceUnbind */);
1051ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        }
1061ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin
1071ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        @Override
1081ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        public void onPackageModified(String packageName) {
1091ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            evaluateBinding(packageName, false /* forceUnbind */);
1101ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        }
1111ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin
1121ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        @Override
1131ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
1141ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            if (doit) { // "doit" means the force stop happened instead of just being queried for.
1151ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                for (String packageName : packages) {
1161ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    evaluateBinding(packageName, true /* forceUnbind */);
1171ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                }
1181ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            }
1191ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            return super.onHandleForceStop(intent, packages, uid, doit);
1207842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        }
1217842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
1227842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        @Override
1231ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        public void onPackageUpdateFinished(String packageName, int uid) {
1241ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            evaluateBinding(packageName, true /* forceUnbind */);
1251ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        }
1261ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin
1271ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
1281ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            if (mRegisteredPackage.equals(scorerPackageName)) {
1291ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                if (DBG) {
1301ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    Log.d(TAG, "Evaluating binding for: " + scorerPackageName
1311ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                            + ", forceUnbind=" + forceUnbind);
1321ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                }
1331ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                final NetworkScorerAppData activeScorer =
134dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                        NetworkScorerAppManager.getActiveScorer(mContext);
135dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                if (activeScorer == null) {
1361ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    // Package change has invalidated a scorer, this will also unbind any service
1371ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    // connection.
138dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                    Log.i(TAG, "Package " + mRegisteredPackage +
139dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                            " is no longer valid, disabling scoring.");
140dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                    setScorerInternal(null);
141dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                } else if (activeScorer.mScoringServiceClassName == null) {
142dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                    // The scoring service is not available, make sure it's unbound.
143dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                    unbindFromScoringServiceIfNeeded();
1441ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                } else { // The scoring service changed in some way.
1451ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    if (forceUnbind) {
1461ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                        unbindFromScoringServiceIfNeeded();
1471ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    }
148dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                    bindToScoringServiceIfNeeded(activeScorer);
149dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                }
1507842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            }
1517842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        }
1527842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    }
1537842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
1546a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    public NetworkScoreService(Context context) {
1556a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        mContext = context;
15614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        mScoreCaches = new HashMap<>();
157967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
158967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin        // TODO: Need to update when we support per-user scorers. http://b/23422763
159967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin        mContext.registerReceiverAsUser(
160967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin                mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
161967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin                null /* scheduler */);
1626a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
1636a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
1646a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    /** Called when the system is ready to run third-party code but before it actually does so. */
1656a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    void systemReady() {
166dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        if (DBG) Log.d(TAG, "systemReady");
16756f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidson        ContentResolver cr = mContext.getContentResolver();
16856f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidson        if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
1696a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            // On first run, we try to initialize the scorer to the one configured at build time.
1706a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            // This will be a no-op if the scorer isn't actually valid.
1716a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            String defaultPackage = mContext.getResources().getString(
1726a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                    R.string.config_defaultNetworkScorerPackageName);
1736a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            if (!TextUtils.isEmpty(defaultPackage)) {
1746a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
1756a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            }
17656f9f73a5aad38aa777ec9a42c859e687f2d2af1Jeff Davidson            Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
1776a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
1787842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
1791ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        registerPackageMonitorIfNeeded();
1807842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson    }
1817842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
182dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    /** Called when the system is ready for us to start third-party code. */
183dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    void systemRunning() {
184dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        if (DBG) Log.d(TAG, "systemRunning");
185dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        bindToScoringServiceIfNeeded();
186dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    }
187dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
188967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin    private void onUserUnlocked(int userId) {
189967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin        registerPackageMonitorIfNeeded();
190967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin        bindToScoringServiceIfNeeded();
191967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin    }
192967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin
1931ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin    private void registerPackageMonitorIfNeeded() {
1941ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
1957842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext);
1961ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        synchronized (mPackageMonitorLock) {
1971ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            // Unregister the current monitor if needed.
1981ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            if (mPackageMonitor != null) {
1991ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                if (DBG) {
2001ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    Log.d(TAG, "Unregistering package monitor for "
2011ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                            + mPackageMonitor.mRegisteredPackage);
2027842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                }
2031ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                mPackageMonitor.unregister();
2041ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                mPackageMonitor = null;
2057842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            }
2067842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson
2071ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            // Create and register the monitor if a scorer is active.
2087842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            if (scorer != null) {
2091ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                mPackageMonitor = new NetworkScorerPackageMonitor(scorer.mPackageName);
210e4de5a0d3b6e0c897c1cea0912b58e11db962365Xiaohui Chen                // TODO: Need to update when we support per-user scorers. http://b/23422763
2111ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                mPackageMonitor.register(mContext, null /* thread */, UserHandle.SYSTEM,
2121ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                        false /* externalStorage */);
2131ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                if (DBG) {
2141ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    Log.d(TAG, "Registered package monitor for "
2151ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                            + mPackageMonitor.mRegisteredPackage);
2167842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson                }
2177842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson            }
2187842f64b75282f1233b30ebdb0d876c485def9a7Jeff Davidson        }
2196a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
2206a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
221dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    private void bindToScoringServiceIfNeeded() {
222dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded");
223dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        NetworkScorerAppData scorerData = NetworkScorerAppManager.getActiveScorer(mContext);
224dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        bindToScoringServiceIfNeeded(scorerData);
225dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    }
226dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
227dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) {
228dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")");
229dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        if (scorerData != null && scorerData.mScoringServiceClassName != null) {
230dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            ComponentName componentName =
231dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                    new ComponentName(scorerData.mPackageName, scorerData.mScoringServiceClassName);
232dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            // If we're connected to a different component then drop it.
233dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            if (mServiceConnection != null
234dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                    && !mServiceConnection.mComponentName.equals(componentName)) {
235dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                unbindFromScoringServiceIfNeeded();
236dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            }
237dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
238dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            // If we're not connected at all then create a new connection.
239dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            if (mServiceConnection == null) {
240dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                mServiceConnection = new ScoringServiceConnection(componentName);
241dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            }
242dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
243dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            // Make sure the connection is connected (idempotent)
244dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            mServiceConnection.connect(mContext);
245967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin        } else { // otherwise make sure it isn't bound.
246967b5815fb55e25419dcfdcf2144cce513123a61Jeremy Joslin            unbindFromScoringServiceIfNeeded();
247dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        }
248dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    }
249dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
250dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    private void unbindFromScoringServiceIfNeeded() {
251dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded");
252dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        if (mServiceConnection != null) {
253dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            mServiceConnection.disconnect(mContext);
254dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        }
255dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        mServiceConnection = null;
256dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    }
257dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
2586a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    @Override
2596a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    public boolean updateScores(ScoredNetwork[] networks) {
2606a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
2616a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            throw new SecurityException("Caller with UID " + getCallingUid() +
2626a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                    " is not the active scorer.");
2636a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
2646a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
26514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        // Separate networks by type.
26614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
2676a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        for (ScoredNetwork network : networks) {
26814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
26914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            if (networkList == null) {
27014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                networkList = new ArrayList<>();
27114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                networksByType.put(network.networkKey.type, networkList);
27214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            }
27314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            networkList.add(network);
27414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        }
27514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
27614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        // Pass the scores of each type down to the appropriate network scorer.
27714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
27814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
27914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            if (scoreCache != null) {
28014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                try {
28114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    scoreCache.updateScores(entry.getValue());
28214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                } catch (RemoteException e) {
28314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
28414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                        Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
28514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    }
28614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                }
28714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
28814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
28914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            }
2906a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
2916a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
2926a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        return true;
2936a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
2946a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
2956a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    @Override
2966a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    public boolean clearScores() {
297161977998feebf0a855ea56558464470877040cfJeff Davidson        // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
298161977998feebf0a855ea56558464470877040cfJeff Davidson        // should be allowed to flush all scores.
2996a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
300161977998feebf0a855ea56558464470877040cfJeff Davidson                mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
3016a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                        PackageManager.PERMISSION_GRANTED) {
3026a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            clearInternal();
3036a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            return true;
3046a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        } else {
3056a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            throw new SecurityException(
3066a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson                    "Caller is neither the active scorer nor the scorer manager.");
3076a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
3086a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
3096a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
3106a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    @Override
3116a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    public boolean setActiveScorer(String packageName) {
312e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // TODO: For now, since SCORE_NETWORKS requires an app to be privileged, we allow such apps
313e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // to directly set the scorer app rather than having to use the consent dialog. The
314e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // assumption is that anyone bundling a scorer app with the system is trusted by the OEM to
315e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // do the right thing and not enable this feature without explaining it to the user.
316e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // In the future, should this API be opened to 3p apps, we will need to lock this down and
317e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // figure out another way to streamline the UX.
318e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson
319e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        // mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
320e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson        mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
321e56f2bb5ecae624e1b6573515f855a26f756aed5Jeff Davidson
32226fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        return setScorerInternal(packageName);
32326fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    }
32426fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson
32526fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    @Override
32626fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    public void disableScoring() {
327161977998feebf0a855ea56558464470877040cfJeff Davidson        // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
328161977998feebf0a855ea56558464470877040cfJeff Davidson        // should be allowed to disable scoring.
32926fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
330161977998feebf0a855ea56558464470877040cfJeff Davidson                mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
33126fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson                        PackageManager.PERMISSION_GRANTED) {
33226fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // The return value is discarded here because at this point, the call should always
33326fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // succeed. The only reason for failure is if the new package is not a valid scorer, but
33426fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // we're disabling scoring altogether here.
33526fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            setScorerInternal(null /* packageName */);
33626fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        } else {
33726fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            throw new SecurityException(
33826fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson                    "Caller is neither the active scorer nor the scorer manager.");
33926fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        }
34026fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    }
34126fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson
34226fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
34326fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson    private boolean setScorerInternal(String packageName) {
344dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        if (DBG) Log.d(TAG, "setScorerInternal(" + packageName + ")");
34526fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        long token = Binder.clearCallingIdentity();
34626fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        try {
347dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            unbindFromScoringServiceIfNeeded();
34826fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // Preemptively clear scores even though the set operation could fail. We do this for
34926fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // safety as scores should never be compared across apps; in practice, Settings should
35026fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            // only be allowing valid apps to be set as scorers, so failure here should be rare.
35126fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            clearInternal();
352da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin            // Get the scorer that is about to be replaced, if any, so we can notify it directly.
353da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin            NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext);
35426fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
355dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed
356dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            // then we'll attempt to restore the previous binding (if any), otherwise an attempt
357dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            // will be made to bind to the new scorer.
358dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            bindToScoringServiceIfNeeded();
359da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin            if (result) { // new scorer successfully set
3601ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                registerPackageMonitorIfNeeded();
361dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
36226fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson                Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
363da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                if (prevScorer != null) { // Directly notify the old scorer.
364da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                    intent.setPackage(prevScorer.mPackageName);
365da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                    // TODO: Need to update when we support per-user scorers. http://b/23422763
366da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                    mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
367da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                }
368da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin
369da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                if (packageName != null) { // Then notify the new scorer
370da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                    intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
371da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                    intent.setPackage(packageName);
372da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                    // TODO: Need to update when we support per-user scorers. http://b/23422763
373da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                    mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
374da11f5cdf55ace0330008613fa7153993ddd943fJeremy Joslin                }
37526fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            }
37626fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            return result;
37726fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson        } finally {
37826fd143326a11c9dd7942e31acca6df56288d194Jeff Davidson            Binder.restoreCallingIdentity(token);
379b096bdceaf2a4becffd2d930a870ccb57bfbe99cJeff Davidson        }
3806a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
3816a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
3826a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    /** Clear scores. Callers are responsible for checking permissions as appropriate. */
3836a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    private void clearInternal() {
38414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        Set<INetworkScoreCache> cachesToClear = getScoreCaches();
38514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
38614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        for (INetworkScoreCache scoreCache : cachesToClear) {
38714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            try {
38814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                scoreCache.clearScores();
38914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            } catch (RemoteException e) {
39014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                if (Log.isLoggable(TAG, Log.VERBOSE)) {
39114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    Log.v(TAG, "Unable to clear scores", e);
39214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                }
39314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            }
39414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        }
39514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    }
39614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
39714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    @Override
39814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
399161977998feebf0a855ea56558464470877040cfJeff Davidson        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
40014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        synchronized (mScoreCaches) {
40114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            if (mScoreCaches.containsKey(networkType)) {
40214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                throw new IllegalArgumentException(
40314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                        "Score cache already registered for type " + networkType);
40414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            }
40514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            mScoreCaches.put(networkType, scoreCache);
40614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        }
4076a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
4086a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson
4096a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    @Override
4106a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
4116a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
412c741553644f8b19c63938ab9e36af1721c2cfa34Jeff Davidson        NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
4136a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        if (currentScorer == null) {
4146a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            writer.println("Scoring is disabled.");
4156a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            return;
4166a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
417c741553644f8b19c63938ab9e36af1721c2cfa34Jeff Davidson        writer.println("Current scorer: " + currentScorer.mPackageName);
41814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
41914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        for (INetworkScoreCache scoreCache : getScoreCaches()) {
42014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            try {
42114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                scoreCache.asBinder().dump(fd, args);
42214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            } catch (RemoteException e) {
42314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                writer.println("Unable to dump score cache");
42414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                if (Log.isLoggable(TAG, Log.VERBOSE)) {
42514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                    Log.v(TAG, "Unable to dump score cache", e);
42614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson                }
4276a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson            }
4286a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson        }
429dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        if (mServiceConnection != null) {
430dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            mServiceConnection.dump(fd, writer, args);
431dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        } else {
432dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            writer.println("ScoringServiceConnection: null");
433dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        }
434dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        writer.flush();
4356a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson    }
43614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson
43714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    /**
43814f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     * Returns a set of all score caches that are currently active.
43914f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     *
44014f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     * <p>May be used to perform an action on all score caches without potentially strange behavior
44114f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     * if a new scorer is registered during that action's execution.
44214f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson     */
44314f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    private Set<INetworkScoreCache> getScoreCaches() {
44414f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        synchronized (mScoreCaches) {
44514f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson            return new HashSet<>(mScoreCaches.values());
44614f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson        }
44714f1ec05b2add5ee051c0d2e7c7c3b36a6e77b92Jeff Davidson    }
448dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
449dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    private static class ScoringServiceConnection implements ServiceConnection {
450dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        private final ComponentName mComponentName;
451dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        private boolean mBound = false;
4521ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin        private boolean mConnected = false;
453dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
454dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        ScoringServiceConnection(ComponentName componentName) {
455dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            mComponentName = componentName;
456dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        }
457dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
458dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        void connect(Context context) {
459dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            if (!mBound) {
4601ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                Intent service = new Intent();
4611ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                service.setComponent(mComponentName);
4621ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                mBound = context.bindServiceAsUser(service, this,
4631ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
4641ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                        UserHandle.SYSTEM);
4651ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                if (!mBound) {
4661ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    Log.w(TAG, "Bind call failed for " + service);
4671ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                } else {
4681ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    if (DBG) Log.d(TAG, "ScoringServiceConnection bound.");
4691ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                }
470dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            }
471dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        }
472dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
473dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        void disconnect(Context context) {
474dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            try {
475dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                if (mBound) {
476dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                    mBound = false;
477dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                    context.unbindService(this);
4781ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    if (DBG) Log.d(TAG, "ScoringServiceConnection unbound.");
479dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                }
480dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            } catch (RuntimeException e) {
481dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin                Log.e(TAG, "Unbind failed.", e);
482dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            }
483dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        }
484dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
485dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        @Override
486dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        public void onServiceConnected(ComponentName name, IBinder service) {
487dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin            if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
4881ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            mConnected = true;
489dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        }
490dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
491dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        @Override
492dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        public void onServiceDisconnected(ComponentName name) {
4931ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            if (DBG) {
4941ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
4951ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            }
4961ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            mConnected = false;
497dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        }
498dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin
499dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
5001ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin            writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound
5011ec8cd95426a22ded1ab9a13c7608ce9c699010aJeremy Joslin                    + ", connected: " + mConnected);
502dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin        }
503dd251ef4952b6c7abbfe8cea49285d8cfe62f96eJeremy Joslin    }
5046a4b220f1263d95fdefe6361c2bc87bbb04bbed0Jeff Davidson}
505