NetworkScoreService.java revision c741553644f8b19c63938ab9e36af1721c2cfa34
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.NetworkScorerAppManager.NetworkScorerAppData; 27import android.net.ScoredNetwork; 28import android.os.RemoteException; 29import android.text.TextUtils; 30import android.util.Log; 31 32import com.android.internal.R; 33 34import java.io.FileDescriptor; 35import java.io.PrintWriter; 36import java.util.ArrayList; 37import java.util.HashMap; 38import java.util.HashSet; 39import java.util.List; 40import java.util.Map; 41import java.util.Set; 42 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 55 private final Map<Integer, INetworkScoreCache> mScoreCaches; 56 57 public NetworkScoreService(Context context) { 58 mContext = context; 59 mScoreCaches = new HashMap<>(); 60 } 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 84 // Separate networks by type. 85 Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>(); 86 for (ScoredNetwork network : networks) { 87 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 } 109 } 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() { 141 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 } 164 } 165 166 @Override 167 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 168 mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG); 169 NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext); 170 if (currentScorer == null) { 171 writer.println("Scoring is disabled."); 172 return; 173 } 174 writer.println("Current scorer: " + currentScorer.mPackageName); 175 writer.flush(); 176 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 } 185 } 186 } 187 } 188 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 } 200} 201