blob: 3745e0b4a1f5ddd306bbe0c43d959edb7cb8c9aa [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 Joslindd251ef2016-03-14 11:17:41 -070020import android.content.ComponentName;
Jeff Davidson56f9f732014-08-14 16:47:23 -070021import android.content.ContentResolver;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070022import android.content.Context;
Jeff Davidsonb096bdc2014-07-01 12:29:11 -070023import android.content.Intent;
Jeremy Joslindd251ef2016-03-14 11:17:41 -070024import android.content.ServiceConnection;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070025import android.content.pm.PackageManager;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070026import android.net.INetworkScoreCache;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070027import android.net.INetworkScoreService;
Jeff Davidsonb096bdc2014-07-01 12:29:11 -070028import android.net.NetworkScoreManager;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070029import android.net.NetworkScorerAppManager;
Jeff Davidsonc7415532014-06-23 18:15:34 -070030import android.net.NetworkScorerAppManager.NetworkScorerAppData;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070031import android.net.ScoredNetwork;
Jeff Davidson26fd1432014-07-29 09:39:52 -070032import android.os.Binder;
Jeremy Joslindd251ef2016-03-14 11:17:41 -070033import android.os.IBinder;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070034import android.os.RemoteException;
Jeff Davidsonac7285d2014-08-08 15:12:47 -070035import android.os.UserHandle;
Jeff Davidson56f9f732014-08-14 16:47:23 -070036import android.provider.Settings;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070037import android.text.TextUtils;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070038import android.util.Log;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070039
40import com.android.internal.R;
Jeff Davidson7842f642014-11-23 13:48:12 -080041import com.android.internal.annotations.GuardedBy;
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070042import com.android.internal.content.PackageMonitor;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070043
44import java.io.FileDescriptor;
45import java.io.PrintWriter;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070046import java.util.ArrayList;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070047import java.util.HashMap;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070048import java.util.HashSet;
49import java.util.List;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070050import java.util.Map;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070051import java.util.Set;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070052
53/**
54 * Backing service for {@link android.net.NetworkScoreManager}.
55 * @hide
56 */
57public class NetworkScoreService extends INetworkScoreService.Stub {
58 private static final String TAG = "NetworkScoreService";
Jeremy Joslindd251ef2016-03-14 11:17:41 -070059 private static final boolean DBG = false;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070060
Jeff Davidson6a4b2202014-04-16 17:29:40 -070061 private final Context mContext;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070062 private final Map<Integer, INetworkScoreCache> mScoreCaches;
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070063 /** Lock used to update mPackageMonitor when scorer package changes occur. */
64 private final Object mPackageMonitorLock = new Object[0];
Jeff Davidson7842f642014-11-23 13:48:12 -080065
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070066 @GuardedBy("mPackageMonitorLock")
67 private NetworkScorerPackageMonitor mPackageMonitor;
Jeremy Joslindd251ef2016-03-14 11:17:41 -070068 private ScoringServiceConnection mServiceConnection;
Jeff Davidson7842f642014-11-23 13:48:12 -080069
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070070 /**
71 * Clears scores when the active scorer package is no longer valid and
72 * manages the service connection.
73 */
74 private class NetworkScorerPackageMonitor extends PackageMonitor {
Jeff Davidson7842f642014-11-23 13:48:12 -080075 final String mRegisteredPackage;
76
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070077 private NetworkScorerPackageMonitor(String mRegisteredPackage) {
78 this.mRegisteredPackage = mRegisteredPackage;
Jeff Davidson7842f642014-11-23 13:48:12 -080079 }
80
81 @Override
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070082 public void onPackageAdded(String packageName, int uid) {
83 evaluateBinding(packageName, true /* forceUnbind */);
84 }
85
86 @Override
87 public void onPackageRemoved(String packageName, int uid) {
88 evaluateBinding(packageName, true /* forceUnbind */);
89 }
90
91 @Override
92 public void onPackageModified(String packageName) {
93 evaluateBinding(packageName, false /* forceUnbind */);
94 }
95
96 @Override
97 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
98 if (doit) { // "doit" means the force stop happened instead of just being queried for.
99 for (String packageName : packages) {
100 evaluateBinding(packageName, true /* forceUnbind */);
101 }
102 }
103 return super.onHandleForceStop(intent, packages, uid, doit);
104 }
105
106 @Override
107 public void onPackageUpdateFinished(String packageName, int uid) {
108 evaluateBinding(packageName, true /* forceUnbind */);
109 }
110
111 private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
112 if (mRegisteredPackage.equals(scorerPackageName)) {
113 if (DBG) {
114 Log.d(TAG, "Evaluating binding for: " + scorerPackageName
115 + ", forceUnbind=" + forceUnbind);
116 }
117 final NetworkScorerAppData activeScorer =
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700118 NetworkScorerAppManager.getActiveScorer(mContext);
119 if (activeScorer == null) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700120 // Package change has invalidated a scorer, this will also unbind any service
121 // connection.
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700122 Log.i(TAG, "Package " + mRegisteredPackage +
123 " is no longer valid, disabling scoring.");
124 setScorerInternal(null);
125 } else if (activeScorer.mScoringServiceClassName == null) {
126 // The scoring service is not available, make sure it's unbound.
127 unbindFromScoringServiceIfNeeded();
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700128 } else { // The scoring service changed in some way.
129 if (forceUnbind) {
130 unbindFromScoringServiceIfNeeded();
131 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700132 bindToScoringServiceIfNeeded(activeScorer);
133 }
Jeff Davidson7842f642014-11-23 13:48:12 -0800134 }
135 }
136 }
137
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700138 public NetworkScoreService(Context context) {
139 mContext = context;
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700140 mScoreCaches = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700141 }
142
143 /** Called when the system is ready to run third-party code but before it actually does so. */
144 void systemReady() {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700145 if (DBG) Log.d(TAG, "systemReady");
Jeff Davidson56f9f732014-08-14 16:47:23 -0700146 ContentResolver cr = mContext.getContentResolver();
147 if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700148 // On first run, we try to initialize the scorer to the one configured at build time.
149 // This will be a no-op if the scorer isn't actually valid.
150 String defaultPackage = mContext.getResources().getString(
151 R.string.config_defaultNetworkScorerPackageName);
152 if (!TextUtils.isEmpty(defaultPackage)) {
153 NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
154 }
Jeff Davidson56f9f732014-08-14 16:47:23 -0700155 Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700156 }
Jeff Davidson7842f642014-11-23 13:48:12 -0800157
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700158 registerPackageMonitorIfNeeded();
Jeff Davidson7842f642014-11-23 13:48:12 -0800159 }
160
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700161 /** Called when the system is ready for us to start third-party code. */
162 void systemRunning() {
163 if (DBG) Log.d(TAG, "systemRunning");
164 bindToScoringServiceIfNeeded();
165 }
166
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700167 private void registerPackageMonitorIfNeeded() {
168 if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
Jeff Davidson7842f642014-11-23 13:48:12 -0800169 NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext);
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700170 synchronized (mPackageMonitorLock) {
171 // Unregister the current monitor if needed.
172 if (mPackageMonitor != null) {
173 if (DBG) {
174 Log.d(TAG, "Unregistering package monitor for "
175 + mPackageMonitor.mRegisteredPackage);
Jeff Davidson7842f642014-11-23 13:48:12 -0800176 }
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700177 mPackageMonitor.unregister();
178 mPackageMonitor = null;
Jeff Davidson7842f642014-11-23 13:48:12 -0800179 }
180
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700181 // Create and register the monitor if a scorer is active.
Jeff Davidson7842f642014-11-23 13:48:12 -0800182 if (scorer != null) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700183 mPackageMonitor = new NetworkScorerPackageMonitor(scorer.mPackageName);
Xiaohui Chene4de5a02015-09-22 15:33:31 -0700184 // TODO: Need to update when we support per-user scorers. http://b/23422763
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700185 mPackageMonitor.register(mContext, null /* thread */, UserHandle.SYSTEM,
186 false /* externalStorage */);
187 if (DBG) {
188 Log.d(TAG, "Registered package monitor for "
189 + mPackageMonitor.mRegisteredPackage);
Jeff Davidson7842f642014-11-23 13:48:12 -0800190 }
191 }
192 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700193 }
194
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700195 private void bindToScoringServiceIfNeeded() {
196 if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded");
197 NetworkScorerAppData scorerData = NetworkScorerAppManager.getActiveScorer(mContext);
198 bindToScoringServiceIfNeeded(scorerData);
199 }
200
201 private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) {
202 if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")");
203 if (scorerData != null && scorerData.mScoringServiceClassName != null) {
204 ComponentName componentName =
205 new ComponentName(scorerData.mPackageName, scorerData.mScoringServiceClassName);
206 // If we're connected to a different component then drop it.
207 if (mServiceConnection != null
208 && !mServiceConnection.mComponentName.equals(componentName)) {
209 unbindFromScoringServiceIfNeeded();
210 }
211
212 // If we're not connected at all then create a new connection.
213 if (mServiceConnection == null) {
214 mServiceConnection = new ScoringServiceConnection(componentName);
215 }
216
217 // Make sure the connection is connected (idempotent)
218 mServiceConnection.connect(mContext);
219 }
220 }
221
222 private void unbindFromScoringServiceIfNeeded() {
223 if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded");
224 if (mServiceConnection != null) {
225 mServiceConnection.disconnect(mContext);
226 }
227 mServiceConnection = null;
228 }
229
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700230 @Override
231 public boolean updateScores(ScoredNetwork[] networks) {
232 if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
233 throw new SecurityException("Caller with UID " + getCallingUid() +
234 " is not the active scorer.");
235 }
236
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700237 // Separate networks by type.
238 Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700239 for (ScoredNetwork network : networks) {
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700240 List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
241 if (networkList == null) {
242 networkList = new ArrayList<>();
243 networksByType.put(network.networkKey.type, networkList);
244 }
245 networkList.add(network);
246 }
247
248 // Pass the scores of each type down to the appropriate network scorer.
249 for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
250 INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
251 if (scoreCache != null) {
252 try {
253 scoreCache.updateScores(entry.getValue());
254 } catch (RemoteException e) {
255 if (Log.isLoggable(TAG, Log.VERBOSE)) {
256 Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
257 }
258 }
259 } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
260 Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
261 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700262 }
263
264 return true;
265 }
266
267 @Override
268 public boolean clearScores() {
Jeff Davidson16197792014-11-03 17:39:54 -0800269 // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
270 // should be allowed to flush all scores.
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700271 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
Jeff Davidson16197792014-11-03 17:39:54 -0800272 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700273 PackageManager.PERMISSION_GRANTED) {
274 clearInternal();
275 return true;
276 } else {
277 throw new SecurityException(
278 "Caller is neither the active scorer nor the scorer manager.");
279 }
280 }
281
282 @Override
283 public boolean setActiveScorer(String packageName) {
Jeff Davidsone56f2bb2014-11-05 11:14:19 -0800284 // TODO: For now, since SCORE_NETWORKS requires an app to be privileged, we allow such apps
285 // to directly set the scorer app rather than having to use the consent dialog. The
286 // assumption is that anyone bundling a scorer app with the system is trusted by the OEM to
287 // do the right thing and not enable this feature without explaining it to the user.
288 // In the future, should this API be opened to 3p apps, we will need to lock this down and
289 // figure out another way to streamline the UX.
290
291 // mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
292 mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
293
Jeff Davidson26fd1432014-07-29 09:39:52 -0700294 return setScorerInternal(packageName);
295 }
296
297 @Override
298 public void disableScoring() {
Jeff Davidson16197792014-11-03 17:39:54 -0800299 // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
300 // should be allowed to disable scoring.
Jeff Davidson26fd1432014-07-29 09:39:52 -0700301 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
Jeff Davidson16197792014-11-03 17:39:54 -0800302 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
Jeff Davidson26fd1432014-07-29 09:39:52 -0700303 PackageManager.PERMISSION_GRANTED) {
304 // The return value is discarded here because at this point, the call should always
305 // succeed. The only reason for failure is if the new package is not a valid scorer, but
306 // we're disabling scoring altogether here.
307 setScorerInternal(null /* packageName */);
308 } else {
309 throw new SecurityException(
310 "Caller is neither the active scorer nor the scorer manager.");
Jeff Davidsonb096bdc2014-07-01 12:29:11 -0700311 }
Jeff Davidson26fd1432014-07-29 09:39:52 -0700312 }
313
314 /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
315 private boolean setScorerInternal(String packageName) {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700316 if (DBG) Log.d(TAG, "setScorerInternal(" + packageName + ")");
Jeff Davidson26fd1432014-07-29 09:39:52 -0700317 long token = Binder.clearCallingIdentity();
318 try {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700319 unbindFromScoringServiceIfNeeded();
Jeff Davidson26fd1432014-07-29 09:39:52 -0700320 // Preemptively clear scores even though the set operation could fail. We do this for
321 // safety as scores should never be compared across apps; in practice, Settings should
322 // only be allowing valid apps to be set as scorers, so failure here should be rare.
323 clearInternal();
Jeremy Joslinda11f5c2016-02-10 07:31:33 -0800324 // Get the scorer that is about to be replaced, if any, so we can notify it directly.
325 NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext);
Jeff Davidson26fd1432014-07-29 09:39:52 -0700326 boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700327 // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed
328 // then we'll attempt to restore the previous binding (if any), otherwise an attempt
329 // will be made to bind to the new scorer.
330 bindToScoringServiceIfNeeded();
Jeremy Joslinda11f5c2016-02-10 07:31:33 -0800331 if (result) { // new scorer successfully set
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700332 registerPackageMonitorIfNeeded();
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700333
Jeff Davidson26fd1432014-07-29 09:39:52 -0700334 Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
Jeremy Joslinda11f5c2016-02-10 07:31:33 -0800335 if (prevScorer != null) { // Directly notify the old scorer.
336 intent.setPackage(prevScorer.mPackageName);
337 // TODO: Need to update when we support per-user scorers. http://b/23422763
338 mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
339 }
340
341 if (packageName != null) { // Then notify the new scorer
342 intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
343 intent.setPackage(packageName);
344 // TODO: Need to update when we support per-user scorers. http://b/23422763
345 mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
346 }
Jeff Davidson26fd1432014-07-29 09:39:52 -0700347 }
348 return result;
349 } finally {
350 Binder.restoreCallingIdentity(token);
351 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700352 }
353
354 /** Clear scores. Callers are responsible for checking permissions as appropriate. */
355 private void clearInternal() {
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700356 Set<INetworkScoreCache> cachesToClear = getScoreCaches();
357
358 for (INetworkScoreCache scoreCache : cachesToClear) {
359 try {
360 scoreCache.clearScores();
361 } catch (RemoteException e) {
362 if (Log.isLoggable(TAG, Log.VERBOSE)) {
363 Log.v(TAG, "Unable to clear scores", e);
364 }
365 }
366 }
367 }
368
369 @Override
370 public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
Jeff Davidson16197792014-11-03 17:39:54 -0800371 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700372 synchronized (mScoreCaches) {
373 if (mScoreCaches.containsKey(networkType)) {
374 throw new IllegalArgumentException(
375 "Score cache already registered for type " + networkType);
376 }
377 mScoreCaches.put(networkType, scoreCache);
378 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700379 }
380
381 @Override
382 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
383 mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
Jeff Davidsonc7415532014-06-23 18:15:34 -0700384 NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700385 if (currentScorer == null) {
386 writer.println("Scoring is disabled.");
387 return;
388 }
Jeff Davidsonc7415532014-06-23 18:15:34 -0700389 writer.println("Current scorer: " + currentScorer.mPackageName);
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700390
391 for (INetworkScoreCache scoreCache : getScoreCaches()) {
392 try {
393 scoreCache.asBinder().dump(fd, args);
394 } catch (RemoteException e) {
395 writer.println("Unable to dump score cache");
396 if (Log.isLoggable(TAG, Log.VERBOSE)) {
397 Log.v(TAG, "Unable to dump score cache", e);
398 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700399 }
400 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700401 if (mServiceConnection != null) {
402 mServiceConnection.dump(fd, writer, args);
403 } else {
404 writer.println("ScoringServiceConnection: null");
405 }
406 writer.flush();
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700407 }
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700408
409 /**
410 * Returns a set of all score caches that are currently active.
411 *
412 * <p>May be used to perform an action on all score caches without potentially strange behavior
413 * if a new scorer is registered during that action's execution.
414 */
415 private Set<INetworkScoreCache> getScoreCaches() {
416 synchronized (mScoreCaches) {
417 return new HashSet<>(mScoreCaches.values());
418 }
419 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700420
421 private static class ScoringServiceConnection implements ServiceConnection {
422 private final ComponentName mComponentName;
423 private boolean mBound = false;
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700424 private boolean mConnected = false;
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700425
426 ScoringServiceConnection(ComponentName componentName) {
427 mComponentName = componentName;
428 }
429
430 void connect(Context context) {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700431 if (!mBound) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700432 Intent service = new Intent();
433 service.setComponent(mComponentName);
434 mBound = context.bindServiceAsUser(service, this,
435 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
436 UserHandle.SYSTEM);
437 if (!mBound) {
438 Log.w(TAG, "Bind call failed for " + service);
439 } else {
440 if (DBG) Log.d(TAG, "ScoringServiceConnection bound.");
441 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700442 }
443 }
444
445 void disconnect(Context context) {
446 try {
447 if (mBound) {
448 mBound = false;
449 context.unbindService(this);
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700450 if (DBG) Log.d(TAG, "ScoringServiceConnection unbound.");
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700451 }
452 } catch (RuntimeException e) {
453 Log.e(TAG, "Unbind failed.", e);
454 }
455 }
456
457 @Override
458 public void onServiceConnected(ComponentName name, IBinder service) {
459 if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700460 mConnected = true;
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700461 }
462
463 @Override
464 public void onServiceDisconnected(ComponentName name) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700465 if (DBG) {
466 Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
467 }
468 mConnected = false;
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700469 }
470
471 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700472 writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound
473 + ", connected: " + mConnected);
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700474 }
475 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700476}