Merge "[Thread] move createRandomDataset to ThreadNetworkController" into main
This commit is contained in:
commit
7cca114efc
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