diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | core/java/android/net/INetworkScoreCache.aidl | 43 | ||||
-rw-r--r-- | core/java/android/net/INetworkScoreService.aidl | 18 | ||||
-rw-r--r-- | core/java/android/net/NetworkScoreManager.java | 47 | ||||
-rw-r--r-- | core/java/android/net/RssiCurve.java | 21 | ||||
-rw-r--r-- | core/tests/coretests/src/android/net/RssiCurveTest.java | 41 | ||||
-rw-r--r-- | services/core/java/com/android/server/NetworkScoreService.java | 92 |
7 files changed, 242 insertions, 21 deletions
@@ -157,6 +157,7 @@ LOCAL_SRC_FILES += \ core/java/android/net/INetworkManagementEventObserver.aidl \ core/java/android/net/INetworkPolicyListener.aidl \ core/java/android/net/INetworkPolicyManager.aidl \ + core/java/android/net/INetworkScoreCache.aidl \ core/java/android/net/INetworkScoreService.aidl \ core/java/android/net/INetworkStatsService.aidl \ core/java/android/net/INetworkStatsSession.aidl \ diff --git a/core/java/android/net/INetworkScoreCache.aidl b/core/java/android/net/INetworkScoreCache.aidl new file mode 100644 index 0000000..35601ce --- /dev/null +++ b/core/java/android/net/INetworkScoreCache.aidl @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.net.ScoredNetwork; + +/** + * A service which stores a subset of scored networks from the active network scorer. + * + * <p>To be implemented by network subsystems (e.g. Wi-Fi). NetworkScoreService will propagate + * scores down to each subsystem depending on the network type. Implementations may register for + * a given network type by calling NetworkScoreManager.registerNetworkSubsystem. + * + * <p>A proper implementation should throw SecurityException whenever the caller is not privileged. + * It may request scores by calling NetworkScoreManager#requestScores(NetworkKey[]); a call to + * updateScores may follow but may not depending on the active scorer's implementation, and in + * general this method may be called at any time. + * + * <p>Implementations should also override dump() so that "adb shell dumpsys network_score" includes + * the current scores for each network for debugging purposes. + * @hide + */ +interface INetworkScoreCache +{ + void updateScores(in List<ScoredNetwork> networks); + + void clearScores(); +} + diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl index a72d9a0..626bd2a 100644 --- a/core/java/android/net/INetworkScoreService.aidl +++ b/core/java/android/net/INetworkScoreService.aidl @@ -16,6 +16,7 @@ package android.net; +import android.net.INetworkScoreCache; import android.net.ScoredNetwork; /** @@ -34,8 +35,7 @@ interface INetworkScoreService /** * Clear all scores. * @return whether the clear was successful. - * @throws SecurityException if the caller is neither the current active scorer nor the scorer - * manager. + * @throws SecurityException if the caller is neither the current active scorer nor the system. */ boolean clearScores(); @@ -43,7 +43,19 @@ interface INetworkScoreService * Set the active scorer and clear existing scores. * @param packageName the package name of the new scorer to use. * @return true if the operation succeeded, or false if the new package is not a valid scorer. - * @throws SecurityException if the caller is not the scorer manager. + * @throws SecurityException if the caller is not the system. */ boolean setActiveScorer(in String packageName); + + /** + * Register a network subsystem for scoring. + * + * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}. + * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores. + * @throws SecurityException if the caller is not the system. + * @throws IllegalArgumentException if a score cache is already registed for this type. + * @hide + */ + void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache); + } diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index 6dd56d9..352512e 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -19,6 +19,7 @@ package android.net; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; +import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -101,7 +102,7 @@ public class NetworkScoreManager { * determine the current scorer and offer the user the ability to select a different scorer via * the {@link #ACTION_CHANGE_ACTIVE} intent. * @return the full package name of the current active scorer, or null if there is no active - * scorer. + * scorer. */ public String getActiveScorerPackage() { return NetworkScorerAppManager.getActiveScorer(mContext); @@ -151,8 +152,8 @@ public class NetworkScoreManager { * * @return true if the operation succeeded, or false if the new package is not a valid scorer. * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating that - * it can manage scorer applications. + * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating + * that it can manage scorer applications. * @hide */ public boolean setActiveScorer(String packageName) throws SecurityException { @@ -162,4 +163,44 @@ public class NetworkScoreManager { return false; } } + + /** + * Request scoring for networks. + * + * <p>Note that this is just a helper method to assemble the broadcast, and will run in the + * calling process. + * + * @return true if the broadcast was sent, or false if there is no active scorer. + * @throws SecurityException if the caller does not hold the + * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission. + * @hide + */ + public boolean requestScores(NetworkKey[] networks) throws SecurityException { + String activeScorer = getActiveScorerPackage(); + if (activeScorer == null) { + return false; + } + Intent intent = new Intent(ACTION_SCORE_NETWORKS); + intent.setPackage(activeScorer); + intent.putExtra(EXTRA_NETWORKS_TO_SCORE, networks); + mContext.sendBroadcast(intent); + return true; + } + + /** + * Register a network score cache. + * + * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}. + * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores. + * @throws SecurityException if the caller does not hold the + * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission. + * @throws IllegalArgumentException if a score cache is already registered for this type. + * @hide + */ + public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) { + try { + mService.registerNetworkScoreCache(networkType, scoreCache); + } catch (RemoteException e) { + } + } } diff --git a/core/java/android/net/RssiCurve.java b/core/java/android/net/RssiCurve.java index 33e81c2..dd744d3 100644 --- a/core/java/android/net/RssiCurve.java +++ b/core/java/android/net/RssiCurve.java @@ -98,6 +98,27 @@ public class RssiCurve implements Parcelable { } /** + * Lookup the score for a given RSSI value. + * + * @param rssi The RSSI to lookup. If the RSSI falls below the start of the curve, the score at + * the start of the curve will be returned. If it falls after the end of the curve, the + * score at the end of the curve will be returned. + * @return the score for the given RSSI. + */ + public byte lookupScore(int rssi) { + int index = (rssi - start) / bucketWidth; + + // Snap the index to the closest bucket if it falls outside the curve. + if (index < 0) { + index = 0; + } else if (index > rssiBuckets.length - 1) { + index = rssiBuckets.length - 1; + } + + return rssiBuckets[index]; + } + + /** * Determine if two RSSI curves are defined in the same way. * * <p>Note that two curves can be equivalent but defined differently, e.g. if one bucket in one diff --git a/core/tests/coretests/src/android/net/RssiCurveTest.java b/core/tests/coretests/src/android/net/RssiCurveTest.java new file mode 100644 index 0000000..d4438df --- /dev/null +++ b/core/tests/coretests/src/android/net/RssiCurveTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.net; + +import junit.framework.TestCase; + +public class RssiCurveTest extends TestCase { + public void testLookupScore_constantCurve() { + RssiCurve curve = new RssiCurve(-100, 200, new byte[] { 10 }); + assertEquals(10, curve.lookupScore(-200)); + assertEquals(10, curve.lookupScore(-100)); + assertEquals(10, curve.lookupScore(0)); + assertEquals(10, curve.lookupScore(100)); + assertEquals(10, curve.lookupScore(200)); + } + + public void testLookupScore_changingCurve() { + RssiCurve curve = new RssiCurve(-100, 100, new byte[] { -10, 10 }); + assertEquals(-10, curve.lookupScore(-200)); + assertEquals(-10, curve.lookupScore(-100)); + assertEquals(-10, curve.lookupScore(-50)); + assertEquals(10, curve.lookupScore(0)); + assertEquals(10, curve.lookupScore(50)); + assertEquals(10, curve.lookupScore(100)); + assertEquals(10, curve.lookupScore(200)); + } +} diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 8a30e50..4f0c9b5 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -20,19 +20,24 @@ import android.Manifest.permission; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.net.INetworkScoreCache; import android.net.INetworkScoreService; -import android.net.NetworkKey; import android.net.NetworkScorerAppManager; -import android.net.RssiCurve; import android.net.ScoredNetwork; +import android.os.RemoteException; import android.text.TextUtils; +import android.util.Log; import com.android.internal.R; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; /** * Backing service for {@link android.net.NetworkScoreManager}. @@ -46,12 +51,11 @@ public class NetworkScoreService extends INetworkScoreService.Stub { private final Context mContext; - // TODO: Delete this temporary class once we have a real place for scores. - private final Map<NetworkKey, RssiCurve> mScoredNetworks; + private final Map<Integer, INetworkScoreCache> mScoreCaches; public NetworkScoreService(Context context) { mContext = context; - mScoredNetworks = new HashMap<>(); + mScoreCaches = new HashMap<>(); } /** Called when the system is ready to run third-party code but before it actually does so. */ @@ -76,10 +80,31 @@ public class NetworkScoreService extends INetworkScoreService.Stub { " is not the active scorer."); } - // TODO: Propagate these scores down to the network subsystem layer instead of just holding - // them in memory. + // Separate networks by type. + Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>(); for (ScoredNetwork network : networks) { - mScoredNetworks.put(network.networkKey, network.rssiCurve); + List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type); + if (networkList == null) { + networkList = new ArrayList<>(); + networksByType.put(network.networkKey.type, networkList); + } + networkList.add(network); + } + + // Pass the scores of each type down to the appropriate network scorer. + for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) { + INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey()); + if (scoreCache != null) { + try { + scoreCache.updateScores(entry.getValue()); + } catch (RemoteException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e); + } + } + } else if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding"); + } } return true; @@ -112,8 +137,29 @@ public class NetworkScoreService extends INetworkScoreService.Stub { /** Clear scores. Callers are responsible for checking permissions as appropriate. */ private void clearInternal() { - // TODO: Propagate the flush down to the network subsystem layer. - mScoredNetworks.clear(); + Set<INetworkScoreCache> cachesToClear = getScoreCaches(); + + for (INetworkScoreCache scoreCache : cachesToClear) { + try { + scoreCache.clearScores(); + } catch (RemoteException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Unable to clear scores", e); + } + } + } + } + + @Override + public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) { + mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG); + synchronized (mScoreCaches) { + if (mScoreCaches.containsKey(networkType)) { + throw new IllegalArgumentException( + "Score cache already registered for type " + networkType); + } + mScoreCaches.put(networkType, scoreCache); + } } @Override @@ -125,12 +171,28 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return; } writer.println("Current scorer: " + currentScorer); - if (mScoredNetworks.isEmpty()) { - writer.println("No networks scored."); - } else { - for (Map.Entry<NetworkKey, RssiCurve> entry : mScoredNetworks.entrySet()) { - writer.println(entry.getKey() + ": " + entry.getValue()); + + for (INetworkScoreCache scoreCache : getScoreCaches()) { + try { + scoreCache.asBinder().dump(fd, args); + } catch (RemoteException e) { + writer.println("Unable to dump score cache"); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Unable to dump score cache", e); + } } } } + + /** + * Returns a set of all score caches that are currently active. + * + * <p>May be used to perform an action on all score caches without potentially strange behavior + * if a new scorer is registered during that action's execution. + */ + private Set<INetworkScoreCache> getScoreCaches() { + synchronized (mScoreCaches) { + return new HashSet<>(mScoreCaches.values()); + } + } } |