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