summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/system-current.txt3
-rw-r--r--core/java/android/net/ScoredNetwork.java72
-rw-r--r--core/tests/coretests/src/android/net/ScoredNetworkTest.java169
3 files changed, 238 insertions, 6 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index f96a58b759a6..2066c9aeaceb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -25824,8 +25824,9 @@ package android.net {
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean, android.os.Bundle);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL = "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
+ field public static final java.lang.String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET = "android.net.attributes.key.RANKING_SCORE_OFFSET";
field public static final android.os.Parcelable.Creator<android.net.ScoredNetwork> CREATOR;
- field public static final java.lang.String EXTRA_HAS_CAPTIVE_PORTAL = "android.net.extra.HAS_CAPTIVE_PORTAL";
field public final android.os.Bundle attributes;
field public final boolean meteredHint;
field public final android.net.NetworkKey networkKey;
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index cf81e9191cca..94e518707cf9 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -16,11 +16,14 @@
package android.net;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.Math;
+import java.lang.UnsupportedOperationException;
import java.util.Objects;
/**
@@ -43,7 +46,17 @@ public class ScoredNetwork implements Parcelable {
* <p>
* If no value is associated with this key then it's unknown.
*/
- public static final String EXTRA_HAS_CAPTIVE_PORTAL = "android.net.extra.HAS_CAPTIVE_PORTAL";
+ public static final String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL =
+ "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
+
+ /**
+ * Key used with the {@link #attributes} bundle to define the rankingScoreOffset int value.
+ *
+ * <p>The rankingScoreOffset is used when calculating the ranking score used to rank networks
+ * against one another. See {@link #calculateRankingScore} for more information.
+ */
+ public static final String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET =
+ "android.net.attributes.key.RANKING_SCORE_OFFSET";
/** A {@link NetworkKey} uniquely identifying this network. */
public final NetworkKey networkKey;
@@ -71,8 +84,10 @@ public class ScoredNetwork implements Parcelable {
* An additional collection of optional attributes set by
* the Network Recommendation Provider.
*
- * @see #EXTRA_HAS_CAPTIVE_PORTAL
+ * @see #ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL
+ * @see #ATTRIBUTES_KEY_RANKING_SCORE_OFFSET_KEY
*/
+ @Nullable
public final Bundle attributes;
/**
@@ -122,7 +137,7 @@ public class ScoredNetwork implements Parcelable {
* @param attributes optional provider specific attributes
*/
public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint,
- Bundle attributes) {
+ @Nullable Bundle attributes) {
this.networkKey = networkKey;
this.rssiCurve = rssiCurve;
this.meteredHint = meteredHint;
@@ -136,7 +151,7 @@ public class ScoredNetwork implements Parcelable {
} else {
rssiCurve = null;
}
- meteredHint = in.readByte() != 0;
+ meteredHint = (in.readByte() == 1);
attributes = in.readBundle();
}
@@ -156,7 +171,6 @@ public class ScoredNetwork implements Parcelable {
}
out.writeByte((byte) (meteredHint ? 1 : 0));
out.writeBundle(attributes);
-
}
@Override
@@ -187,6 +201,54 @@ public class ScoredNetwork implements Parcelable {
'}';
}
+ /**
+ * Returns true if a ranking score can be calculated for this network.
+ *
+ * @hide
+ */
+ public boolean hasRankingScore() {
+ return (rssiCurve != null)
+ || (attributes != null
+ && attributes.containsKey(ATTRIBUTES_KEY_RANKING_SCORE_OFFSET));
+ }
+
+ /**
+ * Returns a ranking score for a given RSSI which can be used to comparatively
+ * rank networks.
+ *
+ * <p>The score obtained by the rssiCurve is bitshifted left by 8 bits to expand it to an
+ * integer and then the offset is added. If the addition operation overflows or underflows,
+ * Integer.MAX_VALUE and Integer.MIN_VALUE will be returned respectively.
+ *
+ * <p>{@link #hasRankingScore} should be called first to ensure this network is capable
+ * of returning a ranking score.
+ *
+ * @throws UnsupportedOperationException if there is no RssiCurve and no rankingScoreOffset
+ * for this network (hasRankingScore returns false).
+ *
+ * @hide
+ */
+ public int calculateRankingScore(int rssi) throws UnsupportedOperationException {
+ if (!hasRankingScore()) {
+ throw new UnsupportedOperationException(
+ "Either rssiCurve or rankingScoreOffset is required to calculate the "
+ + "ranking score");
+ }
+
+ int offset = 0;
+ if (attributes != null) {
+ offset += attributes.getInt(ATTRIBUTES_KEY_RANKING_SCORE_OFFSET, 0 /* default */);
+ }
+
+ int score = (rssiCurve == null) ? 0 : rssiCurve.lookupScore(rssi) << Byte.SIZE;
+
+ try {
+ return Math.addExact(score, offset);
+ } catch (ArithmeticException e) {
+ return (score < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
+ }
+ }
+
public static final Parcelable.Creator<ScoredNetwork> CREATOR =
new Parcelable.Creator<ScoredNetwork>() {
@Override
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
new file mode 100644
index 000000000000..9c3346e1238b
--- /dev/null
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -0,0 +1,169 @@
+/*
+ t Copyright (C) 2016 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 static org.junit.Assert.*;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/** Unit tests for {@link ScoredNetwork}. */
+@RunWith(AndroidJUnit4.class)
+public class ScoredNetworkTest {
+
+ private static final int RSSI_START = -110;
+ private static final int TEST_RSSI = -50;
+ private static final byte TEST_SCORE = 5;
+ private static final RssiCurve CURVE =
+ new RssiCurve(RSSI_START, 10, new byte[] {-1, 0, 1, 2, 3, 4, TEST_SCORE, 6, 7});
+
+ private static final byte RANKING_SCORE_OFFSET = 13;
+ private static final Bundle ATTRIBUTES;
+ static {
+ ATTRIBUTES = new Bundle();
+ ATTRIBUTES.putInt(
+ ScoredNetwork.ATTRIBUTES_KEY_RANKING_SCORE_OFFSET, RANKING_SCORE_OFFSET);
+ }
+
+ private static final NetworkKey KEY
+ = new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00"));
+
+ @Test
+ public void calculateRankingOffsetShouldThrowUnsupportedOperationException() {
+ // No curve or ranking score offset set in curve
+ ScoredNetwork scoredNetwork = new ScoredNetwork(KEY, null);
+ try {
+ scoredNetwork.calculateRankingScore(TEST_RSSI);
+ fail("Should have thrown UnsupportedOperationException");
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void calculateRankingOffsetWithRssiCurveShouldReturnExpectedScore() {
+ ScoredNetwork scoredNetwork = new ScoredNetwork(KEY, CURVE);
+ assertEquals(TEST_SCORE << Byte.SIZE, scoredNetwork.calculateRankingScore(TEST_RSSI));
+ }
+
+ @Test
+ public void rankingScoresShouldDifferByRankingScoreOffset() {
+ ScoredNetwork scoredNetwork1 = new ScoredNetwork(KEY, CURVE);
+ ScoredNetwork scoredNetwork2
+ = new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+ int scoreDifference =
+ scoredNetwork2.calculateRankingScore(TEST_RSSI)
+ - scoredNetwork1.calculateRankingScore(TEST_RSSI);
+ assertEquals(RANKING_SCORE_OFFSET, scoreDifference);
+ }
+
+ @Test
+ public void calculateRankingScoreShouldNotResultInIntegerOverflow() {
+ Bundle attr = new Bundle();
+ attr.putInt(ScoredNetwork.ATTRIBUTES_KEY_RANKING_SCORE_OFFSET, Integer.MAX_VALUE);
+ ScoredNetwork scoredNetwork
+ = new ScoredNetwork(KEY, CURVE, false /* meteredHint */, attr);
+ assertEquals(Integer.MAX_VALUE, scoredNetwork.calculateRankingScore(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateRankingScoreShouldNotResultInIntegerUnderflow() {
+ Bundle attr = new Bundle();
+ attr.putInt(ScoredNetwork.ATTRIBUTES_KEY_RANKING_SCORE_OFFSET, Integer.MIN_VALUE);
+ ScoredNetwork scoredNetwork =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, attr);
+ assertEquals(Integer.MIN_VALUE, scoredNetwork.calculateRankingScore(RSSI_START));
+ }
+
+ @Test
+ public void hasRankingScoreShouldReturnFalse() {
+ ScoredNetwork network = new ScoredNetwork(KEY, null /* rssiCurve */);
+ assertFalse(network.hasRankingScore());
+ }
+
+ @Test
+ public void hasRankingScoreShouldReturnTrueWhenAttributesHasRankingScoreOffset() {
+ ScoredNetwork network =
+ new ScoredNetwork(KEY, null /* rssiCurve */, false /* meteredHint */, ATTRIBUTES);
+ assertTrue(network.hasRankingScore());
+ }
+
+ @Test
+ public void hasRankingScoreShouldReturnTrueWhenCurveIsPresent() {
+ ScoredNetwork network =
+ new ScoredNetwork(KEY, CURVE , false /* meteredHint */);
+ assertTrue(network.hasRankingScore());
+ }
+
+ @Test
+ public void shouldWriteAndReadFromParcelWhenAllFieldsSet() {
+ ScoredNetwork network = new ScoredNetwork(KEY, CURVE, true /* meteredHint */, ATTRIBUTES);
+ ScoredNetwork newNetwork;
+
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ network.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+ newNetwork = ScoredNetwork.CREATOR.createFromParcel(parcel);
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ assertEquals(CURVE.start, newNetwork.rssiCurve.start);
+ assertEquals(CURVE.bucketWidth, newNetwork.rssiCurve.bucketWidth);
+ assertTrue(Arrays.equals(CURVE.rssiBuckets, newNetwork.rssiCurve.rssiBuckets));
+ assertTrue(newNetwork.meteredHint);
+ assertNotNull(newNetwork.attributes);
+ assertEquals(
+ RANKING_SCORE_OFFSET,
+ newNetwork.attributes.getInt(ScoredNetwork.ATTRIBUTES_KEY_RANKING_SCORE_OFFSET));
+ }
+
+ @Test
+ public void shouldWriteAndReadFromParcelWithoutBundle() {
+ ScoredNetwork network = new ScoredNetwork(KEY, CURVE, true /* meteredHint */);
+ ScoredNetwork newNetwork;
+
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ network.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+ newNetwork = ScoredNetwork.CREATOR.createFromParcel(parcel);
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ assertEquals(CURVE.start, newNetwork.rssiCurve.start);
+ assertEquals(CURVE.bucketWidth, newNetwork.rssiCurve.bucketWidth);
+ assertTrue(Arrays.equals(CURVE.rssiBuckets, newNetwork.rssiCurve.rssiBuckets));
+ assertTrue(newNetwork.meteredHint);
+ assertNull(newNetwork.attributes);
+ }
+}