blob: 83d6344c95b0d93abda11d6c4a6c8f6c955d9513 [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;
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070044import com.android.internal.content.PackageMonitor;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070045
46import java.io.FileDescriptor;
47import java.io.PrintWriter;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070048import java.util.ArrayList;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070049import java.util.HashMap;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070050import java.util.HashSet;
51import java.util.List;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070052import java.util.Map;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070053import java.util.Set;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070054
55/**
56 * Backing service for {@link android.net.NetworkScoreManager}.
57 * @hide
58 */
59public class NetworkScoreService extends INetworkScoreService.Stub {
60 private static final String TAG = "NetworkScoreService";
Jeremy Joslindd251ef2016-03-14 11:17:41 -070061 private static final boolean DBG = false;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070062
Jeff Davidson6a4b2202014-04-16 17:29:40 -070063 private final Context mContext;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070064 private final Map<Integer, INetworkScoreCache> mScoreCaches;
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070065 /** Lock used to update mPackageMonitor when scorer package changes occur. */
66 private final Object mPackageMonitorLock = new Object[0];
Jeff Davidson7842f642014-11-23 13:48:12 -080067
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070068 @GuardedBy("mPackageMonitorLock")
69 private NetworkScorerPackageMonitor mPackageMonitor;
Jeremy Joslindd251ef2016-03-14 11:17:41 -070070 private ScoringServiceConnection mServiceConnection;
Jeff Davidson7842f642014-11-23 13:48:12 -080071
Jeremy Joslin967b5812016-06-02 07:58:14 -070072 private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
73 @Override
74 public void onReceive(Context context, Intent intent) {
75 final String action = intent.getAction();
76 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
77 if (DBG) Log.d(TAG, "Received " + action + " for userId " + userId);
78 if (userId == UserHandle.USER_NULL) return;
79
80 if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
81 onUserUnlocked(userId);
82 }
83 }
84 };
85
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070086 /**
87 * Clears scores when the active scorer package is no longer valid and
88 * manages the service connection.
89 */
90 private class NetworkScorerPackageMonitor extends PackageMonitor {
Jeff Davidson7842f642014-11-23 13:48:12 -080091 final String mRegisteredPackage;
92
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070093 private NetworkScorerPackageMonitor(String mRegisteredPackage) {
94 this.mRegisteredPackage = mRegisteredPackage;
Jeff Davidson7842f642014-11-23 13:48:12 -080095 }
96
97 @Override
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -070098 public void onPackageAdded(String packageName, int uid) {
99 evaluateBinding(packageName, true /* forceUnbind */);
100 }
101
102 @Override
103 public void onPackageRemoved(String packageName, int uid) {
104 evaluateBinding(packageName, true /* forceUnbind */);
105 }
106
107 @Override
108 public void onPackageModified(String packageName) {
109 evaluateBinding(packageName, false /* forceUnbind */);
110 }
111
112 @Override
113 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
114 if (doit) { // "doit" means the force stop happened instead of just being queried for.
115 for (String packageName : packages) {
116 evaluateBinding(packageName, true /* forceUnbind */);
117 }
118 }
119 return super.onHandleForceStop(intent, packages, uid, doit);
120 }
121
122 @Override
123 public void onPackageUpdateFinished(String packageName, int uid) {
124 evaluateBinding(packageName, true /* forceUnbind */);
125 }
126
127 private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
128 if (mRegisteredPackage.equals(scorerPackageName)) {
129 if (DBG) {
130 Log.d(TAG, "Evaluating binding for: " + scorerPackageName
131 + ", forceUnbind=" + forceUnbind);
132 }
133 final NetworkScorerAppData activeScorer =
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700134 NetworkScorerAppManager.getActiveScorer(mContext);
135 if (activeScorer == null) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700136 // Package change has invalidated a scorer, this will also unbind any service
137 // connection.
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700138 Log.i(TAG, "Package " + mRegisteredPackage +
139 " is no longer valid, disabling scoring.");
140 setScorerInternal(null);
141 } else if (activeScorer.mScoringServiceClassName == null) {
142 // The scoring service is not available, make sure it's unbound.
143 unbindFromScoringServiceIfNeeded();
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700144 } else { // The scoring service changed in some way.
145 if (forceUnbind) {
146 unbindFromScoringServiceIfNeeded();
147 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700148 bindToScoringServiceIfNeeded(activeScorer);
149 }
Jeff Davidson7842f642014-11-23 13:48:12 -0800150 }
151 }
152 }
153
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700154 public NetworkScoreService(Context context) {
155 mContext = context;
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700156 mScoreCaches = new HashMap<>();
Jeremy Joslin967b5812016-06-02 07:58:14 -0700157 IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
158 // TODO: Need to update when we support per-user scorers. http://b/23422763
159 mContext.registerReceiverAsUser(
160 mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
161 null /* scheduler */);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700162 }
163
164 /** Called when the system is ready to run third-party code but before it actually does so. */
165 void systemReady() {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700166 if (DBG) Log.d(TAG, "systemReady");
Jeff Davidson56f9f732014-08-14 16:47:23 -0700167 ContentResolver cr = mContext.getContentResolver();
168 if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700169 // On first run, we try to initialize the scorer to the one configured at build time.
170 // This will be a no-op if the scorer isn't actually valid.
171 String defaultPackage = mContext.getResources().getString(
172 R.string.config_defaultNetworkScorerPackageName);
173 if (!TextUtils.isEmpty(defaultPackage)) {
174 NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
175 }
Jeff Davidson56f9f732014-08-14 16:47:23 -0700176 Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700177 }
Jeff Davidson7842f642014-11-23 13:48:12 -0800178
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700179 registerPackageMonitorIfNeeded();
Jeff Davidson7842f642014-11-23 13:48:12 -0800180 }
181
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700182 /** Called when the system is ready for us to start third-party code. */
183 void systemRunning() {
184 if (DBG) Log.d(TAG, "systemRunning");
185 bindToScoringServiceIfNeeded();
186 }
187
Jeremy Joslin967b5812016-06-02 07:58:14 -0700188 private void onUserUnlocked(int userId) {
189 registerPackageMonitorIfNeeded();
190 bindToScoringServiceIfNeeded();
191 }
192
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700193 private void registerPackageMonitorIfNeeded() {
194 if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
Jeff Davidson7842f642014-11-23 13:48:12 -0800195 NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext);
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700196 synchronized (mPackageMonitorLock) {
197 // Unregister the current monitor if needed.
198 if (mPackageMonitor != null) {
199 if (DBG) {
200 Log.d(TAG, "Unregistering package monitor for "
201 + mPackageMonitor.mRegisteredPackage);
Jeff Davidson7842f642014-11-23 13:48:12 -0800202 }
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700203 mPackageMonitor.unregister();
204 mPackageMonitor = null;
Jeff Davidson7842f642014-11-23 13:48:12 -0800205 }
206
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700207 // Create and register the monitor if a scorer is active.
Jeff Davidson7842f642014-11-23 13:48:12 -0800208 if (scorer != null) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700209 mPackageMonitor = new NetworkScorerPackageMonitor(scorer.mPackageName);
Xiaohui Chene4de5a02015-09-22 15:33:31 -0700210 // TODO: Need to update when we support per-user scorers. http://b/23422763
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700211 mPackageMonitor.register(mContext, null /* thread */, UserHandle.SYSTEM,
212 false /* externalStorage */);
213 if (DBG) {
214 Log.d(TAG, "Registered package monitor for "
215 + mPackageMonitor.mRegisteredPackage);
Jeff Davidson7842f642014-11-23 13:48:12 -0800216 }
217 }
218 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700219 }
220
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700221 private void bindToScoringServiceIfNeeded() {
222 if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded");
223 NetworkScorerAppData scorerData = NetworkScorerAppManager.getActiveScorer(mContext);
224 bindToScoringServiceIfNeeded(scorerData);
225 }
226
227 private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) {
228 if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")");
229 if (scorerData != null && scorerData.mScoringServiceClassName != null) {
230 ComponentName componentName =
231 new ComponentName(scorerData.mPackageName, scorerData.mScoringServiceClassName);
232 // If we're connected to a different component then drop it.
233 if (mServiceConnection != null
234 && !mServiceConnection.mComponentName.equals(componentName)) {
235 unbindFromScoringServiceIfNeeded();
236 }
237
238 // If we're not connected at all then create a new connection.
239 if (mServiceConnection == null) {
240 mServiceConnection = new ScoringServiceConnection(componentName);
241 }
242
243 // Make sure the connection is connected (idempotent)
244 mServiceConnection.connect(mContext);
Jeremy Joslin967b5812016-06-02 07:58:14 -0700245 } else { // otherwise make sure it isn't bound.
246 unbindFromScoringServiceIfNeeded();
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700247 }
248 }
249
250 private void unbindFromScoringServiceIfNeeded() {
251 if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded");
252 if (mServiceConnection != null) {
253 mServiceConnection.disconnect(mContext);
254 }
255 mServiceConnection = null;
256 }
257
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700258 @Override
259 public boolean updateScores(ScoredNetwork[] networks) {
260 if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
261 throw new SecurityException("Caller with UID " + getCallingUid() +
262 " is not the active scorer.");
263 }
264
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700265 // Separate networks by type.
266 Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700267 for (ScoredNetwork network : networks) {
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700268 List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
269 if (networkList == null) {
270 networkList = new ArrayList<>();
271 networksByType.put(network.networkKey.type, networkList);
272 }
273 networkList.add(network);
274 }
275
276 // Pass the scores of each type down to the appropriate network scorer.
277 for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
278 INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
279 if (scoreCache != null) {
280 try {
281 scoreCache.updateScores(entry.getValue());
282 } catch (RemoteException e) {
283 if (Log.isLoggable(TAG, Log.VERBOSE)) {
284 Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
285 }
286 }
287 } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
288 Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
289 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700290 }
291
292 return true;
293 }
294
295 @Override
296 public boolean clearScores() {
Jeff Davidson16197792014-11-03 17:39:54 -0800297 // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
298 // should be allowed to flush all scores.
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700299 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
Jeff Davidson16197792014-11-03 17:39:54 -0800300 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700301 PackageManager.PERMISSION_GRANTED) {
302 clearInternal();
303 return true;
304 } else {
305 throw new SecurityException(
306 "Caller is neither the active scorer nor the scorer manager.");
307 }
308 }
309
310 @Override
311 public boolean setActiveScorer(String packageName) {
Jeff Davidsone56f2bb2014-11-05 11:14:19 -0800312 // TODO: For now, since SCORE_NETWORKS requires an app to be privileged, we allow such apps
313 // to directly set the scorer app rather than having to use the consent dialog. The
314 // assumption is that anyone bundling a scorer app with the system is trusted by the OEM to
315 // do the right thing and not enable this feature without explaining it to the user.
316 // In the future, should this API be opened to 3p apps, we will need to lock this down and
317 // figure out another way to streamline the UX.
318
319 // mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
320 mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
321
Jeff Davidson26fd1432014-07-29 09:39:52 -0700322 return setScorerInternal(packageName);
323 }
324
325 @Override
326 public void disableScoring() {
Jeff Davidson16197792014-11-03 17:39:54 -0800327 // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
328 // should be allowed to disable scoring.
Jeff Davidson26fd1432014-07-29 09:39:52 -0700329 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
Jeff Davidson16197792014-11-03 17:39:54 -0800330 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
Jeff Davidson26fd1432014-07-29 09:39:52 -0700331 PackageManager.PERMISSION_GRANTED) {
332 // The return value is discarded here because at this point, the call should always
333 // succeed. The only reason for failure is if the new package is not a valid scorer, but
334 // we're disabling scoring altogether here.
335 setScorerInternal(null /* packageName */);
336 } else {
337 throw new SecurityException(
338 "Caller is neither the active scorer nor the scorer manager.");
Jeff Davidsonb096bdc2014-07-01 12:29:11 -0700339 }
Jeff Davidson26fd1432014-07-29 09:39:52 -0700340 }
341
342 /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
343 private boolean setScorerInternal(String packageName) {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700344 if (DBG) Log.d(TAG, "setScorerInternal(" + packageName + ")");
Jeff Davidson26fd1432014-07-29 09:39:52 -0700345 long token = Binder.clearCallingIdentity();
346 try {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700347 unbindFromScoringServiceIfNeeded();
Jeff Davidson26fd1432014-07-29 09:39:52 -0700348 // Preemptively clear scores even though the set operation could fail. We do this for
349 // safety as scores should never be compared across apps; in practice, Settings should
350 // only be allowing valid apps to be set as scorers, so failure here should be rare.
351 clearInternal();
Jeremy Joslinda11f5c2016-02-10 07:31:33 -0800352 // Get the scorer that is about to be replaced, if any, so we can notify it directly.
353 NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext);
Jeff Davidson26fd1432014-07-29 09:39:52 -0700354 boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700355 // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed
356 // then we'll attempt to restore the previous binding (if any), otherwise an attempt
357 // will be made to bind to the new scorer.
358 bindToScoringServiceIfNeeded();
Jeremy Joslinda11f5c2016-02-10 07:31:33 -0800359 if (result) { // new scorer successfully set
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700360 registerPackageMonitorIfNeeded();
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700361
Jeff Davidson26fd1432014-07-29 09:39:52 -0700362 Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
Jeremy Joslinda11f5c2016-02-10 07:31:33 -0800363 if (prevScorer != null) { // Directly notify the old scorer.
364 intent.setPackage(prevScorer.mPackageName);
365 // TODO: Need to update when we support per-user scorers. http://b/23422763
366 mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
367 }
368
369 if (packageName != null) { // Then notify the new scorer
370 intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
371 intent.setPackage(packageName);
372 // TODO: Need to update when we support per-user scorers. http://b/23422763
373 mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
374 }
Jeff Davidson26fd1432014-07-29 09:39:52 -0700375 }
376 return result;
377 } finally {
378 Binder.restoreCallingIdentity(token);
379 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700380 }
381
382 /** Clear scores. Callers are responsible for checking permissions as appropriate. */
383 private void clearInternal() {
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700384 Set<INetworkScoreCache> cachesToClear = getScoreCaches();
385
386 for (INetworkScoreCache scoreCache : cachesToClear) {
387 try {
388 scoreCache.clearScores();
389 } catch (RemoteException e) {
390 if (Log.isLoggable(TAG, Log.VERBOSE)) {
391 Log.v(TAG, "Unable to clear scores", e);
392 }
393 }
394 }
395 }
396
397 @Override
398 public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
Jeff Davidson16197792014-11-03 17:39:54 -0800399 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700400 synchronized (mScoreCaches) {
401 if (mScoreCaches.containsKey(networkType)) {
402 throw new IllegalArgumentException(
403 "Score cache already registered for type " + networkType);
404 }
405 mScoreCaches.put(networkType, scoreCache);
406 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700407 }
408
409 @Override
410 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
411 mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
Jeff Davidsonc7415532014-06-23 18:15:34 -0700412 NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700413 if (currentScorer == null) {
414 writer.println("Scoring is disabled.");
415 return;
416 }
Jeff Davidsonc7415532014-06-23 18:15:34 -0700417 writer.println("Current scorer: " + currentScorer.mPackageName);
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700418
419 for (INetworkScoreCache scoreCache : getScoreCaches()) {
420 try {
421 scoreCache.asBinder().dump(fd, args);
422 } catch (RemoteException e) {
423 writer.println("Unable to dump score cache");
424 if (Log.isLoggable(TAG, Log.VERBOSE)) {
425 Log.v(TAG, "Unable to dump score cache", e);
426 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700427 }
428 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700429 if (mServiceConnection != null) {
430 mServiceConnection.dump(fd, writer, args);
431 } else {
432 writer.println("ScoringServiceConnection: null");
433 }
434 writer.flush();
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700435 }
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700436
437 /**
438 * Returns a set of all score caches that are currently active.
439 *
440 * <p>May be used to perform an action on all score caches without potentially strange behavior
441 * if a new scorer is registered during that action's execution.
442 */
443 private Set<INetworkScoreCache> getScoreCaches() {
444 synchronized (mScoreCaches) {
445 return new HashSet<>(mScoreCaches.values());
446 }
447 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700448
449 private static class ScoringServiceConnection implements ServiceConnection {
450 private final ComponentName mComponentName;
451 private boolean mBound = false;
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700452 private boolean mConnected = false;
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700453
454 ScoringServiceConnection(ComponentName componentName) {
455 mComponentName = componentName;
456 }
457
458 void connect(Context context) {
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700459 if (!mBound) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700460 Intent service = new Intent();
461 service.setComponent(mComponentName);
462 mBound = context.bindServiceAsUser(service, this,
463 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
464 UserHandle.SYSTEM);
465 if (!mBound) {
466 Log.w(TAG, "Bind call failed for " + service);
467 } else {
468 if (DBG) Log.d(TAG, "ScoringServiceConnection bound.");
469 }
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700470 }
471 }
472
473 void disconnect(Context context) {
474 try {
475 if (mBound) {
476 mBound = false;
477 context.unbindService(this);
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700478 if (DBG) Log.d(TAG, "ScoringServiceConnection unbound.");
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700479 }
480 } catch (RuntimeException e) {
481 Log.e(TAG, "Unbind failed.", e);
482 }
483 }
484
485 @Override
486 public void onServiceConnected(ComponentName name, IBinder service) {
487 if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700488 mConnected = true;
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700489 }
490
491 @Override
492 public void onServiceDisconnected(ComponentName name) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700493 if (DBG) {
494 Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
495 }
496 mConnected = false;
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700497 }
498
499 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
Jeremy Joslin1ec8cd952016-05-26 15:28:48 -0700500 writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound
501 + ", connected: " + mConnected);
Jeremy Joslindd251ef2016-03-14 11:17:41 -0700502 }
503 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700504}