Merge "Thread: use network time for generating dataset" into main
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index b418ef6..02c6a09 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -105,6 +105,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
@@ -131,6 +132,8 @@
import java.net.Inet6Address;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
+import java.time.Clock;
+import java.time.DateTimeException;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
@@ -821,7 +824,6 @@
networkName,
supportedChannelMask,
preferredChannelMask,
- Instant.now(),
new Random(),
new SecureRandom());
@@ -839,9 +841,18 @@
String networkName,
int supportedChannelMask,
int preferredChannelMask,
- Instant now,
Random random,
SecureRandom secureRandom) {
+ boolean authoritative = false;
+ Instant now = Instant.now();
+ try {
+ Clock clock = SystemClock.currentNetworkTimeClock();
+ now = clock.instant();
+ authoritative = true;
+ } catch (DateTimeException e) {
+ Log.w(TAG, "Failed to get authoritative time", e);
+ }
+
int panId = random.nextInt(/* bound= */ 0xffff);
final byte[] meshLocalPrefix = newRandomBytes(random, LENGTH_MESH_LOCAL_PREFIX_BITS / 8);
meshLocalPrefix[0] = MESH_LOCAL_PREFIX_FIRST_BYTE;
@@ -855,7 +866,7 @@
return new ActiveOperationalDataset.Builder()
.setActiveTimestamp(
new OperationalDatasetTimestamp(
- now.getEpochSecond() & 0xffffffffffffL, 0, false))
+ now.getEpochSecond() & 0xffffffffffffL, 0, authoritative))
.setExtendedPanId(newRandomBytes(random, LENGTH_EXTENDED_PAN_ID))
.setPanId(panId)
.setNetworkName(networkName)
diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
index cf6eb44..8f60783 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
@@ -64,6 +64,7 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.provider.Settings;
@@ -75,6 +76,7 @@
import androidx.test.filters.SmallTest;
import com.android.connectivity.resources.R;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.connectivity.ConnectivityResources;
import com.android.server.thread.openthread.MeshcopTxtAttributes;
import com.android.server.thread.openthread.testing.FakeOtDaemon;
@@ -90,7 +92,10 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import java.time.Clock;
+import java.time.DateTimeException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
@@ -535,6 +540,53 @@
}
@Test
+ public void
+ createRandomizedDataset_noNetworkTimeClock_datasetActiveTimestampIsNotAuthoritative()
+ throws Exception {
+ MockitoSession session =
+ ExtendedMockito.mockitoSession().mockStatic(SystemClock.class).startMocking();
+ final IActiveOperationalDatasetReceiver mockReceiver =
+ ExtendedMockito.mock(IActiveOperationalDatasetReceiver.class);
+
+ try {
+ ExtendedMockito.when(SystemClock.currentNetworkTimeClock())
+ .thenThrow(new DateTimeException("fake throw"));
+ mService.createRandomizedDataset(DEFAULT_NETWORK_NAME, mockReceiver);
+ mTestLooper.dispatchAll();
+ } finally {
+ session.finishMocking();
+ }
+
+ verify(mockReceiver, never()).onError(anyInt(), anyString());
+ verify(mockReceiver, times(1)).onSuccess(mActiveDatasetCaptor.capture());
+ ActiveOperationalDataset activeDataset = mActiveDatasetCaptor.getValue();
+ assertThat(activeDataset.getActiveTimestamp().isAuthoritativeSource()).isFalse();
+ }
+
+ @Test
+ public void createRandomizedDataset_hasNetworkTimeClock_datasetActiveTimestampIsAuthoritative()
+ throws Exception {
+ MockitoSession session =
+ ExtendedMockito.mockitoSession().mockStatic(SystemClock.class).startMocking();
+ final IActiveOperationalDatasetReceiver mockReceiver =
+ ExtendedMockito.mock(IActiveOperationalDatasetReceiver.class);
+
+ try {
+ ExtendedMockito.when(SystemClock.currentNetworkTimeClock())
+ .thenReturn(Clock.systemUTC());
+ mService.createRandomizedDataset(DEFAULT_NETWORK_NAME, mockReceiver);
+ mTestLooper.dispatchAll();
+ } finally {
+ session.finishMocking();
+ }
+
+ verify(mockReceiver, never()).onError(anyInt(), anyString());
+ verify(mockReceiver, times(1)).onSuccess(mActiveDatasetCaptor.capture());
+ ActiveOperationalDataset activeDataset = mActiveDatasetCaptor.getValue();
+ assertThat(activeDataset.getActiveTimestamp().isAuthoritativeSource()).isTrue();
+ }
+
+ @Test
public void createRandomizedDataset_succeed_activeDatasetCreated() throws Exception {
final IActiveOperationalDatasetReceiver mockReceiver =
mock(IActiveOperationalDatasetReceiver.class);