blob: 756cb301fefad9f3cbef95b2086001f1e2c07738 [file] [log] [blame]
Jeff Davidson6a4b2202014-04-16 17:29:40 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server;
18
19import android.Manifest.permission;
Jeremy Joslin967b5812016-06-02 07:58:14 -070020import android.content.BroadcastReceiver;
Jeremy Joslindd251ef2016-03-14 11:17:41 -070021import android.content.ComponentName;
Jeff Davidson56f9f732014-08-14 16:47:23 -070022import android.content.ContentResolver;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070023import android.content.Context;
Jeff Davidsonb096bdc2014-07-01 12:29:11 -070024import android.content.Intent;
Jeremy Joslin967b5812016-06-02 07:58:14 -070025import android.content.IntentFilter;
Jeremy Joslindd251ef2016-03-14 11:17:41 -070026import android.content.ServiceConnection;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070027import android.content.pm.PackageManager;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070028import android.net.INetworkScoreCache;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070029import android.net.INetworkScoreService;
Jeff Davidsonb096bdc2014-07-01 12:29:11 -070030import android.net.NetworkScoreManager;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070031import android.net.NetworkScorerAppManager;
Jeff Davidsonc7415532014-06-23 18:15:34 -070032import android.net.NetworkScorerAppManager.NetworkScorerAppData;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070033import android.net.ScoredNetwork;
Jeff Davidson26fd1432014-07-29 09:39:52 -070034import android.os.Binder;
Jeremy Joslindd251ef2016-03-14 11:17:41 -070035import android.os.IBinder;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070036import android.os.RemoteException;
Jeff Davidsonac7285d2014-08-08 15:12:47 -070037import android.os.UserHandle;
Jeff Davidson56f9f732014-08-14 16:47:23 -070038import android.provider.Settings;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070039import android.text.TextUtils;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070040import android.util.Log;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070041
42import com.android.internal.R;
Jeff Davidson7842f642014-11-23 13:48:12 -080043import com.android.internal.annotations.GuardedBy;
Amin Shaikhaa09aa02016-11-21 17:27:53 -080044import com.android.internal.annotations.VisibleForTesting;
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070045import com.android.internal.content.PackageMonitor;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070046
47import java.io.FileDescriptor;
48import java.io.PrintWriter;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070049import java.util.ArrayList;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070050import java.util.HashMap;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070051import java.util.HashSet;
52import java.util.List;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070053import java.util.Map;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070054import java.util.Set;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070055
56/**
57 * Backing service for {@link android.net.NetworkScoreManager}.
58 * @hide
59 */
60public class NetworkScoreService extends INetworkScoreService.Stub {
61 private static final String TAG = "NetworkScoreService";
Jeremy Joslindd251ef2016-03-14 11:17:41 -070062 private static final boolean DBG = false;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070063
Jeff Davidson6a4b2202014-04-16 17:29:40 -070064 private final Context mContext;
Amin Shaikhaa09aa02016-11-21 17:27:53 -080065 private final NetworkScorerAppManager mNetworkScorerAppManager;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070066 private final Map<Integer, INetworkScoreCache> mScoreCaches;
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070067 /** Lock used to update mPackageMonitor when scorer package changes occur. */
68 private final Object mPackageMonitorLock = new Object[0];
Jeff Davidson7842f642014-11-23 13:48:12 -080069
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070070 @GuardedBy("mPackageMonitorLock")
71 private NetworkScorerPackageMonitor mPackageMonitor;
Jeremy Joslindd251ef2016-03-14 11:17:41 -070072 private ScoringServiceConnection mServiceConnection;
Jeff Davidson7842f642014-11-23 13:48:12 -080073
Jeremy Joslin967b5812016-06-02 07:58:14 -070074 private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
75 @Override
76 public void onReceive(Context context, Intent intent) {
77 final String action = intent.getAction();
78 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
79 if (DBG) Log.d(TAG, "Received " + action + " for userId " + userId);
80 if (userId == UserHandle.USER_NULL) return;
81
82 if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
83 onUserUnlocked(userId);
84 }
85 }
86 };
87
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070088 /**
89 * Clears scores when the active scorer package is no longer valid and
90 * manages the service connection.
91 */
92 private class NetworkScorerPackageMonitor extends PackageMonitor {
Jeff Davidson7842f642014-11-23 13:48:12 -080093 final String mRegisteredPackage;
94
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070095 private NetworkScorerPackageMonitor(String mRegisteredPackage) {
96 this.mRegisteredPackage = mRegisteredPackage;
Jeff Davidson7842f642014-11-23 13:48:12 -080097 }
98
99 @Override
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700100 public void onPackageAdded(String packageName, int uid) {
101 evaluateBinding(packageName, true /* forceUnbind */);
102 }
103
104 @Override
105 public void onPackageRemoved(String packageName, int uid) {
106 evaluateBinding(packageName, true /* forceUnbind */);
107 }
108
109 @Override
110 public void onPackageModified(String packageName) {
111 evaluateBinding(packageName, false /* forceUnbind */);
112 }
113
114 @Override
115 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
116 if (doit) { // "doit" means the force stop happened instead of just being queried for.
117 for (String packageName : packages) {
118 evaluateBinding(packageName, true /* forceUnbind */);
119 }
120 }
121 return super.onHandleForceStop(intent, packages, uid, doit);
122 }
123
124 @Override
125 public void onPackageUpdateFinished(String packageName, int uid) {
126 evaluateBinding(packageName, true /* forceUnbind */);
127 }
128
129 private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
130 if (mRegisteredPackage.equals(scorerPackageName)) {
131 if (DBG) {
132 Log.d(TAG, "Evaluating binding for: " + scorerPackageName
133 + ", forceUnbind=" + forceUnbind);
134 }
135 final NetworkScorerAppData activeScorer =
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800136 mNetworkScorerAppManager.getActiveScorer();
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700137 if (activeScorer == null) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700138 // Package change has invalidated a scorer, this will also unbind any service
139 // connection.
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700140 Log.i(TAG, "Package " + mRegisteredPackage +
141 " is no longer valid, disabling scoring.");
142 setScorerInternal(null);
143 } else if (activeScorer.mScoringServiceClassName == null) {
144 // The scoring service is not available, make sure it's unbound.
145 unbindFromScoringServiceIfNeeded();
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700146 } else { // The scoring service changed in some way.
147 if (forceUnbind) {
148 unbindFromScoringServiceIfNeeded();
149 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700150 bindToScoringServiceIfNeeded(activeScorer);
151 }
Jeff Davidson7842f642014-11-23 13:48:12 -0800152 }
153 }
154 }
155
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700156 public NetworkScoreService(Context context) {
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800157 this(context, new NetworkScorerAppManager(context));
158 }
159
160 @VisibleForTesting
161 NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager) {
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700162 mContext = context;
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800163 mNetworkScorerAppManager = networkScoreAppManager;
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700164 mScoreCaches = new HashMap<>();
Jeremy Joslin967b5812016-06-02 07:58:14 -0700165 IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
166 // TODO: Need to update when we support per-user scorers. http://b/23422763
167 mContext.registerReceiverAsUser(
168 mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
169 null /* scheduler */);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700170 }
171
172 /** Called when the system is ready to run third-party code but before it actually does so. */
173 void systemReady() {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700174 if (DBG) Log.d(TAG, "systemReady");
Jeff Davidson56f9f732014-08-14 16:47:23 -0700175 ContentResolver cr = mContext.getContentResolver();
176 if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700177 // On first run, we try to initialize the scorer to the one configured at build time.
178 // This will be a no-op if the scorer isn't actually valid.
179 String defaultPackage = mContext.getResources().getString(
180 R.string.config_defaultNetworkScorerPackageName);
181 if (!TextUtils.isEmpty(defaultPackage)) {
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800182 mNetworkScorerAppManager.setActiveScorer(defaultPackage);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700183 }
Jeff Davidson56f9f732014-08-14 16:47:23 -0700184 Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700185 }
Jeff Davidson7842f642014-11-23 13:48:12 -0800186
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700187 registerPackageMonitorIfNeeded();
Jeff Davidson7842f642014-11-23 13:48:12 -0800188 }
189
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700190 /** Called when the system is ready for us to start third-party code. */
191 void systemRunning() {
192 if (DBG) Log.d(TAG, "systemRunning");
193 bindToScoringServiceIfNeeded();
194 }
195
Jeremy Joslin967b5812016-06-02 07:58:14 -0700196 private void onUserUnlocked(int userId) {
197 registerPackageMonitorIfNeeded();
198 bindToScoringServiceIfNeeded();
199 }
200
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700201 private void registerPackageMonitorIfNeeded() {
202 if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800203 NetworkScorerAppData scorer = mNetworkScorerAppManager.getActiveScorer();
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700204 synchronized (mPackageMonitorLock) {
205 // Unregister the current monitor if needed.
206 if (mPackageMonitor != null) {
207 if (DBG) {
208 Log.d(TAG, "Unregistering package monitor for "
209 + mPackageMonitor.mRegisteredPackage);
Jeff Davidson7842f642014-11-23 13:48:12 -0800210 }
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700211 mPackageMonitor.unregister();
212 mPackageMonitor = null;
Jeff Davidson7842f642014-11-23 13:48:12 -0800213 }
214
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700215 // Create and register the monitor if a scorer is active.
Jeff Davidson7842f642014-11-23 13:48:12 -0800216 if (scorer != null) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700217 mPackageMonitor = new NetworkScorerPackageMonitor(scorer.mPackageName);
Xiaohui Chene4de5a02015-09-22 15:33:31 -0700218 // TODO: Need to update when we support per-user scorers. http://b/23422763
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700219 mPackageMonitor.register(mContext, null /* thread */, UserHandle.SYSTEM,
220 false /* externalStorage */);
221 if (DBG) {
222 Log.d(TAG, "Registered package monitor for "
223 + mPackageMonitor.mRegisteredPackage);
Jeff Davidson7842f642014-11-23 13:48:12 -0800224 }
225 }
226 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700227 }
228
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700229 private void bindToScoringServiceIfNeeded() {
230 if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded");
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800231 NetworkScorerAppData scorerData = mNetworkScorerAppManager.getActiveScorer();
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700232 bindToScoringServiceIfNeeded(scorerData);
233 }
234
235 private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) {
236 if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")");
237 if (scorerData != null && scorerData.mScoringServiceClassName != null) {
238 ComponentName componentName =
239 new ComponentName(scorerData.mPackageName, scorerData.mScoringServiceClassName);
240 // If we're connected to a different component then drop it.
241 if (mServiceConnection != null
242 && !mServiceConnection.mComponentName.equals(componentName)) {
243 unbindFromScoringServiceIfNeeded();
244 }
245
246 // If we're not connected at all then create a new connection.
247 if (mServiceConnection == null) {
248 mServiceConnection = new ScoringServiceConnection(componentName);
249 }
250
251 // Make sure the connection is connected (idempotent)
252 mServiceConnection.connect(mContext);
Jeremy Joslin967b5812016-06-02 07:58:14 -0700253 } else { // otherwise make sure it isn't bound.
254 unbindFromScoringServiceIfNeeded();
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700255 }
256 }
257
258 private void unbindFromScoringServiceIfNeeded() {
259 if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded");
260 if (mServiceConnection != null) {
261 mServiceConnection.disconnect(mContext);
262 }
263 mServiceConnection = null;
264 }
265
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700266 @Override
267 public boolean updateScores(ScoredNetwork[] networks) {
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800268 if (!mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid())) {
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700269 throw new SecurityException("Caller with UID " + getCallingUid() +
270 " is not the active scorer.");
271 }
272
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700273 // Separate networks by type.
274 Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700275 for (ScoredNetwork network : networks) {
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700276 List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
277 if (networkList == null) {
278 networkList = new ArrayList<>();
279 networksByType.put(network.networkKey.type, networkList);
280 }
281 networkList.add(network);
282 }
283
284 // Pass the scores of each type down to the appropriate network scorer.
285 for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
286 INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
287 if (scoreCache != null) {
288 try {
289 scoreCache.updateScores(entry.getValue());
290 } catch (RemoteException e) {
291 if (Log.isLoggable(TAG, Log.VERBOSE)) {
292 Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
293 }
294 }
295 } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
296 Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
297 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700298 }
299
300 return true;
301 }
302
303 @Override
304 public boolean clearScores() {
Jeff Davidson16197792014-11-03 17:39:54 -0800305 // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
306 // should be allowed to flush all scores.
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800307 if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
Jeff Davidson16197792014-11-03 17:39:54 -0800308 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700309 PackageManager.PERMISSION_GRANTED) {
310 clearInternal();
311 return true;
312 } else {
313 throw new SecurityException(
314 "Caller is neither the active scorer nor the scorer manager.");
315 }
316 }
317
318 @Override
319 public boolean setActiveScorer(String packageName) {
Jeff Davidsone56f2bb2014-11-05 11:14:19 -0800320 // TODO: For now, since SCORE_NETWORKS requires an app to be privileged, we allow such apps
321 // to directly set the scorer app rather than having to use the consent dialog. The
322 // assumption is that anyone bundling a scorer app with the system is trusted by the OEM to
323 // do the right thing and not enable this feature without explaining it to the user.
324 // In the future, should this API be opened to 3p apps, we will need to lock this down and
325 // figure out another way to streamline the UX.
326
327 // mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
328 mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
329
Jeff Davidson26fd1432014-07-29 09:39:52 -0700330 return setScorerInternal(packageName);
331 }
332
333 @Override
334 public void disableScoring() {
Jeff Davidson16197792014-11-03 17:39:54 -0800335 // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
336 // should be allowed to disable scoring.
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800337 if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
Jeff Davidson16197792014-11-03 17:39:54 -0800338 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
Jeff Davidson26fd1432014-07-29 09:39:52 -0700339 PackageManager.PERMISSION_GRANTED) {
340 // The return value is discarded here because at this point, the call should always
341 // succeed. The only reason for failure is if the new package is not a valid scorer, but
342 // we're disabling scoring altogether here.
343 setScorerInternal(null /* packageName */);
344 } else {
345 throw new SecurityException(
346 "Caller is neither the active scorer nor the scorer manager.");
Jeff Davidsonb096bdc2014-07-01 12:29:11 -0700347 }
Jeff Davidson26fd1432014-07-29 09:39:52 -0700348 }
349
350 /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
351 private boolean setScorerInternal(String packageName) {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700352 if (DBG) Log.d(TAG, "setScorerInternal(" + packageName + ")");
Jeff Davidson26fd1432014-07-29 09:39:52 -0700353 long token = Binder.clearCallingIdentity();
354 try {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700355 unbindFromScoringServiceIfNeeded();
Jeff Davidson26fd1432014-07-29 09:39:52 -0700356 // Preemptively clear scores even though the set operation could fail. We do this for
357 // safety as scores should never be compared across apps; in practice, Settings should
358 // only be allowing valid apps to be set as scorers, so failure here should be rare.
359 clearInternal();
Jeremy Joslinda11f5c2016-02-10 07:31:33 -0800360 // Get the scorer that is about to be replaced, if any, so we can notify it directly.
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800361 NetworkScorerAppData prevScorer = mNetworkScorerAppManager.getActiveScorer();
362 boolean result = mNetworkScorerAppManager.setActiveScorer(packageName);
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700363 // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed
364 // then we'll attempt to restore the previous binding (if any), otherwise an attempt
365 // will be made to bind to the new scorer.
366 bindToScoringServiceIfNeeded();
Jeremy Joslinda11f5c2016-02-10 07:31:33 -0800367 if (result) { // new scorer successfully set
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700368 registerPackageMonitorIfNeeded();
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700369
Jeff Davidson26fd1432014-07-29 09:39:52 -0700370 Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
Jeremy Joslinda11f5c2016-02-10 07:31:33 -0800371 if (prevScorer != null) { // Directly notify the old scorer.
372 intent.setPackage(prevScorer.mPackageName);
373 // TODO: Need to update when we support per-user scorers. http://b/23422763
374 mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
375 }
376
377 if (packageName != null) { // Then notify the new scorer
378 intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
379 intent.setPackage(packageName);
380 // TODO: Need to update when we support per-user scorers. http://b/23422763
381 mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
382 }
Jeff Davidson26fd1432014-07-29 09:39:52 -0700383 }
384 return result;
385 } finally {
386 Binder.restoreCallingIdentity(token);
387 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700388 }
389
390 /** Clear scores. Callers are responsible for checking permissions as appropriate. */
391 private void clearInternal() {
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700392 Set<INetworkScoreCache> cachesToClear = getScoreCaches();
393
394 for (INetworkScoreCache scoreCache : cachesToClear) {
395 try {
396 scoreCache.clearScores();
397 } catch (RemoteException e) {
398 if (Log.isLoggable(TAG, Log.VERBOSE)) {
399 Log.v(TAG, "Unable to clear scores", e);
400 }
401 }
402 }
403 }
404
405 @Override
406 public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
Jeff Davidson16197792014-11-03 17:39:54 -0800407 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700408 synchronized (mScoreCaches) {
409 if (mScoreCaches.containsKey(networkType)) {
410 throw new IllegalArgumentException(
411 "Score cache already registered for type " + networkType);
412 }
413 mScoreCaches.put(networkType, scoreCache);
414 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700415 }
416
417 @Override
418 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
419 mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800420 NetworkScorerAppData currentScorer = mNetworkScorerAppManager.getActiveScorer();
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700421 if (currentScorer == null) {
422 writer.println("Scoring is disabled.");
423 return;
424 }
Jeff Davidsonc7415532014-06-23 18:15:34 -0700425 writer.println("Current scorer: " + currentScorer.mPackageName);
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700426
427 for (INetworkScoreCache scoreCache : getScoreCaches()) {
428 try {
429 scoreCache.asBinder().dump(fd, args);
430 } catch (RemoteException e) {
431 writer.println("Unable to dump score cache");
432 if (Log.isLoggable(TAG, Log.VERBOSE)) {
433 Log.v(TAG, "Unable to dump score cache", e);
434 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700435 }
436 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700437 if (mServiceConnection != null) {
438 mServiceConnection.dump(fd, writer, args);
439 } else {
440 writer.println("ScoringServiceConnection: null");
441 }
442 writer.flush();
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700443 }
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700444
445 /**
446 * Returns a set of all score caches that are currently active.
447 *
448 * <p>May be used to perform an action on all score caches without potentially strange behavior
449 * if a new scorer is registered during that action's execution.
450 */
451 private Set<INetworkScoreCache> getScoreCaches() {
452 synchronized (mScoreCaches) {
453 return new HashSet<>(mScoreCaches.values());
454 }
455 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700456
457 private static class ScoringServiceConnection implements ServiceConnection {
458 private final ComponentName mComponentName;
459 private boolean mBound = false;
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700460 private boolean mConnected = false;
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700461
462 ScoringServiceConnection(ComponentName componentName) {
463 mComponentName = componentName;
464 }
465
466 void connect(Context context) {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700467 if (!mBound) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700468 Intent service = new Intent();
469 service.setComponent(mComponentName);
470 mBound = context.bindServiceAsUser(service, this,
471 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
472 UserHandle.SYSTEM);
473 if (!mBound) {
474 Log.w(TAG, "Bind call failed for " + service);
475 } else {
476 if (DBG) Log.d(TAG, "ScoringServiceConnection bound.");
477 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700478 }
479 }
480
481 void disconnect(Context context) {
482 try {
483 if (mBound) {
484 mBound = false;
485 context.unbindService(this);
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700486 if (DBG) Log.d(TAG, "ScoringServiceConnection unbound.");
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700487 }
488 } catch (RuntimeException e) {
489 Log.e(TAG, "Unbind failed.", e);
490 }
491 }
492
493 @Override
494 public void onServiceConnected(ComponentName name, IBinder service) {
495 if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700496 mConnected = true;
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700497 }
498
499 @Override
500 public void onServiceDisconnected(ComponentName name) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700501 if (DBG) {
502 Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
503 }
504 mConnected = false;
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700505 }
506
507 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700508 writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound
509 + ", connected: " + mConnected);
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700510 }
511 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700512}