From 6420ccf4a8c9e60db8374dd0db732b121376cc40 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Mon, 13 May 2024 16:52:15 +0800 Subject: [PATCH] Thread: use network time for generating dataset This commit tries to use network time as authoritative time source for generating Thread operational dataset. If the time is get successfully, the authoritative bit will be set in the dataset. Bug: 340130726 Test: presubmit Change-Id: Ibaca1f866be0be121763fdb63e02621809484b17 --- .../ThreadNetworkControllerService.java | 17 ++++-- .../ThreadNetworkControllerServiceTest.java | 52 +++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java index 0c200fd18f..34291e9b5d 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; @@ -820,7 +823,6 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub networkName, supportedChannelMask, preferredChannelMask, - Instant.now(), new Random(), new SecureRandom()); @@ -838,9 +840,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; @@ -854,7 +865,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 =