diff options
3 files changed, 406 insertions, 0 deletions
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java new file mode 100644 index 000000000000..7c99c497c54b --- /dev/null +++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2017 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.wifi.aware; + +import android.net.NetworkSpecifier; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import libcore.util.HexEncoding; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.StringJoiner; + +/** + * A network specifier object used to represent the capabilities of an network agent. A collection + * of multiple WifiAwareNetworkSpecifier objects whose matching critiera (satisfiedBy) is an OR: + * a match on any of the network specifiers in the collection is a match. + * + * This class is not intended for use in network requests. + * + * @hide + */ +public class WifiAwareAgentNetworkSpecifier extends NetworkSpecifier implements Parcelable { + private static final String TAG = "WifiAwareAgentNs"; + + private static final boolean VDBG = false; // STOPSHIP if true + + private Set<ByteArrayWrapper> mNetworkSpecifiers = new HashSet<>(); + private MessageDigest mDigester; + + public WifiAwareAgentNetworkSpecifier() { + // do nothing, already initialized to empty + } + + public WifiAwareAgentNetworkSpecifier(WifiAwareNetworkSpecifier ns) { + initialize(); + mNetworkSpecifiers.add(convert(ns)); + } + + public WifiAwareAgentNetworkSpecifier(WifiAwareNetworkSpecifier[] nss) { + initialize(); + for (WifiAwareNetworkSpecifier ns : nss) { + mNetworkSpecifiers.add(convert(ns)); + } + } + + public boolean isEmpty() { + return mNetworkSpecifiers.isEmpty(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeArray(mNetworkSpecifiers.toArray()); + } + + public static final Creator<WifiAwareAgentNetworkSpecifier> CREATOR = + new Creator<WifiAwareAgentNetworkSpecifier>() { + @Override + public WifiAwareAgentNetworkSpecifier createFromParcel(Parcel in) { + WifiAwareAgentNetworkSpecifier agentNs = new WifiAwareAgentNetworkSpecifier(); + Object[] objs = in.readArray(null); + for (Object obj : objs) { + agentNs.mNetworkSpecifiers.add((ByteArrayWrapper) obj); + } + return agentNs; + } + + @Override + public WifiAwareAgentNetworkSpecifier[] newArray(int size) { + return new WifiAwareAgentNetworkSpecifier[size]; + } + }; + + @Override + public int hashCode() { + return mNetworkSpecifiers.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof WifiAwareAgentNetworkSpecifier)) { + return false; + } + return mNetworkSpecifiers.equals(((WifiAwareAgentNetworkSpecifier) obj).mNetworkSpecifiers); + } + + @Override + public String toString() { + StringJoiner sj = new StringJoiner(","); + for (ByteArrayWrapper baw: mNetworkSpecifiers) { + sj.add(baw.toString()); + } + return sj.toString(); + } + + @Override + public boolean satisfiedBy(NetworkSpecifier other) { + if (!(other instanceof WifiAwareAgentNetworkSpecifier)) { + return false; + } + WifiAwareAgentNetworkSpecifier otherNs = (WifiAwareAgentNetworkSpecifier) other; + + // called as old.satifiedBy(new): satisfied if old contained in new + for (ByteArrayWrapper baw: mNetworkSpecifiers) { + if (!otherNs.mNetworkSpecifiers.contains(baw)) { + return false; + } + } + + return true; + } + + public boolean satisfiesAwareNetworkSpecifier(WifiAwareNetworkSpecifier ns) { + if (VDBG) Log.v(TAG, "satisfiesAwareNetworkSpecifier: ns=" + ns); + ByteArrayWrapper nsBytes = convert(ns); + return mNetworkSpecifiers.contains(nsBytes); + } + + @Override + public void assertValidFromUid(int requestorUid) { + throw new SecurityException( + "WifiAwareAgentNetworkSpecifier should not be used in network requests"); + } + + private void initialize() { + try { + mDigester = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + Log.e(TAG, "Can not instantiate a SHA-256 digester!? Will match nothing."); + return; + } + } + + private ByteArrayWrapper convert(WifiAwareNetworkSpecifier ns) { + if (mDigester == null) { + return null; + } + + Parcel parcel = Parcel.obtain(); + ns.writeToParcel(parcel, 0); + byte[] bytes = parcel.marshall(); + + mDigester.reset(); + mDigester.update(bytes); + return new ByteArrayWrapper(mDigester.digest()); + } + + private static class ByteArrayWrapper implements Parcelable { + private byte[] mData; + + ByteArrayWrapper(byte[] data) { + mData = data; + } + + @Override + public int hashCode() { + return Arrays.hashCode(mData); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof ByteArrayWrapper)) { + return false; + } + return Arrays.equals(((ByteArrayWrapper) obj).mData, mData); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBlob(mData); + } + + public static final Creator<ByteArrayWrapper> CREATOR = + new Creator<ByteArrayWrapper>() { + @Override + public ByteArrayWrapper createFromParcel(Parcel in) { + return new ByteArrayWrapper(in.readBlob()); + } + + @Override + public ByteArrayWrapper[] newArray(int size) { + return new ByteArrayWrapper[size]; + } + }; + + @Override + public String toString() { + return new String(HexEncoding.encode(mData)); + } + } +} diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java index eeabbfa25892..6e37fcf4d338 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java @@ -193,6 +193,9 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements @Override public boolean satisfiedBy(NetworkSpecifier other) { // MatchAllNetworkSpecifier is taken care in NetworkCapabilities#satisfiedBySpecifier. + if (other instanceof WifiAwareAgentNetworkSpecifier) { + return ((WifiAwareAgentNetworkSpecifier) other).satisfiesAwareNetworkSpecifier(this); + } return equals(other); } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java new file mode 100644 index 000000000000..2dd0537a7aff --- /dev/null +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java @@ -0,0 +1,177 @@ +/* + * 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.wifi.aware; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertEquals; + +import android.os.Parcel; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ErrorCollector; + +import java.util.HashSet; +import java.util.Set; + +/** + * Unit test harness for WifiAwareAgentNetworkSpecifier class. + */ +@SmallTest +public class WifiAwareAgentNetworkSpecifierTest { + @Rule + public ErrorCollector collector = new ErrorCollector(); + + @Test + public void testParcel() { + final int numNs = 10; + + Set<WifiAwareNetworkSpecifier> nsSet = new HashSet<>(); + for (int i = 0; i < numNs; ++i) { + nsSet.add(getDummyNetworkSpecifier(10 + i)); + } + WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier( + nsSet.toArray(new WifiAwareNetworkSpecifier[numNs])); + + Parcel parcelW = Parcel.obtain(); + dut.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + WifiAwareAgentNetworkSpecifier rereadDut = + WifiAwareAgentNetworkSpecifier.CREATOR.createFromParcel(parcelR); + + assertEquals(dut, rereadDut); + } + + /** + * Validate that an empty agent network specifier doesn't match any base network specifier. + */ + @Test + public void testEmptyDoesntMatchAnything() { + WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier(); + WifiAwareNetworkSpecifier ns = getDummyNetworkSpecifier(6); + collector.checkThat("No match expected", ns.satisfiedBy(dut), equalTo(false)); + } + + /** + * Validate that an agent network specifier constructed with a single entry matches that entry, + * and only that entry. + */ + @Test + public void testSingleMatch() { + WifiAwareNetworkSpecifier nsThis = getDummyNetworkSpecifier(6); + WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier(nsThis); + WifiAwareNetworkSpecifier nsOther = getDummyNetworkSpecifier(8); + collector.checkThat("Match expected", nsThis.satisfiedBy(dut), equalTo(true)); + collector.checkThat("No match expected", nsOther.satisfiedBy(dut), equalTo(false)); + } + + /** + * Validate that an agent network specifier constructed with multiple entries matches all those + * entries - but none other. + */ + @Test + public void testMultipleMatchesAllMembers() { + final int numNs = 10; + + Set<WifiAwareNetworkSpecifier> nsSet = new HashSet<>(); + for (int i = 0; i < numNs; ++i) { + nsSet.add(getDummyNetworkSpecifier(10 + i)); + } + + WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier( + nsSet.toArray(new WifiAwareNetworkSpecifier[numNs])); + WifiAwareNetworkSpecifier nsOther = getDummyNetworkSpecifier(10000); + + for (WifiAwareNetworkSpecifier nsThis: nsSet) { + collector.checkThat("Match expected", nsThis.satisfiedBy(dut), equalTo(true)); + } + collector.checkThat("No match expected", nsOther.satisfiedBy(dut), equalTo(false)); + } + + /** + * Validate that agent network specifier matches against a super-set. + */ + @Test + public void testMatchSuperset() { + final int numNs = 10; + + Set<WifiAwareNetworkSpecifier> nsSet = new HashSet<>(); + for (int i = 0; i < numNs; ++i) { + nsSet.add(getDummyNetworkSpecifier(10 + i)); + } + + WifiAwareAgentNetworkSpecifier oldNs = new WifiAwareAgentNetworkSpecifier( + nsSet.toArray(new WifiAwareNetworkSpecifier[nsSet.size()])); + + nsSet.add(getDummyNetworkSpecifier(100 + numNs)); + WifiAwareAgentNetworkSpecifier newNs = new WifiAwareAgentNetworkSpecifier( + nsSet.toArray(new WifiAwareNetworkSpecifier[nsSet.size()])); + + collector.checkThat("Match expected", oldNs.satisfiedBy(newNs), equalTo(true)); + } + + /** + * Validate that agent network specifier does not match against a sub-set. + */ + @Test + public void testNoMatchSubset() { + final int numNs = 10; + + Set<WifiAwareNetworkSpecifier> nsSet = new HashSet<>(); + for (int i = 0; i < numNs; ++i) { + nsSet.add(getDummyNetworkSpecifier(10 + i)); + } + + WifiAwareAgentNetworkSpecifier newNs = new WifiAwareAgentNetworkSpecifier( + nsSet.toArray(new WifiAwareNetworkSpecifier[nsSet.size()])); + + nsSet.add(getDummyNetworkSpecifier(100 + numNs)); + WifiAwareAgentNetworkSpecifier oldNs = new WifiAwareAgentNetworkSpecifier( + nsSet.toArray(new WifiAwareNetworkSpecifier[nsSet.size()])); + + collector.checkThat("Match unexpected", oldNs.satisfiedBy(newNs), equalTo(false)); + } + + /** + * Validate that agent network specifier cannot be used as in network requests - i.e. that + * throws an exception when queried for UID validity. + */ + @Test(expected = SecurityException.class) + public void testNoUsageInRequest() { + WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier(); + + dut.assertValidFromUid(0); + } + + // utilities + + /** + * Returns a WifiAwareNetworkSpecifier with dummy (but valid) entries. Each can be + * differentiated (made unique) by specifying a different client ID. + */ + WifiAwareNetworkSpecifier getDummyNetworkSpecifier(int clientId) { + return new WifiAwareNetworkSpecifier(WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, + WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, clientId, 0, 0, new byte[6], + null, null, 0); + } +} |