NetworkScoreService.java revision b096bdceaf2a4becffd2d930a870ccb57bfbe99c
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.Intent; 22import android.content.SharedPreferences; 23import android.content.pm.PackageManager; 24import android.net.INetworkScoreCache; 25import android.net.INetworkScoreService; 26import android.net.NetworkScoreManager; 27import android.net.NetworkScorerAppManager; 28import android.net.NetworkScorerAppManager.NetworkScorerAppData; 29import android.net.ScoredNetwork; 30import android.os.RemoteException; 31import android.text.TextUtils; 32import android.util.Log; 33 34import com.android.internal.R; 35 36import java.io.FileDescriptor; 37import java.io.PrintWriter; 38import java.util.ArrayList; 39import java.util.HashMap; 40import java.util.HashSet; 41import java.util.List; 42import java.util.Map; 43import java.util.Set; 44 45/** 46 * Backing service for {@link android.net.NetworkScoreManager}. 47 * @hide 48 */ 49public class NetworkScoreService extends INetworkScoreService.Stub { 50 private static final String TAG = "NetworkScoreService"; 51 52 /** SharedPreference bit set to true after the service is first initialized. */ 53 private static final String PREF_SCORING_PROVISIONED = "is_provisioned"; 54 55 private final Context mContext; 56 57 private final Map<Integer, INetworkScoreCache> mScoreCaches; 58 59 public NetworkScoreService(Context context) { 60 mContext = context; 61 mScoreCaches = new HashMap<>(); 62 } 63 64 /** Called when the system is ready to run third-party code but before it actually does so. */ 65 void systemReady() { 66 SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE); 67 if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) { 68 // 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 } 75 prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply(); 76 } 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 86 // Separate networks by type. 87 Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>(); 88 for (ScoredNetwork network : networks) { 89 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 } 111 } 112 113 return true; 114 } 115 116 @Override 117 public boolean clearScores() { 118 // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETWORKS) should 119 // be allowed to flush all scores. 120 if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) || 121 mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) == 122 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) { 133 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG); 134 // Preemptively clear scores even though the set operation could fail. We do this for safety 135 // as scores should never be compared across apps; in practice, Settings should only be 136 // allowing valid apps to be set as scorers, so failure here should be rare. 137 clearInternal(); 138 boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName); 139 if (result) { 140 Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED); 141 intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName); 142 mContext.sendBroadcast(intent); 143 } 144 return result; 145 } 146 147 /** Clear scores. Callers are responsible for checking permissions as appropriate. */ 148 private void clearInternal() { 149 Set<INetworkScoreCache> cachesToClear = getScoreCaches(); 150 151 for (INetworkScoreCache scoreCache : cachesToClear) { 152 try { 153 scoreCache.clearScores(); 154 } catch (RemoteException e) { 155 if (Log.isLoggable(TAG, Log.VERBOSE)) { 156 Log.v(TAG, "Unable to clear scores", e); 157 } 158 } 159 } 160 } 161 162 @Override 163 public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) { 164 mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG); 165 synchronized (mScoreCaches) { 166 if (mScoreCaches.containsKey(networkType)) { 167 throw new IllegalArgumentException( 168 "Score cache already registered for type " + networkType); 169 } 170 mScoreCaches.put(networkType, scoreCache); 171 } 172 } 173 174 @Override 175 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 176 mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG); 177 NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext); 178 if (currentScorer == null) { 179 writer.println("Scoring is disabled."); 180 return; 181 } 182 writer.println("Current scorer: " + currentScorer.mPackageName); 183 writer.flush(); 184 185 for (INetworkScoreCache scoreCache : getScoreCaches()) { 186 try { 187 scoreCache.asBinder().dump(fd, args); 188 } catch (RemoteException e) { 189 writer.println("Unable to dump score cache"); 190 if (Log.isLoggable(TAG, Log.VERBOSE)) { 191 Log.v(TAG, "Unable to dump score cache", e); 192 } 193 } 194 } 195 } 196 197 /** 198 * Returns a set of all score caches that are currently active. 199 * 200 * <p>May be used to perform an action on all score caches without potentially strange behavior 201 * if a new scorer is registered during that action's execution. 202 */ 203 private Set<INetworkScoreCache> getScoreCaches() { 204 synchronized (mScoreCaches) { 205 return new HashSet<>(mScoreCaches.values()); 206 } 207 } 208} 209