[Thread] move createRandomDataset to ThreadNetworkController
This commit renames `createRandomDataset` to `createRandomizedDataset` and moves this method to ThreadNetworkController class. The reason for moving this method is that we rely on the service side to know which channels are currently supported by this device and there is no safe way for ActiveOperationalDataset to created a randomized full dataset by its own. Moving this method to ThreadNetworkController also allows us to support future platform-dependend configurations. Bug: 308117328 Bug: 307939803 Change-Id: I0b0b7b727c3b326ab59bb1299ace15df1ff1ad5e
This commit is contained in:
parent
b1c75dbe8c
commit
af4f1f576c
9 changed files with 362 additions and 284 deletions
|
|
@ -418,7 +418,6 @@ package android.net.nsd {
|
|||
package android.net.thread {
|
||||
|
||||
@FlaggedApi("com.android.net.thread.flags.thread_enabled") public final class ActiveOperationalDataset implements android.os.Parcelable {
|
||||
method @NonNull public static android.net.thread.ActiveOperationalDataset createRandomDataset();
|
||||
method public int describeContents();
|
||||
method @NonNull public static android.net.thread.ActiveOperationalDataset fromThreadTlvs(@NonNull byte[]);
|
||||
method @NonNull public android.net.thread.OperationalDatasetTimestamp getActiveTimestamp();
|
||||
|
|
@ -493,6 +492,7 @@ package android.net.thread {
|
|||
}
|
||||
|
||||
@FlaggedApi("com.android.net.thread.flags.thread_enabled") public final class ThreadNetworkController {
|
||||
method public void createRandomizedDataset(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.thread.ActiveOperationalDataset,android.net.thread.ThreadNetworkException>);
|
||||
method public int getThreadVersion();
|
||||
method public static boolean isAttached(int);
|
||||
method @RequiresPermission("android.permission.THREAD_NETWORK_PRIVILEGED") public void join(@NonNull android.net.thread.ActiveOperationalDataset, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.net.thread.ThreadNetworkException>);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package android.net.thread;
|
||||
|
||||
import static android.net.thread.ActiveOperationalDataset.SecurityPolicy.DEFAULT_ROTATION_TIME_HOURS;
|
||||
|
||||
import static com.android.internal.util.Preconditions.checkArgument;
|
||||
import static com.android.internal.util.Preconditions.checkState;
|
||||
import static com.android.net.module.util.HexDump.dumpHexString;
|
||||
|
|
@ -41,26 +39,25 @@ import com.android.internal.annotations.VisibleForTesting;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Data interface for managing a Thread Active Operational Dataset.
|
||||
*
|
||||
* <p>An example usage of creating an Active Operational Dataset with random parameters:
|
||||
* <p>An example usage of creating an Active Operational Dataset with randomized parameters:
|
||||
*
|
||||
* <pre>{@code
|
||||
* ActiveOperationalDataset activeDataset = ActiveOperationalDataset.createRandomDataset();
|
||||
* ActiveOperationalDataset activeDataset = controller.createRandomizedDataset("MyNet");
|
||||
* }</pre>
|
||||
*
|
||||
* <p>or random Dataset with customized Network Name:
|
||||
* <p>or randomized Dataset with customized channel:
|
||||
*
|
||||
* <pre>{@code
|
||||
* ActiveOperationalDataset activeDataset =
|
||||
* new ActiveOperationalDataset.Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
* .setNetworkName("MyThreadNet").build();
|
||||
* new ActiveOperationalDataset.Builder(controller.createRandomizedDataset("MyNet"))
|
||||
* .setChannel(CHANNEL_PAGE_24_GHZ, 17)
|
||||
* .setActiveTimestamp(OperationalDatasetTimestamp.fromInstant(Instant.now()))
|
||||
* .build();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>If the Active Operational Dataset is already known as <a
|
||||
|
|
@ -116,7 +113,9 @@ public final class ActiveOperationalDataset implements Parcelable {
|
|||
/** @hide */
|
||||
@VisibleForTesting public static final int TYPE_CHANNEL_MASK = 53;
|
||||
|
||||
private static final byte MESH_LOCAL_PREFIX_FIRST_BYTE = (byte) 0xfd;
|
||||
/** @hide */
|
||||
public static final byte MESH_LOCAL_PREFIX_FIRST_BYTE = (byte) 0xfd;
|
||||
|
||||
private static final int LENGTH_CHANNEL = 3;
|
||||
private static final int LENGTH_PAN_ID = 2;
|
||||
|
||||
|
|
@ -344,86 +343,6 @@ public final class ActiveOperationalDataset implements Parcelable {
|
|||
outputStream.write(entries, 0, entries.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ActiveOperationalDataset} object with randomized or default parameters.
|
||||
*
|
||||
* <p>The randomized (or default) value for each parameter:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code Active Timestamp} defaults to {@code new OperationalDatasetTimestamp(1, 0,
|
||||
* false)}
|
||||
* <li>{@code Network Name} defaults to "THREAD-PAN-<PAN ID decimal>", for example
|
||||
* "THREAD-PAN-12345"
|
||||
* <li>{@code Extended PAN ID} filled with randomly generated bytes
|
||||
* <li>{@code PAN ID} randomly generated integer in range of [0, 0xfffe]
|
||||
* <li>{@code Channel Page} defaults to {@link #CHANNEL_PAGE_24_GHZ}
|
||||
* <li>{@code Channel} randomly selected channel in range of [{@link #CHANNEL_MIN_24_GHZ},
|
||||
* {@link #CHANNEL_MAX_24_GHZ}]
|
||||
* <li>{@code Channel Mask} all bits from {@link #CHANNEL_MIN_24_GHZ} to {@link
|
||||
* #CHANNEL_MAX_24_GHZ} are set to {@code true}
|
||||
* <li>{@code PSKc} filled with bytes generated by secure random generator
|
||||
* <li>{@code Network Key} filled with bytes generated by secure random generator
|
||||
* <li>{@code Mesh-local Prefix} filled with randomly generated bytes except that the first
|
||||
* byte is always set to {@code 0xfd}
|
||||
* <li>{@code Security Policy} defaults to {@code new SecurityPolicy(
|
||||
* DEFAULT_ROTATION_TIME_HOURS, new byte[]{(byte)0xff, (byte)0xf8})}. This is the default
|
||||
* values required by the Thread 1.2 specification
|
||||
* </ul>
|
||||
*
|
||||
* <p>This method is the recommended way to create a randomized operational dataset for a new
|
||||
* Thread network. It may be desired to change one or more of the generated value(s). For
|
||||
* example, to use a more meaningful Network Name. To do that, create a new {@link Builder}
|
||||
* object from this dataset with {@link Builder#Builder(ActiveOperationalDataset)} and override
|
||||
* the value with the setters of {@link Builder}.
|
||||
*
|
||||
* <p>Note that it's highly discouraged to change the randomly generated Extended PAN ID,
|
||||
* Network Key or PSKc, as it will compromise the security of a Thread network.
|
||||
*/
|
||||
@NonNull
|
||||
public static ActiveOperationalDataset createRandomDataset() {
|
||||
return createRandomDataset(new Random(Instant.now().toEpochMilli()), new SecureRandom());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@VisibleForTesting
|
||||
public static ActiveOperationalDataset createRandomDataset(
|
||||
Random random, SecureRandom secureRandom) {
|
||||
int panId = random.nextInt(/* bound= */ 0xffff);
|
||||
byte[] meshLocalPrefix = newRandomBytes(random, LENGTH_MESH_LOCAL_PREFIX_BITS / 8);
|
||||
meshLocalPrefix[0] = MESH_LOCAL_PREFIX_FIRST_BYTE;
|
||||
|
||||
SparseArray<byte[]> channelMask = new SparseArray<>(1);
|
||||
channelMask.put(CHANNEL_PAGE_24_GHZ, new byte[] {0x00, 0x1f, (byte) 0xff, (byte) 0xe0});
|
||||
|
||||
return new Builder()
|
||||
.setActiveTimestamp(
|
||||
new OperationalDatasetTimestamp(
|
||||
/* seconds= */ 1,
|
||||
/* ticks= */ 0,
|
||||
/* isAuthoritativeSource= */ false))
|
||||
.setExtendedPanId(newRandomBytes(random, LENGTH_EXTENDED_PAN_ID))
|
||||
.setPanId(panId)
|
||||
.setNetworkName("THREAD-PAN-" + panId)
|
||||
.setChannel(
|
||||
CHANNEL_PAGE_24_GHZ,
|
||||
random.nextInt(CHANNEL_MAX_24_GHZ - CHANNEL_MIN_24_GHZ + 1)
|
||||
+ CHANNEL_MIN_24_GHZ)
|
||||
.setChannelMask(channelMask)
|
||||
.setPskc(newRandomBytes(secureRandom, LENGTH_PSKC))
|
||||
.setNetworkKey(newRandomBytes(secureRandom, LENGTH_NETWORK_KEY))
|
||||
.setMeshLocalPrefix(meshLocalPrefix)
|
||||
.setSecurityPolicy(
|
||||
new SecurityPolicy(
|
||||
DEFAULT_ROTATION_TIME_HOURS, new byte[] {(byte) 0xff, (byte) 0xf8}))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static byte[] newRandomBytes(Random random, int length) {
|
||||
byte[] result = new byte[length];
|
||||
random.nextBytes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean areByteSparseArraysEqual(
|
||||
@NonNull SparseArray<byte[]> first, @NonNull SparseArray<byte[]> second) {
|
||||
if (first == second) {
|
||||
|
|
@ -683,6 +602,20 @@ public final class ActiveOperationalDataset implements Parcelable {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
static String checkNetworkName(@NonNull String networkName) {
|
||||
requireNonNull(networkName, "networkName cannot be null");
|
||||
|
||||
int nameLength = networkName.getBytes(UTF_8).length;
|
||||
checkArgument(
|
||||
nameLength >= LENGTH_MIN_NETWORK_NAME_BYTES
|
||||
&& nameLength <= LENGTH_MAX_NETWORK_NAME_BYTES,
|
||||
"Invalid network name (length = %d, expectedLengthRange = [%d, %d])",
|
||||
nameLength,
|
||||
LENGTH_MIN_NETWORK_NAME_BYTES,
|
||||
LENGTH_MAX_NETWORK_NAME_BYTES);
|
||||
return networkName;
|
||||
}
|
||||
|
||||
/** The builder for creating {@link ActiveOperationalDataset} objects. */
|
||||
public static final class Builder {
|
||||
private OperationalDatasetTimestamp mActiveTimestamp;
|
||||
|
|
@ -748,7 +681,7 @@ public final class ActiveOperationalDataset implements Parcelable {
|
|||
* @param networkName the name of the Thread network
|
||||
* @throws IllegalArgumentException if length of the UTF-8 representation of {@code
|
||||
* networkName} isn't in range of [{@link #LENGTH_MIN_NETWORK_NAME_BYTES}, {@link
|
||||
* #LENGTH_MAX_NETWORK_NAME_BYTES}].
|
||||
* #LENGTH_MAX_NETWORK_NAME_BYTES}]
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setNetworkName(
|
||||
|
|
@ -757,26 +690,16 @@ public final class ActiveOperationalDataset implements Parcelable {
|
|||
min = LENGTH_MIN_NETWORK_NAME_BYTES,
|
||||
max = LENGTH_MAX_NETWORK_NAME_BYTES)
|
||||
String networkName) {
|
||||
requireNonNull(networkName, "networkName cannot be null");
|
||||
|
||||
int nameLength = networkName.getBytes(UTF_8).length;
|
||||
checkArgument(
|
||||
nameLength >= LENGTH_MIN_NETWORK_NAME_BYTES
|
||||
&& nameLength <= LENGTH_MAX_NETWORK_NAME_BYTES,
|
||||
"Invalid network name (length = %d, expectedLengthRange = [%d, %d])",
|
||||
nameLength,
|
||||
LENGTH_MIN_NETWORK_NAME_BYTES,
|
||||
LENGTH_MAX_NETWORK_NAME_BYTES);
|
||||
this.mNetworkName = networkName;
|
||||
this.mNetworkName = checkNetworkName(networkName);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Extended PAN ID.
|
||||
*
|
||||
* <p>Use with caution. A randomly generated Extended PAN ID should be used for real Thread
|
||||
* <p>Use with caution. A randomized Extended PAN ID should be used for real Thread
|
||||
* networks. It's discouraged to call this method to override the default value created by
|
||||
* {@link ActiveOperationalDataset#createRandomDataset} in production.
|
||||
* {@link ThreadNetworkController#createRandomizedDataset} in production.
|
||||
*
|
||||
* @throws IllegalArgumentException if length of {@code extendedPanId} is not {@link
|
||||
* #LENGTH_EXTENDED_PAN_ID}.
|
||||
|
|
@ -867,7 +790,7 @@ public final class ActiveOperationalDataset implements Parcelable {
|
|||
*
|
||||
* <p>Use with caution. A randomly generated PSKc should be used for real Thread networks.
|
||||
* It's discouraged to call this method to override the default value created by {@link
|
||||
* ActiveOperationalDataset#createRandomDataset} in production.
|
||||
* ThreadNetworkController#createRandomizedDataset} in production.
|
||||
*
|
||||
* @param pskc the key stretched version of the Commissioning Credential for the network
|
||||
* @throws IllegalArgumentException if length of {@code pskc} is not {@link #LENGTH_PSKC}
|
||||
|
|
@ -889,7 +812,7 @@ public final class ActiveOperationalDataset implements Parcelable {
|
|||
*
|
||||
* <p>Use with caution, randomly generated Network Key should be used for real Thread
|
||||
* networks. It's discouraged to call this method to override the default value created by
|
||||
* {@link ActiveOperationalDataset#createRandomDataset} in production.
|
||||
* {@link ThreadNetworkController#createRandomizedDataset} in production.
|
||||
*
|
||||
* @param networkKey a 128-bit security key-derivation key for the Thread Network
|
||||
* @throws IllegalArgumentException if length of {@code networkKey} is not {@link
|
||||
|
|
@ -930,8 +853,16 @@ public final class ActiveOperationalDataset implements Parcelable {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Mesh-Local Prefix.
|
||||
*
|
||||
* @param meshLocalPrefix the prefix used for realm-local traffic within the mesh
|
||||
* @throws IllegalArgumentException if {@code meshLocalPrefix} doesn't start with {@code
|
||||
* 0xfd} or has length other than {@code LENGTH_MESH_LOCAL_PREFIX_BITS / 8}
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
private Builder setMeshLocalPrefix(byte[] meshLocalPrefix) {
|
||||
public Builder setMeshLocalPrefix(byte[] meshLocalPrefix) {
|
||||
final int prefixLength = meshLocalPrefix.length * 8;
|
||||
checkArgument(
|
||||
prefixLength == LENGTH_MESH_LOCAL_PREFIX_BITS,
|
||||
|
|
|
|||
|
|
@ -40,4 +40,5 @@ interface IThreadNetworkController {
|
|||
void leave(in IOperationReceiver receiver);
|
||||
|
||||
int getThreadVersion();
|
||||
void createRandomizedDataset(String networkName, IActiveOperationalDatasetReceiver receiver);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,33 @@ public final class ThreadNetworkController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Active Operational Dataset with randomized parameters.
|
||||
*
|
||||
* <p>This method is the recommended way to create a randomized dataset which can be used with
|
||||
* {@link #join} to securely join this device to the specified network . It's highly discouraged
|
||||
* to change the randomly generated Extended PAN ID, Network Key or PSKc, as it will compromise
|
||||
* the security of a Thread network.
|
||||
*
|
||||
* @throws IllegalArgumentException if length of the UTF-8 representation of {@code networkName}
|
||||
* isn't in range of [{@link #LENGTH_MIN_NETWORK_NAME_BYTES}, {@link
|
||||
* #LENGTH_MAX_NETWORK_NAME_BYTES}]
|
||||
*/
|
||||
public void createRandomizedDataset(
|
||||
@NonNull String networkName,
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull OutcomeReceiver<ActiveOperationalDataset, ThreadNetworkException> receiver) {
|
||||
ActiveOperationalDataset.checkNetworkName(networkName);
|
||||
requireNonNull(executor, "executor cannot be null");
|
||||
requireNonNull(receiver, "receiver cannot be null");
|
||||
try {
|
||||
mControllerService.createRandomizedDataset(
|
||||
networkName, new ActiveDatasetReceiverProxy(executor, receiver));
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns {@code true} if {@code deviceRole} indicates an attached state. */
|
||||
public static boolean isAttached(@DeviceRole int deviceRole) {
|
||||
return deviceRole == DEVICE_ROLE_CHILD
|
||||
|
|
@ -449,6 +476,29 @@ public final class ThreadNetworkController {
|
|||
executor.execute(() -> receiver.onError(new ThreadNetworkException(errorCode, errorMsg)));
|
||||
}
|
||||
|
||||
private static final class ActiveDatasetReceiverProxy
|
||||
extends IActiveOperationalDatasetReceiver.Stub {
|
||||
final Executor mExecutor;
|
||||
final OutcomeReceiver<ActiveOperationalDataset, ThreadNetworkException> mResultReceiver;
|
||||
|
||||
ActiveDatasetReceiverProxy(
|
||||
@CallbackExecutor Executor executor,
|
||||
OutcomeReceiver<ActiveOperationalDataset, ThreadNetworkException> resultReceiver) {
|
||||
this.mExecutor = executor;
|
||||
this.mResultReceiver = resultReceiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(ActiveOperationalDataset dataset) {
|
||||
mExecutor.execute(() -> mResultReceiver.onResult(dataset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String errorMessage) {
|
||||
propagateError(mExecutor, mResultReceiver, errorCode, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class OperationReceiverProxy extends IOperationReceiver.Stub {
|
||||
final Executor mExecutor;
|
||||
final OutcomeReceiver<Void, ThreadNetworkException> mResultReceiver;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@
|
|||
|
||||
package com.android.server.thread;
|
||||
|
||||
import static android.net.thread.ActiveOperationalDataset.CHANNEL_PAGE_24_GHZ;
|
||||
import static android.net.thread.ActiveOperationalDataset.LENGTH_EXTENDED_PAN_ID;
|
||||
import static android.net.thread.ActiveOperationalDataset.LENGTH_MESH_LOCAL_PREFIX_BITS;
|
||||
import static android.net.thread.ActiveOperationalDataset.LENGTH_NETWORK_KEY;
|
||||
import static android.net.thread.ActiveOperationalDataset.LENGTH_PSKC;
|
||||
import static android.net.thread.ActiveOperationalDataset.MESH_LOCAL_PREFIX_FIRST_BYTE;
|
||||
import static android.net.thread.ActiveOperationalDataset.SecurityPolicy.DEFAULT_ROTATION_TIME_HOURS;
|
||||
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_DETACHED;
|
||||
import static android.net.thread.ThreadNetworkController.THREAD_VERSION_1_3;
|
||||
import static android.net.thread.ThreadNetworkException.ERROR_ABORTED;
|
||||
|
|
@ -52,10 +59,13 @@ import android.net.NetworkCapabilities;
|
|||
import android.net.NetworkProvider;
|
||||
import android.net.NetworkScore;
|
||||
import android.net.thread.ActiveOperationalDataset;
|
||||
import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
|
||||
import android.net.thread.IActiveOperationalDatasetReceiver;
|
||||
import android.net.thread.IOperationReceiver;
|
||||
import android.net.thread.IOperationalDatasetCallback;
|
||||
import android.net.thread.IStateCallback;
|
||||
import android.net.thread.IThreadNetworkController;
|
||||
import android.net.thread.OperationalDatasetTimestamp;
|
||||
import android.net.thread.PendingOperationalDataset;
|
||||
import android.net.thread.ThreadNetworkController;
|
||||
import android.net.thread.ThreadNetworkController.DeviceRole;
|
||||
|
|
@ -66,6 +76,7 @@ import android.os.Looper;
|
|||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.ServiceManagerWrapper;
|
||||
|
|
@ -78,9 +89,12 @@ import com.android.server.thread.openthread.OtDaemonState;
|
|||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
|
|
@ -112,6 +126,9 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
|
|||
private final LinkProperties mLinkProperties = new LinkProperties();
|
||||
private final OtDaemonCallbackProxy mOtDaemonCallbackProxy = new OtDaemonCallbackProxy();
|
||||
|
||||
// TODO(b/308310823): read supported channel from Thread dameon
|
||||
private final int mSupportedChannelMask = 0x07FFF800; // from channel 11 to 26
|
||||
|
||||
private IOtDaemon mOtDaemon;
|
||||
private NetworkAgent mNetworkAgent;
|
||||
|
||||
|
|
@ -304,6 +321,93 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
|
|||
return THREAD_VERSION_1_3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createRandomizedDataset(
|
||||
String networkName, IActiveOperationalDatasetReceiver receiver) {
|
||||
mHandler.post(
|
||||
() -> {
|
||||
ActiveOperationalDataset dataset =
|
||||
createRandomizedDatasetInternal(
|
||||
networkName,
|
||||
mSupportedChannelMask,
|
||||
Instant.now(),
|
||||
new Random(),
|
||||
new SecureRandom());
|
||||
try {
|
||||
receiver.onSuccess(dataset);
|
||||
} catch (RemoteException e) {
|
||||
// The client is dead, do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static ActiveOperationalDataset createRandomizedDatasetInternal(
|
||||
String networkName,
|
||||
int supportedChannelMask,
|
||||
Instant now,
|
||||
Random random,
|
||||
SecureRandom secureRandom) {
|
||||
int panId = random.nextInt(/* bound= */ 0xffff);
|
||||
final byte[] meshLocalPrefix = newRandomBytes(random, LENGTH_MESH_LOCAL_PREFIX_BITS / 8);
|
||||
meshLocalPrefix[0] = MESH_LOCAL_PREFIX_FIRST_BYTE;
|
||||
|
||||
final SparseArray<byte[]> channelMask = new SparseArray<>(1);
|
||||
channelMask.put(CHANNEL_PAGE_24_GHZ, channelMaskToByteArray(supportedChannelMask));
|
||||
|
||||
final byte[] securityFlags = new byte[] {(byte) 0xff, (byte) 0xf8};
|
||||
|
||||
return new ActiveOperationalDataset.Builder()
|
||||
.setActiveTimestamp(
|
||||
new OperationalDatasetTimestamp(
|
||||
now.getEpochSecond() & 0xffffffffffffL, 0, false))
|
||||
.setExtendedPanId(newRandomBytes(random, LENGTH_EXTENDED_PAN_ID))
|
||||
.setPanId(panId)
|
||||
.setNetworkName(networkName)
|
||||
.setChannel(CHANNEL_PAGE_24_GHZ, selectRandomChannel(supportedChannelMask, random))
|
||||
.setChannelMask(channelMask)
|
||||
.setPskc(newRandomBytes(secureRandom, LENGTH_PSKC))
|
||||
.setNetworkKey(newRandomBytes(secureRandom, LENGTH_NETWORK_KEY))
|
||||
.setMeshLocalPrefix(meshLocalPrefix)
|
||||
.setSecurityPolicy(new SecurityPolicy(DEFAULT_ROTATION_TIME_HOURS, securityFlags))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static byte[] newRandomBytes(Random random, int length) {
|
||||
byte[] result = new byte[length];
|
||||
random.nextBytes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static byte[] channelMaskToByteArray(int channelMask) {
|
||||
// Per Thread spec, a Channel Mask is:
|
||||
// A variable-length bit mask that identifies the channels within the channel page
|
||||
// (1 = selected, 0 = unselected). The channels are represented in most significant bit
|
||||
// order. For example, the most significant bit of the left-most byte indicates channel 0.
|
||||
// If channel 0 and channel 10 are selected, the mask would be: 80 20 00 00. For IEEE
|
||||
// 802.15.4-2006 2.4 GHz PHY, the ChannelMask is 27 bits and MaskLength is 4.
|
||||
//
|
||||
// The pass-in channelMask represents a channel K by (channelMask & (1 << K)), so here
|
||||
// needs to do bit-wise reverse to convert it to the Thread spec format in bytes.
|
||||
channelMask = Integer.reverse(channelMask);
|
||||
return new byte[] {
|
||||
(byte) (channelMask >>> 24),
|
||||
(byte) (channelMask >>> 16),
|
||||
(byte) (channelMask >>> 8),
|
||||
(byte) channelMask
|
||||
};
|
||||
}
|
||||
|
||||
private static int selectRandomChannel(int supportedChannelMask, Random random) {
|
||||
int num = random.nextInt(Integer.bitCount(supportedChannelMask));
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if ((supportedChannelMask & 1) == 1 && (num--) == 0) {
|
||||
return i;
|
||||
}
|
||||
supportedChannelMask >>>= 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void enforceAllCallingPermissionsGranted(String... permissions) {
|
||||
for (String permission : permissions) {
|
||||
mContext.enforceCallingPermission(
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
// PAN ID: 0xD9A0
|
||||
// PSKc: A245479C836D551B9CA557F7B9D351B4
|
||||
// Security Policy: 672 onrcb
|
||||
private static final byte[] VALID_DATASET =
|
||||
private static final byte[] VALID_DATASET_TLVS =
|
||||
base16().decode(
|
||||
"0E080000000000010000000300001335060004001FFFE002"
|
||||
+ "08ACC214689BC40BDF0708FD64DB1225F47E0B0510F26B31"
|
||||
|
|
@ -78,6 +78,9 @@ public final class ActiveOperationalDatasetTest {
|
|||
+ "642D643961300102D9A00410A245479C836D551B9CA557F7"
|
||||
+ "B9D351B40C0402A0FFF8");
|
||||
|
||||
private static final ActiveOperationalDataset DEFAULT_DATASET =
|
||||
ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET_TLVS);
|
||||
|
||||
private static byte[] removeTlv(byte[] dataset, int type) {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(dataset.length);
|
||||
int i = 0;
|
||||
|
|
@ -105,7 +108,8 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void parcelable_parcelingIsLossLess() {
|
||||
ActiveOperationalDataset dataset = ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET);
|
||||
ActiveOperationalDataset dataset =
|
||||
ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET_TLVS);
|
||||
|
||||
assertParcelingIsLossless(dataset);
|
||||
}
|
||||
|
|
@ -126,7 +130,8 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_invalidNetworkKeyTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET, TYPE_NETWORK_KEY, "05080000000000000000");
|
||||
byte[] invalidTlv =
|
||||
replaceTlv(VALID_DATASET_TLVS, TYPE_NETWORK_KEY, "05080000000000000000");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -135,7 +140,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noNetworkKeyTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_NETWORK_KEY);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_NETWORK_KEY);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -144,7 +149,8 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_invalidActiveTimestampTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET, TYPE_ACTIVE_TIMESTAMP, "0E0700000000010000");
|
||||
byte[] invalidTlv =
|
||||
replaceTlv(VALID_DATASET_TLVS, TYPE_ACTIVE_TIMESTAMP, "0E0700000000010000");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -153,7 +159,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noActiveTimestampTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_ACTIVE_TIMESTAMP);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_ACTIVE_TIMESTAMP);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -162,7 +168,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_invalidNetworkNameTlv_emptyName_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET, TYPE_NETWORK_NAME, "0300");
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET_TLVS, TYPE_NETWORK_NAME, "0300");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -173,7 +179,9 @@ public final class ActiveOperationalDatasetTest {
|
|||
public void fromThreadTlvs_invalidNetworkNameTlv_tooLongName_throwsIllegalArgument() {
|
||||
byte[] invalidTlv =
|
||||
replaceTlv(
|
||||
VALID_DATASET, TYPE_NETWORK_NAME, "03114142434445464748494A4B4C4D4E4F5051");
|
||||
VALID_DATASET_TLVS,
|
||||
TYPE_NETWORK_NAME,
|
||||
"03114142434445464748494A4B4C4D4E4F5051");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -182,7 +190,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noNetworkNameTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_NETWORK_NAME);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_NETWORK_NAME);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -191,7 +199,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_invalidChannelTlv_channelMissing_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET, TYPE_CHANNEL, "000100");
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET_TLVS, TYPE_CHANNEL, "000100");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -200,7 +208,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_undefinedChannelPage_success() {
|
||||
byte[] datasetTlv = replaceTlv(VALID_DATASET, TYPE_CHANNEL, "0003010020");
|
||||
byte[] datasetTlv = replaceTlv(VALID_DATASET_TLVS, TYPE_CHANNEL, "0003010020");
|
||||
|
||||
ActiveOperationalDataset dataset = ActiveOperationalDataset.fromThreadTlvs(datasetTlv);
|
||||
|
||||
|
|
@ -210,8 +218,8 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_invalid2P4GhzChannel_throwsIllegalArgument() {
|
||||
byte[] invalidTlv1 = replaceTlv(VALID_DATASET, TYPE_CHANNEL, "000300000A");
|
||||
byte[] invalidTlv2 = replaceTlv(VALID_DATASET, TYPE_CHANNEL, "000300001B");
|
||||
byte[] invalidTlv1 = replaceTlv(VALID_DATASET_TLVS, TYPE_CHANNEL, "000300000A");
|
||||
byte[] invalidTlv2 = replaceTlv(VALID_DATASET_TLVS, TYPE_CHANNEL, "000300001B");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -223,7 +231,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_valid2P4GhzChannelTlv_success() {
|
||||
byte[] validTlv = replaceTlv(VALID_DATASET, TYPE_CHANNEL, "0003000010");
|
||||
byte[] validTlv = replaceTlv(VALID_DATASET_TLVS, TYPE_CHANNEL, "0003000010");
|
||||
|
||||
ActiveOperationalDataset dataset = ActiveOperationalDataset.fromThreadTlvs(validTlv);
|
||||
|
||||
|
|
@ -232,7 +240,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noChannelTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_CHANNEL);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_CHANNEL);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -241,7 +249,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_prematureEndOfChannelMaskEntry_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET, TYPE_CHANNEL_MASK, "350100");
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET_TLVS, TYPE_CHANNEL_MASK, "350100");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -250,7 +258,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_inconsistentChannelMaskLength_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET, TYPE_CHANNEL_MASK, "3506000500010000");
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET_TLVS, TYPE_CHANNEL_MASK, "3506000500010000");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -261,7 +269,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
public void fromThreadTlvs_unsupportedChannelMaskLength_success() {
|
||||
ActiveOperationalDataset dataset =
|
||||
ActiveOperationalDataset.fromThreadTlvs(
|
||||
replaceTlv(VALID_DATASET, TYPE_CHANNEL_MASK, "350700050001000000"));
|
||||
replaceTlv(VALID_DATASET_TLVS, TYPE_CHANNEL_MASK, "350700050001000000"));
|
||||
|
||||
SparseArray<byte[]> channelMask = dataset.getChannelMask();
|
||||
assertThat(channelMask.size()).isEqualTo(1);
|
||||
|
|
@ -271,7 +279,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noChannelMaskTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_CHANNEL_MASK);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_CHANNEL_MASK);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -280,7 +288,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_invalidPanIdTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET, TYPE_PAN_ID, "010101");
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET_TLVS, TYPE_PAN_ID, "010101");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -289,7 +297,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noPanIdTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_PAN_ID);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_PAN_ID);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -298,7 +306,8 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_invalidExtendedPanIdTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET, TYPE_EXTENDED_PAN_ID, "020700010203040506");
|
||||
byte[] invalidTlv =
|
||||
replaceTlv(VALID_DATASET_TLVS, TYPE_EXTENDED_PAN_ID, "020700010203040506");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -307,7 +316,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noExtendedPanIdTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_EXTENDED_PAN_ID);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_EXTENDED_PAN_ID);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -317,7 +326,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void fromThreadTlvs_invalidPskcTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv =
|
||||
replaceTlv(VALID_DATASET, TYPE_PSKC, "0411000102030405060708090A0B0C0D0E0F10");
|
||||
replaceTlv(VALID_DATASET_TLVS, TYPE_PSKC, "0411000102030405060708090A0B0C0D0E0F10");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -326,7 +335,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noPskcTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_PSKC);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_PSKC);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -336,7 +345,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void fromThreadTlvs_invalidMeshLocalPrefixTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv =
|
||||
replaceTlv(VALID_DATASET, TYPE_MESH_LOCAL_PREFIX, "0709FD0001020304050607");
|
||||
replaceTlv(VALID_DATASET_TLVS, TYPE_MESH_LOCAL_PREFIX, "0709FD0001020304050607");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -345,7 +354,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noMeshLocalPrefixTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_MESH_LOCAL_PREFIX);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_MESH_LOCAL_PREFIX);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -354,7 +363,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_tooShortSecurityPolicyTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET, TYPE_SECURITY_POLICY, "0C0101");
|
||||
byte[] invalidTlv = replaceTlv(VALID_DATASET_TLVS, TYPE_SECURITY_POLICY, "0C0101");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -363,7 +372,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_noSecurityPolicyTlv_throwsIllegalArgument() {
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET, TYPE_SECURITY_POLICY);
|
||||
byte[] invalidTlv = removeTlv(VALID_DATASET_TLVS, TYPE_SECURITY_POLICY);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -429,7 +438,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_containsUnknownTlvs_unknownTlvsRetained() {
|
||||
final byte[] datasetWithUnknownTlvs = addTlv(VALID_DATASET, "AA01FFBB020102");
|
||||
final byte[] datasetWithUnknownTlvs = addTlv(VALID_DATASET_TLVS, "AA01FFBB020102");
|
||||
|
||||
ActiveOperationalDataset dataset =
|
||||
ActiveOperationalDataset.fromThreadTlvs(datasetWithUnknownTlvs);
|
||||
|
|
@ -443,7 +452,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void toThreadTlvs_conversionIsLossLess() {
|
||||
ActiveOperationalDataset dataset1 = ActiveOperationalDataset.createRandomDataset();
|
||||
ActiveOperationalDataset dataset1 = DEFAULT_DATASET;
|
||||
|
||||
ActiveOperationalDataset dataset2 =
|
||||
ActiveOperationalDataset.fromThreadTlvs(dataset1.toThreadTlvs());
|
||||
|
|
@ -465,9 +474,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
};
|
||||
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
.setNetworkKey(networkKey)
|
||||
.build();
|
||||
new Builder(DEFAULT_DATASET).setNetworkKey(networkKey).build();
|
||||
|
||||
assertThat(dataset.getNetworkKey()).isEqualTo(networkKey);
|
||||
}
|
||||
|
|
@ -475,7 +482,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setInvalidNetworkKey_throwsIllegalArgument() {
|
||||
byte[] invalidNetworkKey = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class, () -> builder.setNetworkKey(invalidNetworkKey));
|
||||
|
|
@ -486,9 +493,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
byte[] extendedPanId = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
|
||||
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
.setExtendedPanId(extendedPanId)
|
||||
.build();
|
||||
new Builder(DEFAULT_DATASET).setExtendedPanId(extendedPanId).build();
|
||||
|
||||
assertThat(dataset.getExtendedPanId()).isEqualTo(extendedPanId);
|
||||
}
|
||||
|
|
@ -496,31 +501,28 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setInvalidExtendedPanId_throwsIllegalArgument() {
|
||||
byte[] extendedPanId = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.setExtendedPanId(extendedPanId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_setValidPanId_success() {
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
.setPanId(0xfffe)
|
||||
.build();
|
||||
ActiveOperationalDataset dataset = new Builder(DEFAULT_DATASET).setPanId(0xfffe).build();
|
||||
|
||||
assertThat(dataset.getPanId()).isEqualTo(0xfffe);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_setInvalidPanId_throwsIllegalArgument() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.setPanId(0xffff));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_setInvalidChannel_throwsIllegalArgument() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.setChannel(0, 0));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.setChannel(0, 27));
|
||||
|
|
@ -529,9 +531,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setValid2P4GhzChannel_success() {
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
.setChannel(CHANNEL_PAGE_24_GHZ, 16)
|
||||
.build();
|
||||
new Builder(DEFAULT_DATASET).setChannel(CHANNEL_PAGE_24_GHZ, 16).build();
|
||||
|
||||
assertThat(dataset.getChannel()).isEqualTo(16);
|
||||
assertThat(dataset.getChannelPage()).isEqualTo(CHANNEL_PAGE_24_GHZ);
|
||||
|
|
@ -540,23 +540,21 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setValidNetworkName_success() {
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
.setNetworkName("ot-network")
|
||||
.build();
|
||||
new Builder(DEFAULT_DATASET).setNetworkName("ot-network").build();
|
||||
|
||||
assertThat(dataset.getNetworkName()).isEqualTo("ot-network");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_setEmptyNetworkName_throwsIllegalArgument() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.setNetworkName(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_setTooLongNetworkName_throwsIllegalArgument() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class, () -> builder.setNetworkName("openthread-network"));
|
||||
|
|
@ -564,7 +562,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void builder_setTooLongUtf8NetworkName_throwsIllegalArgument() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
// UTF-8 encoded length of "我的线程网络" is 18 bytes which exceeds the max length
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.setNetworkName("我的线程网络"));
|
||||
|
|
@ -573,9 +571,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setValidUtf8NetworkName_success() {
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
.setNetworkName("我的网络")
|
||||
.build();
|
||||
new Builder(DEFAULT_DATASET).setNetworkName("我的网络").build();
|
||||
|
||||
assertThat(dataset.getNetworkName()).isEqualTo("我的网络");
|
||||
}
|
||||
|
|
@ -584,8 +580,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
public void builder_setValidPskc_success() {
|
||||
byte[] pskc = base16().decode("A245479C836D551B9CA557F7B9D351B4");
|
||||
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset()).setPskc(pskc).build();
|
||||
ActiveOperationalDataset dataset = new Builder(DEFAULT_DATASET).setPskc(pskc).build();
|
||||
|
||||
assertThat(dataset.getPskc()).isEqualTo(pskc);
|
||||
}
|
||||
|
|
@ -593,18 +588,18 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setTooLongPskc_throwsIllegalArgument() {
|
||||
byte[] tooLongPskc = base16().decode("A245479C836D551B9CA557F7B9D351B400");
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.setPskc(tooLongPskc));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_setValidChannelMask_success() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
SparseArray<byte[]> channelMask = new SparseArray<byte[]>(1);
|
||||
channelMask.put(0, new byte[] {0x00, 0x00, 0x01, 0x00});
|
||||
|
||||
ActiveOperationalDataset dataset = builder.setChannelMask(channelMask).build();
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(DEFAULT_DATASET).setChannelMask(channelMask).build();
|
||||
|
||||
SparseArray<byte[]> resultChannelMask = dataset.getChannelMask();
|
||||
assertThat(resultChannelMask.size()).isEqualTo(1);
|
||||
|
|
@ -613,7 +608,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void builder_setEmptyChannelMask_throwsIllegalArgument() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -623,7 +618,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setValidActiveTimestamp_success() {
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
new Builder(DEFAULT_DATASET)
|
||||
.setActiveTimestamp(
|
||||
new OperationalDatasetTimestamp(
|
||||
/* seconds= */ 1,
|
||||
|
|
@ -638,7 +633,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void builder_wrongMeshLocalPrefixLength_throwsIllegalArguments() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
// The Mesh-Local Prefix length must be 64 bits
|
||||
assertThrows(
|
||||
|
|
@ -656,7 +651,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void builder_meshLocalPrefixNotStartWith0xfd_throwsIllegalArguments() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder();
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -666,9 +661,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setValidMeshLocalPrefix_success() {
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
.setMeshLocalPrefix(new IpPrefix("fd00::/64"))
|
||||
.build();
|
||||
new Builder(DEFAULT_DATASET).setMeshLocalPrefix(new IpPrefix("fd00::/64")).build();
|
||||
|
||||
assertThat(dataset.getMeshLocalPrefix()).isEqualTo(new IpPrefix("fd00::/64"));
|
||||
}
|
||||
|
|
@ -676,7 +669,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setValid1P2SecurityPolicy_success() {
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
new Builder(DEFAULT_DATASET)
|
||||
.setSecurityPolicy(
|
||||
new SecurityPolicy(672, new byte[] {(byte) 0xff, (byte) 0xf8}))
|
||||
.build();
|
||||
|
|
@ -689,7 +682,7 @@ public final class ActiveOperationalDatasetTest {
|
|||
@Test
|
||||
public void builder_setValid1P1SecurityPolicy_success() {
|
||||
ActiveOperationalDataset dataset =
|
||||
new Builder(ActiveOperationalDataset.createRandomDataset())
|
||||
new Builder(DEFAULT_DATASET)
|
||||
.setSecurityPolicy(new SecurityPolicy(672, new byte[] {(byte) 0xff}))
|
||||
.build();
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,10 @@ import static org.junit.Assert.assertThrows;
|
|||
|
||||
import android.net.IpPrefix;
|
||||
import android.net.thread.ActiveOperationalDataset;
|
||||
import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
|
||||
import android.net.thread.OperationalDatasetTimestamp;
|
||||
import android.net.thread.PendingOperationalDataset;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
|
@ -37,20 +39,36 @@ import com.google.common.testing.EqualsTester;
|
|||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.time.Duration;
|
||||
|
||||
/** Tests for {@link PendingOperationalDataset}. */
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class PendingOperationalDatasetTest {
|
||||
private static final ActiveOperationalDataset DEFAULT_ACTIVE_DATASET =
|
||||
ActiveOperationalDataset.createRandomDataset();
|
||||
private static ActiveOperationalDataset createActiveDataset() throws Exception {
|
||||
SparseArray<byte[]> channelMask = new SparseArray<>(1);
|
||||
channelMask.put(0, new byte[] {0x00, 0x1f, (byte) 0xff, (byte) 0xe0});
|
||||
|
||||
return new ActiveOperationalDataset.Builder()
|
||||
.setActiveTimestamp(new OperationalDatasetTimestamp(100, 10, false))
|
||||
.setExtendedPanId(new byte[] {0, 1, 2, 3, 4, 5, 6, 7})
|
||||
.setPanId(12345)
|
||||
.setNetworkName("defaultNet")
|
||||
.setChannel(0, 18)
|
||||
.setChannelMask(channelMask)
|
||||
.setPskc(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})
|
||||
.setNetworkKey(new byte[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
|
||||
.setMeshLocalPrefix(new IpPrefix(InetAddress.getByName("fd00::1"), 64))
|
||||
.setSecurityPolicy(new SecurityPolicy(672, new byte[] {(byte) 0xff, (byte) 0xf8}))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parcelable_parcelingIsLossLess() {
|
||||
public void parcelable_parcelingIsLossLess() throws Exception {
|
||||
PendingOperationalDataset dataset =
|
||||
new PendingOperationalDataset(
|
||||
DEFAULT_ACTIVE_DATASET,
|
||||
createActiveDataset(),
|
||||
new OperationalDatasetTimestamp(31536000, 200, false),
|
||||
Duration.ofHours(100));
|
||||
|
||||
|
|
@ -58,9 +76,15 @@ public final class PendingOperationalDatasetTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void equalityTests() {
|
||||
ActiveOperationalDataset activeDataset1 = ActiveOperationalDataset.createRandomDataset();
|
||||
ActiveOperationalDataset activeDataset2 = ActiveOperationalDataset.createRandomDataset();
|
||||
public void equalityTests() throws Exception {
|
||||
ActiveOperationalDataset activeDataset1 =
|
||||
new ActiveOperationalDataset.Builder(createActiveDataset())
|
||||
.setNetworkName("net1")
|
||||
.build();
|
||||
ActiveOperationalDataset activeDataset2 =
|
||||
new ActiveOperationalDataset.Builder(createActiveDataset())
|
||||
.setNetworkName("net2")
|
||||
.build();
|
||||
|
||||
new EqualsTester()
|
||||
.addEqualityGroup(
|
||||
|
|
@ -103,14 +127,15 @@ public final class PendingOperationalDatasetTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void constructor_correctValuesAreSet() {
|
||||
public void constructor_correctValuesAreSet() throws Exception {
|
||||
final ActiveOperationalDataset activeDataset = createActiveDataset();
|
||||
PendingOperationalDataset dataset =
|
||||
new PendingOperationalDataset(
|
||||
DEFAULT_ACTIVE_DATASET,
|
||||
activeDataset,
|
||||
new OperationalDatasetTimestamp(31536000, 200, false),
|
||||
Duration.ofHours(100));
|
||||
|
||||
assertThat(dataset.getActiveOperationalDataset()).isEqualTo(DEFAULT_ACTIVE_DATASET);
|
||||
assertThat(dataset.getActiveOperationalDataset()).isEqualTo(activeDataset);
|
||||
assertThat(dataset.getPendingTimestamp())
|
||||
.isEqualTo(new OperationalDatasetTimestamp(31536000, 200, false));
|
||||
assertThat(dataset.getDelayTimer()).isEqualTo(Duration.ofHours(100));
|
||||
|
|
@ -166,33 +191,35 @@ public final class PendingOperationalDatasetTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void fromThreadTlvs_completePendingDatasetTlvs_success() {
|
||||
public void fromThreadTlvs_completePendingDatasetTlvs_success() throws Exception {
|
||||
final ActiveOperationalDataset activeDataset = createActiveDataset();
|
||||
|
||||
// Type Length Value
|
||||
// 0x33 0x08 0x0000000000010000 (Pending Timestamp TLV)
|
||||
// 0x34 0x04 0x0000012C (Delay Timer TLV)
|
||||
final byte[] pendingTimestampAndDelayTimerTlvs =
|
||||
base16().decode("3308000000000001000034040000012C");
|
||||
final byte[] pendingDatasetTlvs =
|
||||
Bytes.concat(
|
||||
pendingTimestampAndDelayTimerTlvs, DEFAULT_ACTIVE_DATASET.toThreadTlvs());
|
||||
Bytes.concat(pendingTimestampAndDelayTimerTlvs, activeDataset.toThreadTlvs());
|
||||
|
||||
PendingOperationalDataset dataset =
|
||||
PendingOperationalDataset.fromThreadTlvs(pendingDatasetTlvs);
|
||||
|
||||
assertThat(dataset.getActiveOperationalDataset()).isEqualTo(DEFAULT_ACTIVE_DATASET);
|
||||
assertThat(dataset.getActiveOperationalDataset()).isEqualTo(activeDataset);
|
||||
assertThat(dataset.getPendingTimestamp())
|
||||
.isEqualTo(new OperationalDatasetTimestamp(1, 0, false));
|
||||
assertThat(dataset.getDelayTimer()).isEqualTo(Duration.ofMillis(300));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromThreadTlvs_PendingTimestampTlvIsMissing_throwsIllegalArgument() {
|
||||
public void fromThreadTlvs_PendingTimestampTlvIsMissing_throwsIllegalArgument()
|
||||
throws Exception {
|
||||
// Type Length Value
|
||||
// 0x34 0x04 0x00000064 (Delay Timer TLV)
|
||||
final byte[] pendingTimestampAndDelayTimerTlvs = base16().decode("34040000012C");
|
||||
final byte[] pendingDatasetTlvs =
|
||||
Bytes.concat(
|
||||
pendingTimestampAndDelayTimerTlvs, DEFAULT_ACTIVE_DATASET.toThreadTlvs());
|
||||
pendingTimestampAndDelayTimerTlvs, createActiveDataset().toThreadTlvs());
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -200,13 +227,13 @@ public final class PendingOperationalDatasetTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void fromThreadTlvs_delayTimerTlvIsMissing_throwsIllegalArgument() {
|
||||
public void fromThreadTlvs_delayTimerTlvIsMissing_throwsIllegalArgument() throws Exception {
|
||||
// Type Length Value
|
||||
// 0x33 0x08 0x0000000000010000 (Pending Timestamp TLV)
|
||||
final byte[] pendingTimestampAndDelayTimerTlvs = base16().decode("33080000000000010000");
|
||||
final byte[] pendingDatasetTlvs =
|
||||
Bytes.concat(
|
||||
pendingTimestampAndDelayTimerTlvs, DEFAULT_ACTIVE_DATASET.toThreadTlvs());
|
||||
pendingTimestampAndDelayTimerTlvs, createActiveDataset().toThreadTlvs());
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -214,8 +241,8 @@ public final class PendingOperationalDatasetTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void fromThreadTlvs_activeDatasetTlvs_throwsIllegalArgument() {
|
||||
final byte[] activeDatasetTlvs = DEFAULT_ACTIVE_DATASET.toThreadTlvs();
|
||||
public void fromThreadTlvs_activeDatasetTlvs_throwsIllegalArgument() throws Exception {
|
||||
final byte[] activeDatasetTlvs = createActiveDataset().toThreadTlvs();
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
|
@ -232,10 +259,10 @@ public final class PendingOperationalDatasetTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void toThreadTlvs_conversionIsLossLess() {
|
||||
public void toThreadTlvs_conversionIsLossLess() throws Exception {
|
||||
PendingOperationalDataset dataset1 =
|
||||
new PendingOperationalDataset(
|
||||
DEFAULT_ACTIVE_DATASET,
|
||||
createActiveDataset(),
|
||||
new OperationalDatasetTimestamp(31536000, 200, false),
|
||||
Duration.ofHours(100));
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,13 @@ public class ThreadNetworkControllerTest {
|
|||
getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
|
||||
}
|
||||
|
||||
private static ActiveOperationalDataset newRandomizedDataset(
|
||||
String networkName, ThreadNetworkController controller) throws Exception {
|
||||
SettableFuture<ActiveOperationalDataset> future = SettableFuture.create();
|
||||
controller.createRandomizedDataset(networkName, directExecutor(), future::set);
|
||||
return future.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS);
|
||||
}
|
||||
|
||||
private static boolean isAttached(ThreadNetworkController controller) throws Exception {
|
||||
return ThreadNetworkController.isAttached(getDeviceRole(controller));
|
||||
}
|
||||
|
|
@ -322,7 +329,7 @@ public class ThreadNetworkControllerTest {
|
|||
grantPermissions(PERMISSION_THREAD_NETWORK_PRIVILEGED);
|
||||
|
||||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
ActiveOperationalDataset activeDataset = ActiveOperationalDataset.createRandomDataset();
|
||||
ActiveOperationalDataset activeDataset = newRandomizedDataset("TestNet", controller);
|
||||
SettableFuture<Void> joinFuture = SettableFuture.create();
|
||||
|
||||
controller.join(activeDataset, mExecutor, newOutcomeReceiver(joinFuture));
|
||||
|
|
@ -335,11 +342,11 @@ public class ThreadNetworkControllerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void join_withoutPrivilegedPermission_throwsSecurityException() {
|
||||
public void join_withoutPrivilegedPermission_throwsSecurityException() throws Exception {
|
||||
dropPermissions();
|
||||
|
||||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
ActiveOperationalDataset activeDataset = ActiveOperationalDataset.createRandomDataset();
|
||||
ActiveOperationalDataset activeDataset = newRandomizedDataset("TestNet", controller);
|
||||
|
||||
assertThrows(
|
||||
SecurityException.class,
|
||||
|
|
@ -356,7 +363,7 @@ public class ThreadNetworkControllerTest {
|
|||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
ActiveOperationalDataset activeDataset1 =
|
||||
new ActiveOperationalDataset.Builder(
|
||||
ActiveOperationalDataset.createRandomDataset())
|
||||
newRandomizedDataset("TestNet", controller))
|
||||
.setNetworkKey(KEY_1)
|
||||
.build();
|
||||
ActiveOperationalDataset activeDataset2 =
|
||||
|
|
@ -385,7 +392,7 @@ public class ThreadNetworkControllerTest {
|
|||
grantPermissions(PERMISSION_THREAD_NETWORK_PRIVILEGED);
|
||||
|
||||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
ActiveOperationalDataset activeDataset = ActiveOperationalDataset.createRandomDataset();
|
||||
ActiveOperationalDataset activeDataset = newRandomizedDataset("TestNet", controller);
|
||||
SettableFuture<Void> joinFuture = SettableFuture.create();
|
||||
SettableFuture<Void> leaveFuture = SettableFuture.create();
|
||||
controller.join(activeDataset, mExecutor, newOutcomeReceiver(joinFuture));
|
||||
|
|
@ -413,7 +420,7 @@ public class ThreadNetworkControllerTest {
|
|||
grantPermissions(PERMISSION_THREAD_NETWORK_PRIVILEGED);
|
||||
|
||||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
ActiveOperationalDataset activeDataset = ActiveOperationalDataset.createRandomDataset();
|
||||
ActiveOperationalDataset activeDataset = newRandomizedDataset("TestNet", controller);
|
||||
SettableFuture<Void> joinFuture = SettableFuture.create();
|
||||
SettableFuture<Void> leaveFuture1 = SettableFuture.create();
|
||||
SettableFuture<Void> leaveFuture2 = SettableFuture.create();
|
||||
|
|
@ -437,7 +444,7 @@ public class ThreadNetworkControllerTest {
|
|||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
ActiveOperationalDataset activeDataset1 =
|
||||
new ActiveOperationalDataset.Builder(
|
||||
ActiveOperationalDataset.createRandomDataset())
|
||||
newRandomizedDataset("TestNet", controller))
|
||||
.setActiveTimestamp(new OperationalDatasetTimestamp(1L, 0, false))
|
||||
.setExtendedPanId(new byte[] {1, 1, 1, 1, 1, 1, 1, 1})
|
||||
.build();
|
||||
|
|
@ -473,7 +480,7 @@ public class ThreadNetworkControllerTest {
|
|||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
PendingOperationalDataset pendingDataset =
|
||||
new PendingOperationalDataset(
|
||||
ActiveOperationalDataset.createRandomDataset(),
|
||||
newRandomizedDataset("TestNet", controller),
|
||||
OperationalDatasetTimestamp.fromInstant(Instant.now()),
|
||||
Duration.ofSeconds(30));
|
||||
SettableFuture<Void> migrateFuture = SettableFuture.create();
|
||||
|
|
@ -496,9 +503,8 @@ public class ThreadNetworkControllerTest {
|
|||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
final ActiveOperationalDataset activeDataset =
|
||||
new ActiveOperationalDataset.Builder(
|
||||
ActiveOperationalDataset.createRandomDataset())
|
||||
newRandomizedDataset("testNet", controller))
|
||||
.setActiveTimestamp(new OperationalDatasetTimestamp(1L, 0, false))
|
||||
.setNetworkName("testNet")
|
||||
.build();
|
||||
ActiveOperationalDataset activeDataset1 =
|
||||
new ActiveOperationalDataset.Builder(activeDataset)
|
||||
|
|
@ -546,9 +552,8 @@ public class ThreadNetworkControllerTest {
|
|||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
final ActiveOperationalDataset activeDataset =
|
||||
new ActiveOperationalDataset.Builder(
|
||||
ActiveOperationalDataset.createRandomDataset())
|
||||
newRandomizedDataset("validName", controller))
|
||||
.setActiveTimestamp(new OperationalDatasetTimestamp(1L, 0, false))
|
||||
.setNetworkName("testNet")
|
||||
.build();
|
||||
ActiveOperationalDataset activeDataset1 =
|
||||
new ActiveOperationalDataset.Builder(activeDataset)
|
||||
|
|
@ -588,4 +593,35 @@ public class ThreadNetworkControllerTest {
|
|||
assertThat(getPendingOperationalDataset(controller)).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createRandomizedDataset_wrongNetworkNameLength_throwsIllegalArgumentException() {
|
||||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> controller.createRandomizedDataset("", mExecutor, dataset -> {}));
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
controller.createRandomizedDataset(
|
||||
"ANetNameIs17Bytes", mExecutor, dataset -> {}));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createRandomizedDataset_validNetworkName_success() throws Exception {
|
||||
for (ThreadNetworkController controller : getAllControllers()) {
|
||||
ActiveOperationalDataset dataset = newRandomizedDataset("validName", controller);
|
||||
|
||||
assertThat(dataset.getNetworkName()).isEqualTo("validName");
|
||||
assertThat(dataset.getPanId()).isLessThan(0xffff);
|
||||
assertThat(dataset.getChannelMask().size()).isAtLeast(1);
|
||||
assertThat(dataset.getExtendedPanId()).hasLength(8);
|
||||
assertThat(dataset.getNetworkKey()).hasLength(16);
|
||||
assertThat(dataset.getPskc()).hasLength(16);
|
||||
assertThat(dataset.getMeshLocalPrefix().getPrefixLength()).isEqualTo(64);
|
||||
assertThat(dataset.getMeshLocalPrefix().getRawAddress()[0]).isEqualTo((byte) 0xfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,14 +20,7 @@ import static com.google.common.io.BaseEncoding.base16;
|
|||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.net.IpPrefix;
|
||||
import android.net.thread.ActiveOperationalDataset.Builder;
|
||||
import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
|
||||
import android.util.SparseArray;
|
||||
|
|
@ -61,7 +54,7 @@ public class ActiveOperationalDatasetTest {
|
|||
// PAN ID: 0xD9A0
|
||||
// PSKc: A245479C836D551B9CA557F7B9D351B4
|
||||
// Security Policy: 672 onrcb
|
||||
private static final byte[] VALID_DATASET =
|
||||
private static final byte[] VALID_DATASET_TLVS =
|
||||
base16().decode(
|
||||
"0E080000000000010000000300001335060004001FFFE002"
|
||||
+ "08ACC214689BC40BDF0708FD64DB1225F47E0B0510F26B31"
|
||||
|
|
@ -83,7 +76,7 @@ public class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void fromThreadTlvs_containsUnknownTlvs_unknownTlvsRetained() {
|
||||
byte[] datasetWithUnknownTlvs = addTlv(VALID_DATASET, "AA01FFBB020102");
|
||||
byte[] datasetWithUnknownTlvs = addTlv(VALID_DATASET_TLVS, "AA01FFBB020102");
|
||||
|
||||
ActiveOperationalDataset dataset1 =
|
||||
ActiveOperationalDataset.fromThreadTlvs(datasetWithUnknownTlvs);
|
||||
|
|
@ -97,67 +90,9 @@ public class ActiveOperationalDatasetTest {
|
|||
assertThat(dataset2).isEqualTo(dataset1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createRandomDataset_fieldsAreRandomized() {
|
||||
// Always return the max bounded value
|
||||
doAnswer(invocation -> (int) invocation.getArgument(0) - 1)
|
||||
.when(mockRandom)
|
||||
.nextInt(anyInt());
|
||||
doAnswer(
|
||||
invocation -> {
|
||||
byte[] output = invocation.getArgument(0);
|
||||
for (int i = 0; i < output.length; ++i) {
|
||||
output[i] = (byte) (i + 10);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.when(mockRandom)
|
||||
.nextBytes(any(byte[].class));
|
||||
doAnswer(
|
||||
invocation -> {
|
||||
byte[] output = invocation.getArgument(0);
|
||||
for (int i = 0; i < output.length; ++i) {
|
||||
output[i] = (byte) (i + 30);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.when(mockSecureRandom)
|
||||
.nextBytes(any(byte[].class));
|
||||
|
||||
ActiveOperationalDataset dataset =
|
||||
ActiveOperationalDataset.createRandomDataset(mockRandom, mockSecureRandom);
|
||||
|
||||
assertThat(dataset.getActiveTimestamp())
|
||||
.isEqualTo(new OperationalDatasetTimestamp(1, 0, false));
|
||||
assertThat(dataset.getExtendedPanId())
|
||||
.isEqualTo(new byte[] {10, 11, 12, 13, 14, 15, 16, 17});
|
||||
assertThat(dataset.getMeshLocalPrefix())
|
||||
.isEqualTo(new IpPrefix("fd0b:0c0d:0e0f:1011::/64"));
|
||||
verify(mockRandom, times(2)).nextBytes(any(byte[].class));
|
||||
assertThat(dataset.getPanId()).isEqualTo(0xfffe); // PAN ID <= 0xfffe
|
||||
verify(mockRandom, times(1)).nextInt(eq(0xffff));
|
||||
assertThat(dataset.getChannel()).isEqualTo(26);
|
||||
verify(mockRandom, times(1)).nextInt(eq(16));
|
||||
assertThat(dataset.getChannelPage()).isEqualTo(0);
|
||||
assertThat(dataset.getChannelMask().size()).isEqualTo(1);
|
||||
assertThat(dataset.getPskc())
|
||||
.isEqualTo(
|
||||
new byte[] {
|
||||
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45
|
||||
});
|
||||
assertThat(dataset.getNetworkKey())
|
||||
.isEqualTo(
|
||||
new byte[] {
|
||||
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45
|
||||
});
|
||||
verify(mockSecureRandom, times(2)).nextBytes(any(byte[].class));
|
||||
assertThat(dataset.getSecurityPolicy())
|
||||
.isEqualTo(new SecurityPolicy(672, new byte[] {(byte) 0xff, (byte) 0xf8}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_buildWithTooLongTlvs_throwsIllegalState() {
|
||||
Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
|
||||
Builder builder = new Builder(ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET_TLVS));
|
||||
for (int i = 0; i < 10; i++) {
|
||||
builder.addUnknownTlv(i, new byte[20]);
|
||||
}
|
||||
|
|
@ -167,7 +102,8 @@ public class ActiveOperationalDatasetTest {
|
|||
|
||||
@Test
|
||||
public void builder_setUnknownTlvs_success() {
|
||||
ActiveOperationalDataset dataset1 = ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET);
|
||||
ActiveOperationalDataset dataset1 =
|
||||
ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET_TLVS);
|
||||
SparseArray<byte[]> unknownTlvs = new SparseArray<>(2);
|
||||
unknownTlvs.put(0x33, new byte[] {1, 2, 3});
|
||||
unknownTlvs.put(0x44, new byte[] {1, 2, 3, 4});
|
||||
|
|
|
|||
Loading…
Reference in a new issue