Merge "Cherrypick: Define the calculateRankingScore method and Key." am: 425af45d02
am: 41577a0029
Change-Id: I7807d92a705a8ec9d9c7b04159e1cd2290846e38
This commit is contained in:
@@ -25822,8 +25822,9 @@ package android.net {
|
|||||||
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean, android.os.Bundle);
|
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean, android.os.Bundle);
|
||||||
method public int describeContents();
|
method public int describeContents();
|
||||||
method public void writeToParcel(android.os.Parcel, int);
|
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 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 android.os.Bundle attributes;
|
||||||
field public final boolean meteredHint;
|
field public final boolean meteredHint;
|
||||||
field public final android.net.NetworkKey networkKey;
|
field public final android.net.NetworkKey networkKey;
|
||||||
|
|||||||
@@ -16,11 +16,14 @@
|
|||||||
|
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.annotation.SystemApi;
|
import android.annotation.SystemApi;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import java.lang.Math;
|
||||||
|
import java.lang.UnsupportedOperationException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,7 +46,17 @@ public class ScoredNetwork implements Parcelable {
|
|||||||
* <p>
|
* <p>
|
||||||
* If no value is associated with this key then it's unknown.
|
* 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. */
|
/** A {@link NetworkKey} uniquely identifying this network. */
|
||||||
public final NetworkKey networkKey;
|
public final NetworkKey networkKey;
|
||||||
@@ -71,8 +84,10 @@ public class ScoredNetwork implements Parcelable {
|
|||||||
* An additional collection of optional attributes set by
|
* An additional collection of optional attributes set by
|
||||||
* the Network Recommendation Provider.
|
* 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;
|
public final Bundle attributes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,7 +137,7 @@ public class ScoredNetwork implements Parcelable {
|
|||||||
* @param attributes optional provider specific attributes
|
* @param attributes optional provider specific attributes
|
||||||
*/
|
*/
|
||||||
public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint,
|
public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint,
|
||||||
Bundle attributes) {
|
@Nullable Bundle attributes) {
|
||||||
this.networkKey = networkKey;
|
this.networkKey = networkKey;
|
||||||
this.rssiCurve = rssiCurve;
|
this.rssiCurve = rssiCurve;
|
||||||
this.meteredHint = meteredHint;
|
this.meteredHint = meteredHint;
|
||||||
@@ -136,7 +151,7 @@ public class ScoredNetwork implements Parcelable {
|
|||||||
} else {
|
} else {
|
||||||
rssiCurve = null;
|
rssiCurve = null;
|
||||||
}
|
}
|
||||||
meteredHint = in.readByte() != 0;
|
meteredHint = (in.readByte() == 1);
|
||||||
attributes = in.readBundle();
|
attributes = in.readBundle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +171,6 @@ public class ScoredNetwork implements Parcelable {
|
|||||||
}
|
}
|
||||||
out.writeByte((byte) (meteredHint ? 1 : 0));
|
out.writeByte((byte) (meteredHint ? 1 : 0));
|
||||||
out.writeBundle(attributes);
|
out.writeBundle(attributes);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 =
|
public static final Parcelable.Creator<ScoredNetwork> CREATOR =
|
||||||
new Parcelable.Creator<ScoredNetwork>() {
|
new Parcelable.Creator<ScoredNetwork>() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
169
core/tests/coretests/src/android/net/ScoredNetworkTest.java
Normal file
169
core/tests/coretests/src/android/net/ScoredNetworkTest.java
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user