blob: 3e2f260ac84824f3a4fa26eaa8d1d7e785bc07a0 [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;
20import android.content.Context;
Jeff Davidsonb096bdc2014-07-01 12:29:11 -070021import android.content.Intent;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070022import android.content.SharedPreferences;
23import android.content.pm.PackageManager;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070024import android.net.INetworkScoreCache;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070025import android.net.INetworkScoreService;
Jeff Davidsonb096bdc2014-07-01 12:29:11 -070026import android.net.NetworkScoreManager;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070027import android.net.NetworkScorerAppManager;
Jeff Davidsonc7415532014-06-23 18:15:34 -070028import android.net.NetworkScorerAppManager.NetworkScorerAppData;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070029import android.net.ScoredNetwork;
Jeff Davidson26fd1432014-07-29 09:39:52 -070030import android.os.Binder;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070031import android.os.RemoteException;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070032import android.text.TextUtils;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070033import android.util.Log;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070034
35import com.android.internal.R;
36
37import java.io.FileDescriptor;
38import java.io.PrintWriter;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070039import java.util.ArrayList;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070040import java.util.HashMap;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070041import java.util.HashSet;
42import java.util.List;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070043import java.util.Map;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070044import java.util.Set;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070045
46/**
47 * Backing service for {@link android.net.NetworkScoreManager}.
48 * @hide
49 */
50public class NetworkScoreService extends INetworkScoreService.Stub {
51 private static final String TAG = "NetworkScoreService";
52
53 /** SharedPreference bit set to true after the service is first initialized. */
54 private static final String PREF_SCORING_PROVISIONED = "is_provisioned";
55
56 private final Context mContext;
57
Jeff Davidson14f1ec02014-04-29 11:58:26 -070058 private final Map<Integer, INetworkScoreCache> mScoreCaches;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070059
60 public NetworkScoreService(Context context) {
61 mContext = context;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070062 mScoreCaches = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -070063 }
64
65 /** Called when the system is ready to run third-party code but before it actually does so. */
66 void systemReady() {
67 SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE);
68 if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) {
69 // On first run, we try to initialize the scorer to the one configured at build time.
70 // This will be a no-op if the scorer isn't actually valid.
71 String defaultPackage = mContext.getResources().getString(
72 R.string.config_defaultNetworkScorerPackageName);
73 if (!TextUtils.isEmpty(defaultPackage)) {
74 NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
75 }
76 prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply();
77 }
78 }
79
80 @Override
81 public boolean updateScores(ScoredNetwork[] networks) {
82 if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
83 throw new SecurityException("Caller with UID " + getCallingUid() +
84 " is not the active scorer.");
85 }
86
Jeff Davidson14f1ec02014-04-29 11:58:26 -070087 // Separate networks by type.
88 Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -070089 for (ScoredNetwork network : networks) {
Jeff Davidson14f1ec02014-04-29 11:58:26 -070090 List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
91 if (networkList == null) {
92 networkList = new ArrayList<>();
93 networksByType.put(network.networkKey.type, networkList);
94 }
95 networkList.add(network);
96 }
97
98 // Pass the scores of each type down to the appropriate network scorer.
99 for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
100 INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
101 if (scoreCache != null) {
102 try {
103 scoreCache.updateScores(entry.getValue());
104 } catch (RemoteException e) {
105 if (Log.isLoggable(TAG, Log.VERBOSE)) {
106 Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
107 }
108 }
109 } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
110 Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
111 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700112 }
113
114 return true;
115 }
116
117 @Override
118 public boolean clearScores() {
119 // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETWORKS) should
120 // be allowed to flush all scores.
121 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
122 mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
123 PackageManager.PERMISSION_GRANTED) {
124 clearInternal();
125 return true;
126 } else {
127 throw new SecurityException(
128 "Caller is neither the active scorer nor the scorer manager.");
129 }
130 }
131
132 @Override
133 public boolean setActiveScorer(String packageName) {
134 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
Jeff Davidson26fd1432014-07-29 09:39:52 -0700135 return setScorerInternal(packageName);
136 }
137
138 @Override
139 public void disableScoring() {
140 // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETOWRKS) should
141 // be allowed to disable scoring.
142 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
143 mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
144 PackageManager.PERMISSION_GRANTED) {
145 // The return value is discarded here because at this point, the call should always
146 // succeed. The only reason for failure is if the new package is not a valid scorer, but
147 // we're disabling scoring altogether here.
148 setScorerInternal(null /* packageName */);
149 } else {
150 throw new SecurityException(
151 "Caller is neither the active scorer nor the scorer manager.");
Jeff Davidsonb096bdc2014-07-01 12:29:11 -0700152 }
Jeff Davidson26fd1432014-07-29 09:39:52 -0700153 }
154
155 /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
156 private boolean setScorerInternal(String packageName) {
157 long token = Binder.clearCallingIdentity();
158 try {
159 // Preemptively clear scores even though the set operation could fail. We do this for
160 // safety as scores should never be compared across apps; in practice, Settings should
161 // only be allowing valid apps to be set as scorers, so failure here should be rare.
162 clearInternal();
163 boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
164 if (result) {
165 Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
166 intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
167 mContext.sendBroadcast(intent);
168 }
169 return result;
170 } finally {
171 Binder.restoreCallingIdentity(token);
172 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700173 }
174
175 /** Clear scores. Callers are responsible for checking permissions as appropriate. */
176 private void clearInternal() {
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700177 Set<INetworkScoreCache> cachesToClear = getScoreCaches();
178
179 for (INetworkScoreCache scoreCache : cachesToClear) {
180 try {
181 scoreCache.clearScores();
182 } catch (RemoteException e) {
183 if (Log.isLoggable(TAG, Log.VERBOSE)) {
184 Log.v(TAG, "Unable to clear scores", e);
185 }
186 }
187 }
188 }
189
190 @Override
191 public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
192 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
193 synchronized (mScoreCaches) {
194 if (mScoreCaches.containsKey(networkType)) {
195 throw new IllegalArgumentException(
196 "Score cache already registered for type " + networkType);
197 }
198 mScoreCaches.put(networkType, scoreCache);
199 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700200 }
201
202 @Override
203 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
204 mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
Jeff Davidsonc7415532014-06-23 18:15:34 -0700205 NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700206 if (currentScorer == null) {
207 writer.println("Scoring is disabled.");
208 return;
209 }
Jeff Davidsonc7415532014-06-23 18:15:34 -0700210 writer.println("Current scorer: " + currentScorer.mPackageName);
Jeff Davidson68c46fd2014-05-09 15:51:34 -0700211 writer.flush();
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700212
213 for (INetworkScoreCache scoreCache : getScoreCaches()) {
214 try {
215 scoreCache.asBinder().dump(fd, args);
216 } catch (RemoteException e) {
217 writer.println("Unable to dump score cache");
218 if (Log.isLoggable(TAG, Log.VERBOSE)) {
219 Log.v(TAG, "Unable to dump score cache", e);
220 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700221 }
222 }
223 }
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700224
225 /**
226 * Returns a set of all score caches that are currently active.
227 *
228 * <p>May be used to perform an action on all score caches without potentially strange behavior
229 * if a new scorer is registered during that action's execution.
230 */
231 private Set<INetworkScoreCache> getScoreCaches() {
232 synchronized (mScoreCaches) {
233 return new HashSet<>(mScoreCaches.values());
234 }
235 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700236}