[Thread] add private API ThreadNetworkSpecifier

Bug: 374037595
Change-Id: I8d63a742ec2a03a1cf2f023711696379c975ff80
This commit is contained in:
Kangping Dong 2024-10-17 20:41:42 +08:00
commit 2967cfaf4e
2 changed files with 359 additions and 0 deletions

View file

@ -0,0 +1,227 @@
/*
* Copyright (C) 2024 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.thread;
import static android.net.thread.ActiveOperationalDataset.LENGTH_EXTENDED_PAN_ID;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.NetworkSpecifier;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.net.module.util.HexDump;
import java.util.Arrays;
import java.util.Objects;
/**
* Represents and identifies a Thread network.
*
* @hide
*/
public final class ThreadNetworkSpecifier extends NetworkSpecifier implements Parcelable {
/** The Extended PAN ID of a Thread network. */
@NonNull private final byte[] mExtendedPanId;
/** The Active Timestamp of a Thread network. */
@Nullable private final OperationalDatasetTimestamp mActiveTimestamp;
private final boolean mRouterEligibleForLeader;
private ThreadNetworkSpecifier(@NonNull Builder builder) {
mExtendedPanId = builder.mExtendedPanId.clone();
mActiveTimestamp = builder.mActiveTimestamp;
mRouterEligibleForLeader = builder.mRouterEligibleForLeader;
}
/** Returns the Extended PAN ID of the Thread network this specifier refers to. */
@NonNull
public byte[] getExtendedPanId() {
return mExtendedPanId.clone();
}
/**
* Returns the Active Timestamp of the Thread network this specifier refers to, or {@code null}
* if not specified.
*/
@Nullable
public OperationalDatasetTimestamp getActiveTimestamp() {
return mActiveTimestamp;
}
/**
* Returns {@code true} if this device can be a leader during attachment when there are no
* nearby routers.
*/
public boolean isRouterEligibleForLeader() {
return mRouterEligibleForLeader;
}
/**
* Returns {@code true} if both {@link #getExtendedPanId()} and {@link #getActiveTimestamp()}
* (if not {@code null}) of the two {@link ThreadNetworkSpecifier} objects are equal.
*
* <p>Note value of {@link #isRouterEligibleForLeader()} is expiclitly excluded because this is
* not part of the identifier.
*
* @hide
*/
@Override
public boolean canBeSatisfiedBy(@Nullable NetworkSpecifier other) {
if (!(other instanceof ThreadNetworkSpecifier)) {
return false;
}
ThreadNetworkSpecifier otherSpecifier = (ThreadNetworkSpecifier) other;
if (mActiveTimestamp != null && !mActiveTimestamp.equals(otherSpecifier.mActiveTimestamp)) {
return false;
}
return Arrays.equals(mExtendedPanId, otherSpecifier.mExtendedPanId);
}
@Override
public boolean equals(@Nullable Object other) {
if (!(other instanceof ThreadNetworkSpecifier)) {
return false;
} else if (this == other) {
return true;
}
ThreadNetworkSpecifier otherSpecifier = (ThreadNetworkSpecifier) other;
return Arrays.equals(mExtendedPanId, otherSpecifier.mExtendedPanId)
&& Objects.equals(mActiveTimestamp, otherSpecifier.mActiveTimestamp)
&& mRouterEligibleForLeader == otherSpecifier.mRouterEligibleForLeader;
}
@Override
public int hashCode() {
return deepHashCode(mExtendedPanId, mActiveTimestamp, mRouterEligibleForLeader);
}
/** An easy-to-use wrapper of {@link Arrays#deepHashCode}. */
private static int deepHashCode(Object... values) {
return Arrays.deepHashCode(values);
}
@Override
public String toString() {
return "ThreadNetworkSpecifier{extendedPanId="
+ HexDump.toHexString(mExtendedPanId)
+ ", activeTimestamp="
+ mActiveTimestamp
+ ", routerEligibleForLeader="
+ mRouterEligibleForLeader
+ "}";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeByteArray(mExtendedPanId);
dest.writeByteArray(mActiveTimestamp != null ? mActiveTimestamp.toTlvValue() : null);
dest.writeBoolean(mRouterEligibleForLeader);
}
public static final @NonNull Parcelable.Creator<ThreadNetworkSpecifier> CREATOR =
new Parcelable.Creator<ThreadNetworkSpecifier>() {
@Override
public ThreadNetworkSpecifier createFromParcel(Parcel in) {
byte[] extendedPanId = in.createByteArray();
byte[] activeTimestampBytes = in.createByteArray();
OperationalDatasetTimestamp activeTimestamp =
(activeTimestampBytes != null)
? OperationalDatasetTimestamp.fromTlvValue(activeTimestampBytes)
: null;
boolean routerEligibleForLeader = in.readBoolean();
return new Builder(extendedPanId)
.setActiveTimestamp(activeTimestamp)
.setRouterEligibleForLeader(routerEligibleForLeader)
.build();
}
@Override
public ThreadNetworkSpecifier[] newArray(int size) {
return new ThreadNetworkSpecifier[size];
}
};
/** The builder for creating {@link ActiveOperationalDataset} objects. */
public static final class Builder {
@NonNull private final byte[] mExtendedPanId;
@Nullable private OperationalDatasetTimestamp mActiveTimestamp;
private boolean mRouterEligibleForLeader;
/**
* Creates a new {@link Builder} object with given Extended PAN ID.
*
* @throws IllegalArgumentException if {@code extendedPanId} is {@code null} or the length
* is not {@link ActiveOperationalDataset#LENGTH_EXTENDED_PAN_ID}
*/
public Builder(@NonNull byte[] extendedPanId) {
if (extendedPanId == null || extendedPanId.length != LENGTH_EXTENDED_PAN_ID) {
throw new IllegalArgumentException(
"extendedPanId is null or length is not "
+ LENGTH_EXTENDED_PAN_ID
+ ": "
+ Arrays.toString(extendedPanId));
}
mExtendedPanId = extendedPanId.clone();
mRouterEligibleForLeader = false;
}
/**
* Creates a new {@link Builder} object by copying the data in the given {@code specifier}
* object.
*/
public Builder(@NonNull ThreadNetworkSpecifier specifier) {
this(specifier.getExtendedPanId());
setActiveTimestamp(specifier.getActiveTimestamp());
setRouterEligibleForLeader(specifier.isRouterEligibleForLeader());
}
/** Sets the Active Timestamp of the Thread network. */
@NonNull
public Builder setActiveTimestamp(@Nullable OperationalDatasetTimestamp activeTimestamp) {
mActiveTimestamp = activeTimestamp;
return this;
}
/**
* Sets whether this device should be a leader during attachment when there are no nearby
* routers.
*/
@NonNull
public Builder setRouterEligibleForLeader(boolean eligible) {
mRouterEligibleForLeader = eligible;
return this;
}
/** Creates a new {@link ThreadNetworkSpecifier} object from values set so far. */
@NonNull
public ThreadNetworkSpecifier build() {
return new ThreadNetworkSpecifier(this);
}
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (C) 2024 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.thread;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.filters.SmallTest;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
/** Tests for {@link ThreadNetworkSpecifier}. */
@SmallTest
@RunWith(Parameterized.class)
public final class ThreadNetworkSpecifierTest {
public final byte[] mExtendedPanId;
public final OperationalDatasetTimestamp mActiveTimestamp;
public final boolean mRouterEligibleForLeader;
@Parameterized.Parameters
public static Collection specifierArguments() {
var timestampNow = OperationalDatasetTimestamp.fromInstant(Instant.now());
return Arrays.asList(
new Object[][] {
{new byte[] {0, 1, 2, 3, 4, 5, 6, 7}, null, false},
{new byte[] {1, 1, 1, 1, 2, 2, 2, 2}, timestampNow, true},
{new byte[] {1, 1, 1, 1, 2, 2, 2, 2}, timestampNow, false},
});
}
public ThreadNetworkSpecifierTest(
byte[] extendedPanId,
OperationalDatasetTimestamp activeTimestamp,
boolean routerEligibleForLeader) {
mExtendedPanId = extendedPanId.clone();
mActiveTimestamp = activeTimestamp;
mRouterEligibleForLeader = routerEligibleForLeader;
}
@Test
public void parcelable_parcelingIsLossLess() {
ThreadNetworkSpecifier specifier =
new ThreadNetworkSpecifier.Builder(mExtendedPanId)
.setActiveTimestamp(mActiveTimestamp)
.setRouterEligibleForLeader(mRouterEligibleForLeader)
.build();
assertParcelingIsLossless(specifier);
}
@Test
public void builder_correctValuesAreSet() {
ThreadNetworkSpecifier specifier =
new ThreadNetworkSpecifier.Builder(mExtendedPanId)
.setActiveTimestamp(mActiveTimestamp)
.setRouterEligibleForLeader(mRouterEligibleForLeader)
.build();
assertThat(specifier.getExtendedPanId()).isEqualTo(mExtendedPanId);
assertThat(specifier.getActiveTimestamp()).isEqualTo(mActiveTimestamp);
assertThat(specifier.isRouterEligibleForLeader()).isEqualTo(mRouterEligibleForLeader);
}
@Test
public void builderConstructor_specifiersAreEqual() {
ThreadNetworkSpecifier specifier1 =
new ThreadNetworkSpecifier.Builder(mExtendedPanId)
.setActiveTimestamp(mActiveTimestamp)
.setRouterEligibleForLeader(mRouterEligibleForLeader)
.build();
ThreadNetworkSpecifier specifier2 = new ThreadNetworkSpecifier.Builder(specifier1).build();
assertThat(specifier1).isEqualTo(specifier2);
}
@Test
public void equalsTester() {
var timestampNow = OperationalDatasetTimestamp.fromInstant(Instant.now());
new EqualsTester()
.addEqualityGroup(
new ThreadNetworkSpecifier.Builder(new byte[] {0, 1, 2, 3, 4, 5, 6, 7})
.setActiveTimestamp(timestampNow)
.setRouterEligibleForLeader(true)
.build(),
new ThreadNetworkSpecifier.Builder(new byte[] {0, 1, 2, 3, 4, 5, 6, 7})
.setActiveTimestamp(timestampNow)
.setRouterEligibleForLeader(true)
.build())
.addEqualityGroup(
new ThreadNetworkSpecifier.Builder(new byte[] {0, 1, 2, 3, 4, 5, 6, 7})
.setActiveTimestamp(null)
.setRouterEligibleForLeader(false)
.build(),
new ThreadNetworkSpecifier.Builder(new byte[] {0, 1, 2, 3, 4, 5, 6, 7})
.setActiveTimestamp(null)
.setRouterEligibleForLeader(false)
.build())
.addEqualityGroup(
new ThreadNetworkSpecifier.Builder(new byte[] {1, 1, 1, 1, 2, 2, 2, 2})
.setActiveTimestamp(null)
.setRouterEligibleForLeader(false)
.build(),
new ThreadNetworkSpecifier.Builder(new byte[] {1, 1, 1, 1, 2, 2, 2, 2})
.setActiveTimestamp(null)
.setRouterEligibleForLeader(false)
.build())
.testEquals();
}
}