Add API for tethering clients change
Add a onClientsChanged callback to OnTetheringEventCallback. The callback will provide information on connected clients combining at least DHCP leases and WiFi AP information (WiFi AP tethering used). Test: atest TetheringTests Bug: 135411507 Change-Id: I7065d081c11bc606d691f76ac8b499dd075d6504
This commit is contained in:
@@ -19,7 +19,15 @@ aidl_interface {
|
||||
local_include_dir: "src",
|
||||
include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
|
||||
srcs: [
|
||||
"src/android/net/*.aidl",
|
||||
// @JavaOnlyStableParcelable aidl declarations must not be listed here, as this would cause
|
||||
// compilation to fail (b/148001843).
|
||||
"src/android/net/IIntResultListener.aidl",
|
||||
"src/android/net/ITetheringConnector.aidl",
|
||||
"src/android/net/ITetheringEventCallback.aidl",
|
||||
"src/android/net/TetheringCallbackStartedParcel.aidl",
|
||||
"src/android/net/TetheringConfigurationParcel.aidl",
|
||||
"src/android/net/TetheringRequestParcel.aidl",
|
||||
"src/android/net/TetherStatesParcel.aidl",
|
||||
],
|
||||
backend: {
|
||||
ndk: {
|
||||
@@ -35,6 +43,7 @@ java_library {
|
||||
name: "framework-tethering",
|
||||
sdk_version: "system_current",
|
||||
srcs: [
|
||||
"src/android/net/TetheredClient.java",
|
||||
"src/android/net/TetheringManager.java",
|
||||
"src/android/net/TetheringConstants.java",
|
||||
":framework-tethering-annotations",
|
||||
@@ -63,6 +72,8 @@ java_library {
|
||||
filegroup {
|
||||
name: "framework-tethering-srcs",
|
||||
srcs: [
|
||||
"src/android/net/TetheredClient.aidl",
|
||||
"src/android/net/TetheredClient.java",
|
||||
"src/android/net/TetheringManager.java",
|
||||
"src/android/net/TetheringConstants.java",
|
||||
"src/android/net/IIntResultListener.aidl",
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
@JavaOnlyStableParcelable parcelable TetheredClient;
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.TestApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Information on a tethered downstream client.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@TestApi
|
||||
public final class TetheredClient implements Parcelable {
|
||||
@NonNull
|
||||
private final MacAddress mMacAddress;
|
||||
@NonNull
|
||||
private final List<AddressInfo> mAddresses;
|
||||
// TODO: use an @IntDef here
|
||||
private final int mTetheringType;
|
||||
|
||||
public TetheredClient(@NonNull MacAddress macAddress,
|
||||
@NonNull Collection<AddressInfo> addresses, int tetheringType) {
|
||||
mMacAddress = macAddress;
|
||||
mAddresses = new ArrayList<>(addresses);
|
||||
mTetheringType = tetheringType;
|
||||
}
|
||||
|
||||
private TetheredClient(@NonNull Parcel in) {
|
||||
this(in.readParcelable(null), in.createTypedArrayList(AddressInfo.CREATOR), in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeParcelable(mMacAddress, flags);
|
||||
dest.writeTypedList(mAddresses);
|
||||
dest.writeInt(mTetheringType);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MacAddress getMacAddress() {
|
||||
return mMacAddress;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<AddressInfo> getAddresses() {
|
||||
return new ArrayList<>(mAddresses);
|
||||
}
|
||||
|
||||
public int getTetheringType() {
|
||||
return mTetheringType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link TetheredClient} that has all the attributes of this instance, plus the
|
||||
* {@link AddressInfo} of the provided {@link TetheredClient}.
|
||||
*
|
||||
* <p>Duplicate addresses are removed.
|
||||
* @hide
|
||||
*/
|
||||
public TetheredClient addAddresses(@NonNull TetheredClient other) {
|
||||
final HashSet<AddressInfo> newAddresses = new HashSet<>(
|
||||
mAddresses.size() + other.mAddresses.size());
|
||||
newAddresses.addAll(mAddresses);
|
||||
newAddresses.addAll(other.mAddresses);
|
||||
return new TetheredClient(mMacAddress, newAddresses, mTetheringType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mMacAddress, mAddresses, mTetheringType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof TetheredClient)) return false;
|
||||
final TetheredClient other = (TetheredClient) obj;
|
||||
return mMacAddress.equals(other.mMacAddress)
|
||||
&& mAddresses.equals(other.mAddresses)
|
||||
&& mTetheringType == other.mTetheringType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information on an lease assigned to a tethered client.
|
||||
*/
|
||||
public static final class AddressInfo implements Parcelable {
|
||||
@NonNull
|
||||
private final LinkAddress mAddress;
|
||||
@Nullable
|
||||
private final String mHostname;
|
||||
// TODO: use LinkAddress expiration time once it is supported
|
||||
private final long mExpirationTime;
|
||||
|
||||
/** @hide */
|
||||
public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) {
|
||||
this(address, hostname, 0);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public AddressInfo(@NonNull LinkAddress address, String hostname, long expirationTime) {
|
||||
this.mAddress = address;
|
||||
this.mHostname = hostname;
|
||||
this.mExpirationTime = expirationTime;
|
||||
}
|
||||
|
||||
private AddressInfo(Parcel in) {
|
||||
this(in.readParcelable(null), in.readString(), in.readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeParcelable(mAddress, flags);
|
||||
dest.writeString(mHostname);
|
||||
dest.writeLong(mExpirationTime);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public LinkAddress getAddress() {
|
||||
return mAddress;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getHostname() {
|
||||
return mHostname;
|
||||
}
|
||||
|
||||
/** @hide TODO: use expiration time in LinkAddress */
|
||||
public long getExpirationTime() {
|
||||
return mExpirationTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mAddress, mHostname, mExpirationTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof AddressInfo)) return false;
|
||||
final AddressInfo other = (AddressInfo) obj;
|
||||
// Use .equals() for addresses as all changes, including address expiry changes,
|
||||
// should be included.
|
||||
return other.mAddress.equals(mAddress)
|
||||
&& Objects.equals(mHostname, other.mHostname)
|
||||
&& mExpirationTime == other.mExpirationTime;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<AddressInfo> CREATOR = new Creator<AddressInfo>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public AddressInfo createFromParcel(@NonNull Parcel in) {
|
||||
return new AddressInfo(in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AddressInfo[] newArray(int size) {
|
||||
return new AddressInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<TetheredClient> CREATOR = new Creator<TetheredClient>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public TetheredClient createFromParcel(@NonNull Parcel in) {
|
||||
return new TetheredClient(in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public TetheredClient[] newArray(int size) {
|
||||
return new TetheredClient[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -653,6 +654,19 @@ public class TetheringManager {
|
||||
* @param error One of {@code TetheringManager#TETHER_ERROR_*}.
|
||||
*/
|
||||
public void onError(@NonNull String ifName, int error) {}
|
||||
|
||||
/**
|
||||
* Called when the list of tethered clients changes.
|
||||
*
|
||||
* <p>This callback provides best-effort information on connected clients based on state
|
||||
* known to the system, however the list cannot be completely accurate (and should not be
|
||||
* used for security purposes). For example, clients behind a bridge and using static IP
|
||||
* assignments are not visible to the tethering device; or even when using DHCP, such
|
||||
* clients may still be reported by this callback after disconnection as the system cannot
|
||||
* determine if they are still connected.
|
||||
* @param clients The new set of tethered clients; the collection is not ordered.
|
||||
*/
|
||||
public void onClientsChanged(@NonNull Collection<TetheredClient> clients) {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
95
Tethering/tests/unit/src/android/net/TetheredClientTest.kt
Normal file
95
Tethering/tests/unit/src/android/net/TetheredClientTest.kt
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.net.InetAddresses.parseNumericAddress
|
||||
import android.net.TetheredClient.AddressInfo
|
||||
import android.net.TetheringManager.TETHERING_BLUETOOTH
|
||||
import android.net.TetheringManager.TETHERING_USB
|
||||
import androidx.test.filters.SmallTest
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import com.android.testutils.assertParcelSane
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
|
||||
private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67))
|
||||
private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78))
|
||||
private val TEST_ADDR1 = LinkAddress(parseNumericAddress("192.168.113.3"), 24)
|
||||
private val TEST_ADDR2 = LinkAddress(parseNumericAddress("fe80::1:2:3"), 64)
|
||||
private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname")
|
||||
private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null)
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@SmallTest
|
||||
class TetheredClientTest {
|
||||
@Test
|
||||
fun testParceling() {
|
||||
assertParcelSane(makeTestClient(), fieldCount = 3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEquals() {
|
||||
assertEquals(makeTestClient(), makeTestClient())
|
||||
|
||||
// Different mac address
|
||||
assertNotEquals(makeTestClient(), TetheredClient(
|
||||
TEST_OTHER_MACADDR,
|
||||
listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
|
||||
TETHERING_BLUETOOTH))
|
||||
|
||||
// Different hostname
|
||||
assertNotEquals(makeTestClient(), TetheredClient(
|
||||
TEST_MACADDR,
|
||||
listOf(AddressInfo(TEST_ADDR1, "test_other_hostname"), TEST_ADDRINFO2),
|
||||
TETHERING_BLUETOOTH))
|
||||
|
||||
// Null hostname
|
||||
assertNotEquals(makeTestClient(), TetheredClient(
|
||||
TEST_MACADDR,
|
||||
listOf(AddressInfo(TEST_ADDR1, null), TEST_ADDRINFO2),
|
||||
TETHERING_BLUETOOTH))
|
||||
|
||||
// Missing address
|
||||
assertNotEquals(makeTestClient(), TetheredClient(
|
||||
TEST_MACADDR,
|
||||
listOf(TEST_ADDRINFO2),
|
||||
TETHERING_BLUETOOTH))
|
||||
|
||||
// Different type
|
||||
assertNotEquals(makeTestClient(), TetheredClient(
|
||||
TEST_MACADDR,
|
||||
listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
|
||||
TETHERING_BLUETOOTH))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddAddresses() {
|
||||
val client1 = TetheredClient(TEST_MACADDR, listOf(TEST_ADDRINFO1), TETHERING_USB)
|
||||
val client2 = TetheredClient(TEST_OTHER_MACADDR, listOf(TEST_ADDRINFO2), TETHERING_USB)
|
||||
assertEquals(TetheredClient(
|
||||
TEST_MACADDR,
|
||||
listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
|
||||
TETHERING_USB), client1.addAddresses(client2))
|
||||
}
|
||||
|
||||
private fun makeTestClient() = TetheredClient(
|
||||
TEST_MACADDR,
|
||||
listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
|
||||
TETHERING_BLUETOOTH)
|
||||
}
|
||||
Reference in New Issue
Block a user