Merge "Data structures for communication with network scorer apps."

This commit is contained in:
Jeff Davidson
2014-04-16 18:29:22 +00:00
committed by Android (Google) Code Review
8 changed files with 639 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
/**
* 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;
parcelable NetworkKey;

View File

@@ -0,0 +1,105 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
/**
* Information which identifies a specific network.
*
* @hide
*/
public class NetworkKey implements Parcelable {
/** A wifi network, for which {@link #wifiKey} will be populated. */
public static final int TYPE_WIFI = 1;
/**
* The type of this network.
* @see #TYPE_WIFI
*/
public final int type;
/**
* Information identifying a Wi-Fi network. Only set when {@link #type} equals
* {@link #TYPE_WIFI}.
*/
public final WifiKey wifiKey;
/**
* Construct a new {@link NetworkKey} for a Wi-Fi network.
* @param wifiKey the {@link WifiKey} identifying this Wi-Fi network.
*/
public NetworkKey(WifiKey wifiKey) {
this.type = TYPE_WIFI;
this.wifiKey = wifiKey;
}
private NetworkKey(Parcel in) {
type = in.readInt();
switch (type) {
case TYPE_WIFI:
wifiKey = WifiKey.CREATOR.createFromParcel(in);
break;
default:
throw new IllegalArgumentException("Parcel has unknown type: " + type);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(type);
switch (type) {
case TYPE_WIFI:
wifiKey.writeToParcel(out, flags);
break;
default:
throw new IllegalStateException("NetworkKey has unknown type " + type);
}
}
@Override
public String toString() {
switch (type) {
case TYPE_WIFI:
return wifiKey.toString();
default:
// Don't throw an exception here in case someone is logging this object in a catch
// block for debugging purposes.
return "InvalidKey";
}
}
public static final Parcelable.Creator<NetworkKey> CREATOR =
new Parcelable.Creator<NetworkKey>() {
@Override
public NetworkKey createFromParcel(Parcel in) {
return new NetworkKey(in);
}
@Override
public NetworkKey[] newArray(int size) {
return new NetworkKey[size];
}
};
}

View File

@@ -0,0 +1,129 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
/**
* A curve defining the network score over a range of RSSI values.
*
* <p>For each RSSI bucket, the score may be any byte. Scores have no absolute meaning and are only
* considered relative to other scores assigned by the same scorer. Networks with no score are all
* considered equivalent and ranked below any network with a score.
*
* <p>For example, consider a curve starting at -110 dBm with a bucket width of 10 and the
* following buckets: {@code [-20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]}.
* This represents a linear curve between -110 dBm and 30 dBm. It scores progressively higher at
* stronger signal strengths.
*
* <p>A network can be assigned a fixed score independent of RSSI by setting
* {@link #rssiBuckets} to a one-byte array whose element is the fixed score. {@link #start}
* should be set to the lowest RSSI value at which this fixed score should apply, and
* {@link #bucketWidth} should be set such that {@code start + bucketWidth} is equal to the
* highest RSSI value at which this fixed score should apply.
*
* <p>Note that RSSI values below -110 dBm or above 30 dBm are unlikely to cause any difference
* in connectivity behavior from those endpoints. That is, the connectivity framework will treat
* a network with a -120 dBm signal exactly as it would treat one with a -110 dBm signal.
* Therefore, graphs which specify scores outside this range may be truncated to this range by
* the system.
*
* @see ScoredNetwork
* @hide
*/
public class RssiCurve implements Parcelable {
/** The starting dBm of the curve. */
public final int start;
/** The width of each RSSI bucket, in dBm. */
public final int bucketWidth;
/** The score for each RSSI bucket. */
public final byte[] rssiBuckets;
/**
* Construct a new {@link RssiCurve}.
*
* @param start the starting dBm of the curve.
* @param bucketWidth the width of each RSSI bucket, in dBm.
* @param rssiBuckets the score for each RSSI bucket.
*/
public RssiCurve(int start, int bucketWidth, byte[] rssiBuckets) {
this.start = start;
this.bucketWidth = bucketWidth;
if (rssiBuckets == null || rssiBuckets.length == 0) {
throw new IllegalArgumentException("rssiBuckets must be at least one element large.");
}
this.rssiBuckets = rssiBuckets;
}
private RssiCurve(Parcel in) {
start = in.readInt();
bucketWidth = in.readInt();
int bucketCount = in.readInt();
rssiBuckets = new byte[bucketCount];
in.readByteArray(rssiBuckets);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(start);
out.writeInt(bucketWidth);
out.writeInt(rssiBuckets.length);
out.writeByteArray(rssiBuckets);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("RssiCurve[start=")
.append(start)
.append(",bucketWidth=")
.append(bucketWidth);
sb.append(",buckets=");
for (int i = 0; i < rssiBuckets.length; i++) {
sb.append(rssiBuckets[i]);
if (i < rssiBuckets.length - 1) {
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
public static final Creator<RssiCurve> CREATOR =
new Creator<RssiCurve>() {
@Override
public RssiCurve createFromParcel(Parcel in) {
return new RssiCurve(in);
}
@Override
public RssiCurve[] newArray(int size) {
return new RssiCurve[size];
}
};
}

View File

@@ -0,0 +1,19 @@
/**
* 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;
parcelable ScoredNetwork;

View File

@@ -0,0 +1,99 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
/**
* A network identifier along with a score for the quality of that network.
*
* @hide
*/
public class ScoredNetwork implements Parcelable {
/** A {@link NetworkKey} uniquely identifying this network. */
public final NetworkKey networkKey;
/**
* The {@link RssiCurve} representing the scores for this network based on the RSSI.
*
* <p>This field is optional and may be set to null to indicate that no score is available for
* this network at this time. Such networks, along with networks for which the scorer has not
* responded, are always prioritized below scored networks, regardless of the score.
*/
public final RssiCurve rssiCurve;
/**
* Construct a new {@link ScoredNetwork}.
*
* @param networkKey the {@link NetworkKey} uniquely identifying this network.
* @param rssiCurve the {@link RssiCurve} representing the scores for this network based on the
* RSSI. This field is optional, and may be skipped to represent a network which the scorer
* has opted not to score at this time. Passing a null value here is strongly preferred to
* not returning any {@link ScoredNetwork} for a given {@link NetworkKey} because it
* indicates to the system not to request scores for this network in the future, although
* the scorer may choose to issue an out-of-band update at any time.
*/
public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve) {
this.networkKey = networkKey;
this.rssiCurve = rssiCurve;
}
private ScoredNetwork(Parcel in) {
networkKey = NetworkKey.CREATOR.createFromParcel(in);
if (in.readByte() == 1) {
rssiCurve = RssiCurve.CREATOR.createFromParcel(in);
} else {
rssiCurve = null;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
networkKey.writeToParcel(out, flags);
if (rssiCurve != null) {
out.writeByte((byte) 1);
rssiCurve.writeToParcel(out, flags);
} else {
out.writeByte((byte) 0);
}
}
@Override
public String toString() {
return "ScoredNetwork[key=" + networkKey + ",score=" + rssiCurve + "]";
}
public static final Parcelable.Creator<ScoredNetwork> CREATOR =
new Parcelable.Creator<ScoredNetwork>() {
@Override
public ScoredNetwork createFromParcel(Parcel in) {
return new ScoredNetwork(in);
}
@Override
public ScoredNetwork[] newArray(int size) {
return new ScoredNetwork[size];
}
};
}

View File

@@ -0,0 +1,106 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
import java.util.regex.Pattern;
/**
* Information identifying a Wi-Fi network.
* @see NetworkKey
*
* @hide
*/
public class WifiKey implements Parcelable {
// Patterns used for validation.
private static final Pattern SSID_PATTERN = Pattern.compile("(\".*\")|(0x[\\p{XDigit}]+)");
private static final Pattern BSSID_PATTERN =
Pattern.compile("([\\p{XDigit}]{2}:){5}[\\p{XDigit}]{2}");
/**
* The service set identifier (SSID) of an 802.11 network. If the SSID can be decoded as
* UTF-8, it will be surrounded by double quotation marks. Otherwise, it will be a string of
* hex digits starting with 0x.
*/
public final String ssid;
/**
* The basic service set identifier (BSSID) of an access point for this network. This will
* be in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX}, where each X is a
* hexadecimal digit.
*/
public final String bssid;
/**
* Construct a new {@link WifiKey} for the given Wi-Fi SSID/BSSID pair.
*
* @param ssid the service set identifier (SSID) of an 802.11 network. If the SSID can be
* decoded as UTF-8, it should be surrounded by double quotation marks. Otherwise,
* it should be a string of hex digits starting with 0x.
* @param bssid the basic service set identifier (BSSID) of this network's access point.
* This should be in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX},
* where each X is a hexadecimal digit.
* @throws IllegalArgumentException if either the SSID or BSSID is invalid.
*/
public WifiKey(String ssid, String bssid) {
if (!SSID_PATTERN.matcher(ssid).matches()) {
throw new IllegalArgumentException("Invalid ssid: " + ssid);
}
if (!BSSID_PATTERN.matcher(bssid).matches()) {
throw new IllegalArgumentException("Invalid bssid: " + bssid);
}
this.ssid = ssid;
this.bssid = bssid;
}
private WifiKey(Parcel in) {
ssid = in.readString();
bssid = in.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(ssid);
out.writeString(bssid);
}
@Override
public String toString() {
return "WifiKey[SSID=" + ssid + ",BSSID=" + bssid + "]";
}
public static final Creator<WifiKey> CREATOR =
new Creator<WifiKey>() {
@Override
public WifiKey createFromParcel(Parcel in) {
return new WifiKey(in);
}
@Override
public WifiKey[] newArray(int size) {
return new WifiKey[size];
}
};
}

View File

@@ -0,0 +1,97 @@
/*
* 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.os.Parcel;
import junit.framework.TestCase;
public class NetworkKeyTest extends TestCase {
public void testValidWifiKey_utf8() {
new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23:03");
new WifiKey("\"\"", "AB:CD:01:EF:23:03");
}
public void testValidWifiKey_hex() {
new WifiKey("0x1234abcd", "AB:CD:01:EF:23:03");
}
public void testInvalidWifiKey_empty() {
try {
new WifiKey("", "AB:CD:01:EF:23:03");
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected - empty SSID
}
}
public void testInvalidWifiKey_unquotedUtf8() {
try {
new WifiKey("unquotedSsid", "AB:CD:01:EF:23:03");
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected - empty SSID
}
}
public void testInvalidWifiKey_invalidHex() {
try {
new WifiKey("0x\"nothex\"", "AB:CD:01:EF:23:03");
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected - empty SSID
}
}
public void testInvalidWifiKey_shortBssid() {
try {
new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23");
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected - BSSID too short
}
}
public void testInvalidWifiKey_longBssid() {
try {
new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23:03:11");
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected - BSSID too long
}
}
public void testParceling() {
WifiKey wifiKey = new WifiKey("\"ssid\"", "00:00:00:00:00:00");
NetworkKey networkKey = new NetworkKey(wifiKey);
Parcel parcel = null;
try {
parcel = Parcel.obtain();
parcel.writeParcelable(networkKey, 0);
parcel.setDataPosition(0);
networkKey = parcel.readParcelable(getClass().getClassLoader());
} finally {
if (parcel != null) {
parcel.recycle();
}
}
assertEquals(NetworkKey.TYPE_WIFI, networkKey.type);
assertEquals("\"ssid\"", networkKey.wifiKey.ssid);
assertEquals("00:00:00:00:00:00", networkKey.wifiKey.bssid);
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.os.Parcel;
import junit.framework.TestCase;
import java.util.Arrays;
public class ScoredNetworkTest extends TestCase {
private static final RssiCurve CURVE =
new RssiCurve(-110, 10, new byte[] {0, 1, 2, 3, 4, 5, 6, 7});
public void testInvalidCurve_nullBuckets() {
try {
new RssiCurve(-110, 10, null);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
}
}
public void testInvalidCurve_emptyBuckets() {
try {
new RssiCurve(-110, 10, new byte[] {});
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
}
}
public void testParceling() {
NetworkKey key = new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00"));
ScoredNetwork network = new ScoredNetwork(key, CURVE);
Parcel parcel = null;
try {
parcel = Parcel.obtain();
parcel.writeParcelable(network, 0);
parcel.setDataPosition(0);
network = parcel.readParcelable(getClass().getClassLoader());
} finally {
if (parcel != null) {
parcel.recycle();
}
}
assertEquals(CURVE.start, network.rssiCurve.start);
assertEquals(CURVE.bucketWidth, network.rssiCurve.bucketWidth);
assertTrue(Arrays.equals(CURVE.rssiBuckets, network.rssiCurve.rssiBuckets));
}
}