blob: 738917fc39b2b59574e43c3eb6563c60cd739736 [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;
Jeff Davidson56f9f732014-08-14 16:47:23 -070020import android.content.ContentResolver;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070021import android.content.Context;
Jeff Davidsonb096bdc2014-07-01 12:29:11 -070022import android.content.Intent;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070023import 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 Davidsonac7285d2014-08-08 15:12:47 -070032import android.os.UserHandle;
Jeff Davidson56f9f732014-08-14 16:47:23 -070033import android.provider.Settings;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070034import android.text.TextUtils;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070035import android.util.Log;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070036
37import com.android.internal.R;
38
39import java.io.FileDescriptor;
40import java.io.PrintWriter;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070041import java.util.ArrayList;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070042import java.util.HashMap;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070043import java.util.HashSet;
44import java.util.List;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070045import java.util.Map;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070046import java.util.Set;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070047
48/**
49 * Backing service for {@link android.net.NetworkScoreManager}.
50 * @hide
51 */
52public class NetworkScoreService extends INetworkScoreService.Stub {
53 private static final String TAG = "NetworkScoreService";
54
Jeff Davidson6a4b2202014-04-16 17:29:40 -070055 private final Context mContext;
56
Jeff Davidson14f1ec02014-04-29 11:58:26 -070057 private final Map<Integer, INetworkScoreCache> mScoreCaches;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070058
59 public NetworkScoreService(Context context) {
60 mContext = context;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070061 mScoreCaches = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -070062 }
63
64 /** Called when the system is ready to run third-party code but before it actually does so. */
65 void systemReady() {
Jeff Davidson56f9f732014-08-14 16:47:23 -070066 ContentResolver cr = mContext.getContentResolver();
67 if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
Jeff Davidson6a4b2202014-04-16 17:29:40 -070068 // On first run, we try to initialize the scorer to the one configured at build time.
69 // This will be a no-op if the scorer isn't actually valid.
70 String defaultPackage = mContext.getResources().getString(
71 R.string.config_defaultNetworkScorerPackageName);
72 if (!TextUtils.isEmpty(defaultPackage)) {
73 NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
74 }
Jeff Davidson56f9f732014-08-14 16:47:23 -070075 Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
Jeff Davidson6a4b2202014-04-16 17:29:40 -070076 }
77 }
78
79 @Override
80 public boolean updateScores(ScoredNetwork[] networks) {
81 if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
82 throw new SecurityException("Caller with UID " + getCallingUid() +
83 " is not the active scorer.");
84 }
85
Jeff Davidson14f1ec02014-04-29 11:58:26 -070086 // Separate networks by type.
87 Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -070088 for (ScoredNetwork network : networks) {
Jeff Davidson14f1ec02014-04-29 11:58:26 -070089 List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
90 if (networkList == null) {
91 networkList = new ArrayList<>();
92 networksByType.put(network.networkKey.type, networkList);
93 }
94 networkList.add(network);
95 }
96
97 // Pass the scores of each type down to the appropriate network scorer.
98 for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
99 INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
100 if (scoreCache != null) {
101 try {
102 scoreCache.updateScores(entry.getValue());
103 } catch (RemoteException e) {
104 if (Log.isLoggable(TAG, Log.VERBOSE)) {
105 Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
106 }
107 }
108 } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
109 Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
110 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700111 }
112
113 return true;
114 }
115
116 @Override
117 public boolean clearScores() {
Jeff Davidson16197792014-11-03 17:39:54 -0800118 // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
119 // should be allowed to flush all scores.
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700120 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
Jeff Davidson16197792014-11-03 17:39:54 -0800121 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700122 PackageManager.PERMISSION_GRANTED) {
123 clearInternal();
124 return true;
125 } else {
126 throw new SecurityException(
127 "Caller is neither the active scorer nor the scorer manager.");
128 }
129 }
130
131 @Override
132 public boolean setActiveScorer(String packageName) {
Jeff Davidsone56f2bb2014-11-05 11:14:19 -0800133 // TODO: For now, since SCORE_NETWORKS requires an app to be privileged, we allow such apps
134 // to directly set the scorer app rather than having to use the consent dialog. The
135 // assumption is that anyone bundling a scorer app with the system is trusted by the OEM to
136 // do the right thing and not enable this feature without explaining it to the user.
137 // In the future, should this API be opened to 3p apps, we will need to lock this down and
138 // figure out another way to streamline the UX.
139
140 // mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
141 mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
142
Jeff Davidson26fd1432014-07-29 09:39:52 -0700143 return setScorerInternal(packageName);
144 }
145
146 @Override
147 public void disableScoring() {
Jeff Davidson16197792014-11-03 17:39:54 -0800148 // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
149 // should be allowed to disable scoring.
Jeff Davidson26fd1432014-07-29 09:39:52 -0700150 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
Jeff Davidson16197792014-11-03 17:39:54 -0800151 mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
Jeff Davidson26fd1432014-07-29 09:39:52 -0700152 PackageManager.PERMISSION_GRANTED) {
153 // The return value is discarded here because at this point, the call should always
154 // succeed. The only reason for failure is if the new package is not a valid scorer, but
155 // we're disabling scoring altogether here.
156 setScorerInternal(null /* packageName */);
157 } else {
158 throw new SecurityException(
159 "Caller is neither the active scorer nor the scorer manager.");
Jeff Davidsonb096bdc2014-07-01 12:29:11 -0700160 }
Jeff Davidson26fd1432014-07-29 09:39:52 -0700161 }
162
163 /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
164 private boolean setScorerInternal(String packageName) {
165 long token = Binder.clearCallingIdentity();
166 try {
167 // Preemptively clear scores even though the set operation could fail. We do this for
168 // safety as scores should never be compared across apps; in practice, Settings should
169 // only be allowing valid apps to be set as scorers, so failure here should be rare.
170 clearInternal();
171 boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
172 if (result) {
173 Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
174 intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
Jeff Davidsonac7285d2014-08-08 15:12:47 -0700175 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
Jeff Davidson26fd1432014-07-29 09:39:52 -0700176 }
177 return result;
178 } finally {
179 Binder.restoreCallingIdentity(token);
180 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700181 }
182
183 /** Clear scores. Callers are responsible for checking permissions as appropriate. */
184 private void clearInternal() {
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700185 Set<INetworkScoreCache> cachesToClear = getScoreCaches();
186
187 for (INetworkScoreCache scoreCache : cachesToClear) {
188 try {
189 scoreCache.clearScores();
190 } catch (RemoteException e) {
191 if (Log.isLoggable(TAG, Log.VERBOSE)) {
192 Log.v(TAG, "Unable to clear scores", e);
193 }
194 }
195 }
196 }
197
198 @Override
199 public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
Jeff Davidson16197792014-11-03 17:39:54 -0800200 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700201 synchronized (mScoreCaches) {
202 if (mScoreCaches.containsKey(networkType)) {
203 throw new IllegalArgumentException(
204 "Score cache already registered for type " + networkType);
205 }
206 mScoreCaches.put(networkType, scoreCache);
207 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700208 }
209
210 @Override
211 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
212 mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
Jeff Davidsonc7415532014-06-23 18:15:34 -0700213 NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700214 if (currentScorer == null) {
215 writer.println("Scoring is disabled.");
216 return;
217 }
Jeff Davidsonc7415532014-06-23 18:15:34 -0700218 writer.println("Current scorer: " + currentScorer.mPackageName);
Jeff Davidson68c46fd2014-05-09 15:51:34 -0700219 writer.flush();
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700220
221 for (INetworkScoreCache scoreCache : getScoreCaches()) {
222 try {
223 scoreCache.asBinder().dump(fd, args);
224 } catch (RemoteException e) {
225 writer.println("Unable to dump score cache");
226 if (Log.isLoggable(TAG, Log.VERBOSE)) {
227 Log.v(TAG, "Unable to dump score cache", e);
228 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700229 }
230 }
231 }
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700232
233 /**
234 * Returns a set of all score caches that are currently active.
235 *
236 * <p>May be used to perform an action on all score caches without potentially strange behavior
237 * if a new scorer is registered during that action's execution.
238 */
239 private Set<INetworkScoreCache> getScoreCaches() {
240 synchronized (mScoreCaches) {
241 return new HashSet<>(mScoreCaches.values());
242 }
243 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700244}