Loading api/system-current.txt +2 −1 Original line number Diff line number Diff line Loading @@ -25826,8 +25826,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; core/java/android/net/ScoredNetwork.java +67 −5 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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; Loading @@ -136,7 +151,7 @@ public class ScoredNetwork implements Parcelable { } else { rssiCurve = null; } meteredHint = in.readByte() != 0; meteredHint = (in.readByte() == 1); attributes = in.readBundle(); } Loading @@ -156,7 +171,6 @@ public class ScoredNetwork implements Parcelable { } out.writeByte((byte) (meteredHint ? 1 : 0)); out.writeBundle(attributes); } @Override Loading Loading @@ -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 Loading core/tests/coretests/src/android/net/ScoredNetworkTest.java 0 → 100644 +169 −0 Original line number Diff line number Diff line /* 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); } } Loading
api/system-current.txt +2 −1 Original line number Diff line number Diff line Loading @@ -25826,8 +25826,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;
core/java/android/net/ScoredNetwork.java +67 −5 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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; Loading @@ -136,7 +151,7 @@ public class ScoredNetwork implements Parcelable { } else { rssiCurve = null; } meteredHint = in.readByte() != 0; meteredHint = (in.readByte() == 1); attributes = in.readBundle(); } Loading @@ -156,7 +171,6 @@ public class ScoredNetwork implements Parcelable { } out.writeByte((byte) (meteredHint ? 1 : 0)); out.writeBundle(attributes); } @Override Loading Loading @@ -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 Loading
core/tests/coretests/src/android/net/ScoredNetworkTest.java 0 → 100644 +169 −0 Original line number Diff line number Diff line /* 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); } }