Merge "[Thread] add documentation for Pending Dataset constructor" into main
diff --git a/framework-t/api/OWNERS b/framework-t/api/OWNERS
index 607f85a..8ef735c 100644
--- a/framework-t/api/OWNERS
+++ b/framework-t/api/OWNERS
@@ -1,2 +1,3 @@
 file:platform/packages/modules/Connectivity:main:/nearby/OWNERS
 file:platform/packages/modules/Connectivity:main:/remoteauth/OWNERS
+file:platform/packages/modules/Connectivity:main:/thread/OWNERS
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index 08c2e66..b285d85 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -418,7 +418,6 @@
 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 @@
   }
 
   @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>);
diff --git a/framework/src/android/net/LocalNetworkConfig.java b/framework/src/android/net/LocalNetworkConfig.java
index fca7fd1..e1b33e8 100644
--- a/framework/src/android/net/LocalNetworkConfig.java
+++ b/framework/src/android/net/LocalNetworkConfig.java
@@ -82,6 +82,15 @@
         dest.writeParcelable(mDownstreamMulticastRoutingConfig, flags);
     }
 
+    @Override
+    public String toString() {
+        return "LocalNetworkConfig{"
+                + "UpstreamSelector=" + mUpstreamSelector
+                + ", UpstreamMulticastConfig=" + mUpstreamMulticastRoutingConfig
+                + ", DownstreamMulticastConfig=" + mDownstreamMulticastRoutingConfig
+                + '}';
+    }
+
     public static final @NonNull Creator<LocalNetworkConfig> CREATOR = new Creator<>() {
         public LocalNetworkConfig createFromParcel(Parcel in) {
             final NetworkRequest upstreamSelector = in.readParcelable(null);
diff --git a/framework/src/android/net/MulticastRoutingConfig.java b/framework/src/android/net/MulticastRoutingConfig.java
index ebd9fc5..6f4ab11 100644
--- a/framework/src/android/net/MulticastRoutingConfig.java
+++ b/framework/src/android/net/MulticastRoutingConfig.java
@@ -159,6 +159,24 @@
         }
     };
 
+    @Override
+    public String toString() {
+        return "MulticastRoutingConfig{"
+                + "ForwardingMode=" + forwardingModeToString(mForwardingMode)
+                + ", MinScope=" + mMinScope
+                + ", ListeningAddresses=" + mListeningAddresses
+                + '}';
+    }
+
+    private static String forwardingModeToString(final int forwardingMode) {
+        switch (forwardingMode) {
+            case FORWARD_NONE: return "NONE";
+            case FORWARD_SELECTED: return "SELECTED";
+            case FORWARD_WITH_MIN_SCOPE: return "WITH_MIN_SCOPE";
+            default: return "UNKNOWN";
+        }
+    }
+
     public static class Builder {
         @MulticastForwardingMode
         private final int mForwardingMode;
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 47e0bd9..7d1644e 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -9006,6 +9006,9 @@
             return;
         }
 
+        if (VDBG) {
+            Log.v(TAG, "Update local network config " + nai.network.netId + " : " + newConfig);
+        }
         final LocalNetworkConfig.Builder configBuilder = new LocalNetworkConfig.Builder();
         // TODO : apply the diff for multicast routing.
         configBuilder.setUpstreamMulticastRoutingConfig(
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
index 235f7de..1dab548 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
@@ -29,6 +29,7 @@
 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED
 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_THREAD
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
 import android.net.NetworkRequest
 import android.net.NetworkScore
@@ -472,4 +473,55 @@
         cb.expect<Lost>(localAgent.network)
         cb.assertNoCallback()
     }
+
+    @Test
+    fun testLocalNetworkUnwanted_withUpstream() {
+        doTestLocalNetworkUnwanted(true)
+    }
+
+    @Test
+    fun testLocalNetworkUnwanted_withoutUpstream() {
+        doTestLocalNetworkUnwanted(false)
+    }
+
+    fun doTestLocalNetworkUnwanted(haveUpstream: Boolean) {
+        deps.setBuildSdk(VERSION_V)
+
+        val nr = NetworkRequest.Builder().addCapability(NET_CAPABILITY_LOCAL_NETWORK).build()
+        val requestCb = TestableNetworkCallback()
+        cm.requestNetwork(nr, requestCb)
+        val listenCb = TestableNetworkCallback()
+        cm.registerNetworkCallback(nr, listenCb)
+
+        val upstream = if (haveUpstream) {
+            Agent(score = keepScore(), lp = lp("wifi0"),
+                    nc = nc(TRANSPORT_WIFI)).also { it.connect() }
+        } else {
+            null
+        }
+
+        // Set up a local agent.
+        val lnc = LocalNetworkConfig.Builder().apply {
+            if (haveUpstream) {
+                setUpstreamSelector(NetworkRequest.Builder()
+                        .addTransportType(TRANSPORT_WIFI)
+                        .build())
+            }
+        }.build()
+        val localAgent = Agent(nc = nc(TRANSPORT_THREAD, NET_CAPABILITY_LOCAL_NETWORK),
+                lp = lp("local0"),
+                lnc = lnc,
+                score = FromS(NetworkScore.Builder().build())
+        )
+        localAgent.connect()
+
+        requestCb.expectAvailableCallbacks(localAgent.network,
+                validated = false, upstream = upstream?.network)
+        listenCb.expectAvailableCallbacks(localAgent.network,
+                validated = false, upstream = upstream?.network)
+
+        cm.unregisterNetworkCallback(requestCb)
+
+        listenCb.expect<Lost>()
+    }
 }
