blob: 1b71518dfc9fbdaabf8b671fd60395f37010537d [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;
21import android.content.SharedPreferences;
22import android.content.pm.PackageManager;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070023import android.net.INetworkScoreCache;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070024import android.net.INetworkScoreService;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070025import android.net.NetworkScorerAppManager;
Jeff Davidsonc7415532014-06-23 18:15:34 -070026import android.net.NetworkScorerAppManager.NetworkScorerAppData;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070027import android.net.ScoredNetwork;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070028import android.os.RemoteException;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070029import android.text.TextUtils;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070030import android.util.Log;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070031
32import com.android.internal.R;
33
34import java.io.FileDescriptor;
35import java.io.PrintWriter;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070036import java.util.ArrayList;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070037import java.util.HashMap;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070038import java.util.HashSet;
39import java.util.List;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070040import java.util.Map;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070041import java.util.Set;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070042
43/**
44 * Backing service for {@link android.net.NetworkScoreManager}.
45 * @hide
46 */
47public class NetworkScoreService extends INetworkScoreService.Stub {
48 private static final String TAG = "NetworkScoreService";
49
50 /** SharedPreference bit set to true after the service is first initialized. */
51 private static final String PREF_SCORING_PROVISIONED = "is_provisioned";
52
53 private final Context mContext;
54
Jeff Davidson14f1ec02014-04-29 11:58:26 -070055 private final Map<Integer, INetworkScoreCache> mScoreCaches;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070056
57 public NetworkScoreService(Context context) {
58 mContext = context;
Jeff Davidson14f1ec02014-04-29 11:58:26 -070059 mScoreCaches = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -070060 }
61
62 /** Called when the system is ready to run third-party code but before it actually does so. */
63 void systemReady() {
64 SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE);
65 if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) {
66 // On first run, we try to initialize the scorer to the one configured at build time.
67 // This will be a no-op if the scorer isn't actually valid.
68 String defaultPackage = mContext.getResources().getString(
69 R.string.config_defaultNetworkScorerPackageName);
70 if (!TextUtils.isEmpty(defaultPackage)) {
71 NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
72 }
73 prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply();
74 }
75 }
76
77 @Override
78 public boolean updateScores(ScoredNetwork[] networks) {
79 if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
80 throw new SecurityException("Caller with UID " + getCallingUid() +
81 " is not the active scorer.");
82 }
83
Jeff Davidson14f1ec02014-04-29 11:58:26 -070084 // Separate networks by type.
85 Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
Jeff Davidson6a4b2202014-04-16 17:29:40 -070086 for (ScoredNetwork network : networks) {
Jeff Davidson14f1ec02014-04-29 11:58:26 -070087 List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
88 if (networkList == null) {
89 networkList = new ArrayList<>();
90 networksByType.put(network.networkKey.type, networkList);
91 }
92 networkList.add(network);
93 }
94
95 // Pass the scores of each type down to the appropriate network scorer.
96 for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
97 INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
98 if (scoreCache != null) {
99 try {
100 scoreCache.updateScores(entry.getValue());
101 } catch (RemoteException e) {
102 if (Log.isLoggable(TAG, Log.VERBOSE)) {
103 Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
104 }
105 }
106 } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
107 Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
108 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700109 }
110
111 return true;
112 }
113
114 @Override
115 public boolean clearScores() {
116 // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETWORKS) should
117 // be allowed to flush all scores.
118 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
119 mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
120 PackageManager.PERMISSION_GRANTED) {
121 clearInternal();
122 return true;
123 } else {
124 throw new SecurityException(
125 "Caller is neither the active scorer nor the scorer manager.");
126 }
127 }
128
129 @Override
130 public boolean setActiveScorer(String packageName) {
131 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
132 // Preemptively clear scores even though the set operation could fail. We do this for safety
133 // as scores should never be compared across apps; in practice, Settings should only be
134 // allowing valid apps to be set as scorers, so failure here should be rare.
135 clearInternal();
136 return NetworkScorerAppManager.setActiveScorer(mContext, packageName);
137 }
138
139 /** Clear scores. Callers are responsible for checking permissions as appropriate. */
140 private void clearInternal() {
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700141 Set<INetworkScoreCache> cachesToClear = getScoreCaches();
142
143 for (INetworkScoreCache scoreCache : cachesToClear) {
144 try {
145 scoreCache.clearScores();
146 } catch (RemoteException e) {
147 if (Log.isLoggable(TAG, Log.VERBOSE)) {
148 Log.v(TAG, "Unable to clear scores", e);
149 }
150 }
151 }
152 }
153
154 @Override
155 public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
156 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
157 synchronized (mScoreCaches) {
158 if (mScoreCaches.containsKey(networkType)) {
159 throw new IllegalArgumentException(
160 "Score cache already registered for type " + networkType);
161 }
162 mScoreCaches.put(networkType, scoreCache);
163 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700164 }
165
166 @Override
167 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
168 mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
Jeff Davidsonc7415532014-06-23 18:15:34 -0700169 NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700170 if (currentScorer == null) {
171 writer.println("Scoring is disabled.");
172 return;
173 }
Jeff Davidsonc7415532014-06-23 18:15:34 -0700174 writer.println("Current scorer: " + currentScorer.mPackageName);
Jeff Davidson68c46fd2014-05-09 15:51:34 -0700175 writer.flush();
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700176
177 for (INetworkScoreCache scoreCache : getScoreCaches()) {
178 try {
179 scoreCache.asBinder().dump(fd, args);
180 } catch (RemoteException e) {
181 writer.println("Unable to dump score cache");
182 if (Log.isLoggable(TAG, Log.VERBOSE)) {
183 Log.v(TAG, "Unable to dump score cache", e);
184 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700185 }
186 }
187 }
Jeff Davidson14f1ec02014-04-29 11:58:26 -0700188
189 /**
190 * Returns a set of all score caches that are currently active.
191 *
192 * <p>May be used to perform an action on all score caches without potentially strange behavior
193 * if a new scorer is registered during that action's execution.
194 */
195 private Set<INetworkScoreCache> getScoreCaches() {
196 synchronized (mScoreCaches) {
197 return new HashSet<>(mScoreCaches.values());
198 }
199 }
Jeff Davidson6a4b2202014-04-16 17:29:40 -0700200}