diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java index b418ef6934..02c6a09977 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.HandlerThread; 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.io.IOException; 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 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub networkName, supportedChannelMask, preferredChannelMask, - Instant.now(), new Random(), new SecureRandom()); @@ -839,9 +841,18 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub 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 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub 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 cf6eb44373..8f60783322 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.Handler; 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.ext.junit.runners.AndroidJUnit4; 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.InOrder; 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; @@ -534,6 +539,53 @@ public final class ThreadNetworkControllerServiceTest { }; } + @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 =