diff --git a/thread/framework/java/android/net/thread/ActiveOperationalDataset.java b/thread/framework/java/android/net/thread/ActiveOperationalDataset.java
index c9b047a..b74a15a 100644
--- a/thread/framework/java/android/net/thread/ActiveOperationalDataset.java
+++ b/thread/framework/java/android/net/thread/ActiveOperationalDataset.java
@@ -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 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 @@
     /** @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 @@
         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 @@
         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 @@
          * @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 @@
                                 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 @@
          *
          * <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 @@
          *
          * <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 @@
             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,
diff --git a/thread/framework/java/android/net/thread/IThreadNetworkController.aidl b/thread/framework/java/android/net/thread/IThreadNetworkController.aidl
index 0e62b0b..51e4d88 100644
--- a/thread/framework/java/android/net/thread/IThreadNetworkController.aidl
+++ b/thread/framework/java/android/net/thread/IThreadNetworkController.aidl
@@ -40,4 +40,5 @@
     void leave(in IOperationReceiver receiver);
 
     int getThreadVersion();
+    void createRandomizedDataset(String networkName, IActiveOperationalDatasetReceiver receiver);
 }
diff --git a/thread/framework/java/android/net/thread/ThreadNetworkController.java b/thread/framework/java/android/net/thread/ThreadNetworkController.java
index 9d6a257..ec39db4 100644
--- a/thread/framework/java/android/net/thread/ThreadNetworkController.java
+++ b/thread/framework/java/android/net/thread/ThreadNetworkController.java
@@ -113,6 +113,33 @@
         }
     }
 
+    /**
+     * 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 @@
         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;
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 3470f27..6c9a775 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -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.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.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 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 @@
     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 @@
         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(
diff --git a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
index 39df21b..0e76930 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
@@ -70,7 +70,7 @@
     // 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 @@
                                     + "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 @@
 
     @Test
     public void parcelable_parcelingIsLossLess() {
-        ActiveOperationalDataset dataset = ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET);
+        ActiveOperationalDataset dataset =
+                ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET_TLVS);
 
         assertParcelingIsLossless(dataset);
     }
@@ -126,7 +130,8 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
     @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 @@
 
     @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 @@
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @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 @@
 
     @Test
     public void toThreadTlvs_conversionIsLossLess() {
-        ActiveOperationalDataset dataset1 = ActiveOperationalDataset.createRandomDataset();
+        ActiveOperationalDataset dataset1 = DEFAULT_DATASET;
 
         ActiveOperationalDataset dataset2 =
                 ActiveOperationalDataset.fromThreadTlvs(dataset1.toThreadTlvs());
@@ -465,9 +474,7 @@
                 };
 
         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 @@
     @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 @@
         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 @@
     @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 @@
     @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 @@
     @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 @@
 
     @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 @@
     @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 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 @@
     @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 @@
 
     @Test
     public void builder_setEmptyChannelMask_throwsIllegalArgument() {
-        Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
+        Builder builder = new Builder();
 
         assertThrows(
                 IllegalArgumentException.class,
@@ -623,7 +618,7 @@
     @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 @@
 
     @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 @@
 
     @Test
     public void builder_meshLocalPrefixNotStartWith0xfd_throwsIllegalArguments() {
-        Builder builder = new Builder(ActiveOperationalDataset.createRandomDataset());
+        Builder builder = new Builder();
 
         assertThrows(
                 IllegalArgumentException.class,
@@ -666,9 +661,7 @@
     @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 @@
     @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 @@
     @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();
 
diff --git a/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java b/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
index 7a49957..0bb18ce 100644
--- a/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
@@ -25,8 +25,10 @@
 
 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 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 @@
     }
 
     @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 @@
     }
 
     @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 @@
     }
 
     @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 @@
     }
 
     @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 @@
     }
 
     @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 @@
     }
 
     @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));
 
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
index cfe310c..e17dd02 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
@@ -132,6 +132,13 @@
         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 @@
         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 @@
     }
 
     @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 @@
         for (ThreadNetworkController controller : getAllControllers()) {
             ActiveOperationalDataset activeDataset1 =
                     new ActiveOperationalDataset.Builder(
-                                    ActiveOperationalDataset.createRandomDataset())
+                                    newRandomizedDataset("TestNet", controller))
                             .setNetworkKey(KEY_1)
                             .build();
             ActiveOperationalDataset activeDataset2 =
@@ -385,7 +392,7 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
             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);
+        }
+    }
 }
diff --git a/thread/tests/unit/src/android/net/thread/ActiveOperationalDatasetTest.java b/thread/tests/unit/src/android/net/thread/ActiveOperationalDatasetTest.java
index 78eb3d0..7284968 100644
--- a/thread/tests/unit/src/android/net/thread/ActiveOperationalDatasetTest.java
+++ b/thread/tests/unit/src/android/net/thread/ActiveOperationalDatasetTest.java
@@ -20,14 +20,7 @@
 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 @@
     // 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 @@
 
     @Test
     public void fromThreadTlvs_containsUnknownTlvs_unknownTlvsRetained() {
-        byte[] datasetWithUnknownTlvs = addTlv(VALID_DATASET, "AA01FFBB020102");
+        byte[] datasetWithUnknownTlvs = addTlv(VALID_DATASET_TLVS, "AA01FFBB020102");
 
         ActiveOperationalDataset dataset1 =
                 ActiveOperationalDataset.fromThreadTlvs(datasetWithUnknownTlvs);
@@ -98,66 +91,8 @@
     }
 
     @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 @@
 
     @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});