set the country code when initializing the ot-daemon

The country code is lost after the ot-daemon is restarted. This
CL stores the country code to persistent settings and reads
out the previously stored country code to set the country code
when the ot-daemon is initialized.

Bug: b/326021472
Test: atest ThreadNetworkUnitTests

Change-Id: I6982a57ba4845f5a45f3366400c531398e82339e
This commit is contained in:
Zhanglong Xia 2024-03-21 18:38:05 +08:00
commit 4eab54b40e
9 changed files with 109 additions and 15 deletions

View file

@ -182,6 +182,7 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
private final NsdPublisher mNsdPublisher;
private final OtDaemonCallbackProxy mOtDaemonCallbackProxy = new OtDaemonCallbackProxy();
private final ConnectivityResources mResources;
private final Supplier<String> mCountryCodeSupplier;
// This should not be directly used for calling IOtDaemon APIs because ot-daemon may die and
// {@code mOtDaemon} will be set to {@code null}. Instead, use {@code getOtDaemon()}
@ -215,7 +216,8 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
ThreadPersistentSettings persistentSettings,
NsdPublisher nsdPublisher,
UserManager userManager,
ConnectivityResources resources) {
ConnectivityResources resources,
Supplier<String> countryCodeSupplier) {
mContext = context;
mHandler = handler;
mNetworkProvider = networkProvider;
@ -230,10 +232,13 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
mNsdPublisher = nsdPublisher;
mUserManager = userManager;
mResources = resources;
mCountryCodeSupplier = countryCodeSupplier;
}
public static ThreadNetworkControllerService newInstance(
Context context, ThreadPersistentSettings persistentSettings) {
Context context,
ThreadPersistentSettings persistentSettings,
Supplier<String> countryCodeSupplier) {
HandlerThread handlerThread = new HandlerThread("ThreadHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
@ -251,7 +256,8 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
persistentSettings,
NsdPublisher.newInstance(context, handler),
context.getSystemService(UserManager.class),
new ConnectivityResources(context));
new ConnectivityResources(context),
countryCodeSupplier);
}
private static Inet6Address bytesToInet6Address(byte[] addressBytes) {
@ -347,7 +353,8 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
isEnabled(),
mNsdPublisher,
getMeshcopTxtAttributes(mResources.get()),
mOtDaemonCallbackProxy);
mOtDaemonCallbackProxy,
mCountryCodeSupplier.get());
otDaemon.asBinder().linkToDeath(() -> mHandler.post(this::onOtDaemonDied), 0);
mOtDaemon = otDaemon;
return mOtDaemon;

View file

@ -16,6 +16,8 @@
package com.android.server.thread;
import static com.android.server.thread.ThreadPersistentSettings.THREAD_COUNTRY_CODE;
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.TargetApi;
@ -83,6 +85,7 @@ public class ThreadNetworkCountryCode {
COUNTRY_CODE_SOURCE_TELEPHONY,
COUNTRY_CODE_SOURCE_TELEPHONY_LAST,
COUNTRY_CODE_SOURCE_WIFI,
COUNTRY_CODE_SOURCE_SETTINGS,
})
private @interface CountryCodeSource {}
@ -93,6 +96,7 @@ public class ThreadNetworkCountryCode {
private static final String COUNTRY_CODE_SOURCE_TELEPHONY = "Telephony";
private static final String COUNTRY_CODE_SOURCE_TELEPHONY_LAST = "TelephonyLast";
private static final String COUNTRY_CODE_SOURCE_WIFI = "Wifi";
private static final String COUNTRY_CODE_SOURCE_SETTINGS = "Settings";
private static final CountryCodeInfo DEFAULT_COUNTRY_CODE_INFO =
new CountryCodeInfo(DEFAULT_COUNTRY_CODE, COUNTRY_CODE_SOURCE_DEFAULT);
@ -107,6 +111,7 @@ public class ThreadNetworkCountryCode {
private final SubscriptionManager mSubscriptionManager;
private final Map<Integer, TelephonyCountryCodeSlotInfo> mTelephonyCountryCodeSlotInfoMap =
new ArrayMap();
private final ThreadPersistentSettings mPersistentSettings;
@Nullable private CountryCodeInfo mCurrentCountryCodeInfo;
@Nullable private CountryCodeInfo mLocationCountryCodeInfo;
@ -215,7 +220,8 @@ public class ThreadNetworkCountryCode {
Context context,
TelephonyManager telephonyManager,
SubscriptionManager subscriptionManager,
@Nullable String oemCountryCode) {
@Nullable String oemCountryCode,
ThreadPersistentSettings persistentSettings) {
mLocationManager = locationManager;
mThreadNetworkControllerService = threadNetworkControllerService;
mGeocoder = geocoder;
@ -224,14 +230,19 @@ public class ThreadNetworkCountryCode {
mContext = context;
mTelephonyManager = telephonyManager;
mSubscriptionManager = subscriptionManager;
mPersistentSettings = persistentSettings;
if (oemCountryCode != null) {
mOemCountryCodeInfo = new CountryCodeInfo(oemCountryCode, COUNTRY_CODE_SOURCE_OEM);
}
mCurrentCountryCodeInfo = pickCountryCode();
}
public static ThreadNetworkCountryCode newInstance(
Context context, ThreadNetworkControllerService controllerService) {
Context context,
ThreadNetworkControllerService controllerService,
ThreadPersistentSettings persistentSettings) {
return new ThreadNetworkCountryCode(
context.getSystemService(LocationManager.class),
controllerService,
@ -241,7 +252,8 @@ public class ThreadNetworkCountryCode {
context,
context.getSystemService(TelephonyManager.class),
context.getSystemService(SubscriptionManager.class),
ThreadNetworkProperties.country_code().orElse(null));
ThreadNetworkProperties.country_code().orElse(null),
persistentSettings);
}
/** Sets up this country code module to listen to location country code changes. */
@ -485,6 +497,11 @@ public class ThreadNetworkCountryCode {
return mLocationCountryCodeInfo;
}
String settingsCountryCode = mPersistentSettings.get(THREAD_COUNTRY_CODE);
if (settingsCountryCode != null) {
return new CountryCodeInfo(settingsCountryCode, COUNTRY_CODE_SOURCE_SETTINGS);
}
if (mOemCountryCodeInfo != null) {
return mOemCountryCodeInfo;
}
@ -498,6 +515,8 @@ public class ThreadNetworkCountryCode {
public void onSuccess() {
synchronized ("ThreadNetworkCountryCode.this") {
mCurrentCountryCodeInfo = countryCodeInfo;
mPersistentSettings.put(
THREAD_COUNTRY_CODE.key, countryCodeInfo.getCountryCode());
}
}
@ -536,10 +555,9 @@ public class ThreadNetworkCountryCode {
newOperationReceiver(countryCodeInfo));
}
/** Returns the current country code or {@code null} if no country code is set. */
@Nullable
/** Returns the current country code. */
public synchronized String getCountryCode() {
return (mCurrentCountryCodeInfo != null) ? mCurrentCountryCodeInfo.getCountryCode() : null;
return mCurrentCountryCodeInfo.getCountryCode();
}
/**

View file

@ -60,13 +60,16 @@ public class ThreadNetworkService extends IThreadNetworkManager.Stub {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mPersistentSettings.initialize();
mControllerService =
ThreadNetworkControllerService.newInstance(mContext, mPersistentSettings);
ThreadNetworkControllerService.newInstance(
mContext, mPersistentSettings, () -> mCountryCode.getCountryCode());
mCountryCode =
ThreadNetworkCountryCode.newInstance(
mContext, mControllerService, mPersistentSettings);
mControllerService.initialize();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
// Country code initialization is delayed to the BOOT_COMPLETED phase because it will
// call into Wi-Fi and Telephony service whose country code module is ready after
// PHASE_ACTIVITY_MANAGER_READY and PHASE_THIRD_PARTY_APPS_CAN_START
mCountryCode = ThreadNetworkCountryCode.newInstance(mContext, mControllerService);
mCountryCode.initialize();
mShellCommand =
new ThreadNetworkShellCommand(requireNonNull(mControllerService), mCountryCode);

View file

@ -63,6 +63,9 @@ public class ThreadPersistentSettings {
/** Stores the Thread feature toggle state, true for enabled and false for disabled. */
public static final Key<Boolean> THREAD_ENABLED = new Key<>("Thread_enabled", true);
/** Stores the Thread country code, null if no country code is stored. */
public static final Key<String> THREAD_COUNTRY_CODE = new Key<>("thread_country_code", null);
/******** Thread persistent setting keys ***************/
@GuardedBy("mLock")
@ -123,7 +126,9 @@ public class ThreadPersistentSettings {
private <T> T getObject(String key, T defaultValue) {
Object value;
synchronized (mLock) {
if (defaultValue instanceof Boolean) {
if (defaultValue == null) {
value = mSettings.getString(key, null);
} else if (defaultValue instanceof Boolean) {
value = mSettings.getBoolean(key, (Boolean) defaultValue);
} else if (defaultValue instanceof Integer) {
value = mSettings.getInt(key, (Integer) defaultValue);

View file

@ -23,6 +23,7 @@ import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
@ -140,6 +141,22 @@ public class ThreadIntegrationTest {
}
}
@Test
public void otDaemonRestart_latestCountryCodeIsSetToOtDaemon() throws Exception {
runThreadCommand("force-country-code enabled CN");
runShellCommand("stop ot-daemon");
// TODO(b/323331973): the sleep is needed to workaround the race conditions
SystemClock.sleep(200);
mController.waitForRole(DEVICE_ROLE_STOPPED, CALLBACK_TIMEOUT);
assertThat(mOtCtl.getCountryCode()).isEqualTo("CN");
}
private static String runThreadCommand(String cmd) {
return runShellCommandOrThrow("cmd thread_network " + cmd);
}
// TODO (b/323300829): add more tests for integration with linux platform and
// ConnectivityService
}

View file

@ -74,6 +74,12 @@ public final class OtDaemonController {
return (Inet6Address) InetAddresses.parseNumericAddress(addressStr);
}
/** Returns the country code on ot-daemon. */
public String getCountryCode() {
String countryCodeStr = executeCommand("region").split("\n")[0].trim();
return countryCodeStr;
}
public String executeCommand(String cmd) {
return SystemUtil.runShellCommand(OT_CTL + " " + cmd);
}

View file

@ -25,6 +25,7 @@ import static android.net.thread.ThreadNetworkException.ERROR_THREAD_DISABLED;
import static android.net.thread.ThreadNetworkManager.DISALLOW_THREAD_NETWORK;
import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
import static com.android.server.thread.ThreadNetworkCountryCode.DEFAULT_COUNTRY_CODE;
import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_INVALID_STATE;
import static com.google.common.io.BaseEncoding.base16;
@ -182,7 +183,8 @@ public final class ThreadNetworkControllerServiceTest {
mMockPersistentSettings,
mMockNsdPublisher,
mMockUserManager,
mConnectivityResources);
mConnectivityResources,
() -> DEFAULT_COUNTRY_CODE);
mService.setTestNetworkAgent(mMockNetworkAgent);
}

View file

@ -19,6 +19,7 @@ package com.android.server.thread;
import static android.net.thread.ThreadNetworkException.ERROR_INTERNAL_ERROR;
import static com.android.server.thread.ThreadNetworkCountryCode.DEFAULT_COUNTRY_CODE;
import static com.android.server.thread.ThreadPersistentSettings.THREAD_COUNTRY_CODE;
import static com.google.common.truth.Truth.assertThat;
@ -104,6 +105,7 @@ public class ThreadNetworkCountryCodeTest {
@Mock List<SubscriptionInfo> mSubscriptionInfoList;
@Mock SubscriptionInfo mSubscriptionInfo0;
@Mock SubscriptionInfo mSubscriptionInfo1;
@Mock ThreadPersistentSettings mPersistentSettings;
private ThreadNetworkCountryCode mThreadNetworkCountryCode;
private boolean mErrorSetCountryCode;
@ -164,7 +166,8 @@ public class ThreadNetworkCountryCodeTest {
mContext,
mTelephonyManager,
mSubscriptionManager,
oemCountryCode);
oemCountryCode,
mPersistentSettings);
}
private static Address newAddress(String countryCode) {
@ -449,6 +452,14 @@ public class ThreadNetworkCountryCodeTest {
assertThat(mThreadNetworkCountryCode.getCountryCode()).isEqualTo(DEFAULT_COUNTRY_CODE);
}
@Test
public void settingsCountryCode_settingsCountryCodeIsActive_settingsCountryCodeIsUsed() {
when(mPersistentSettings.get(THREAD_COUNTRY_CODE)).thenReturn(TEST_COUNTRY_CODE_CN);
mThreadNetworkCountryCode.initialize();
assertThat(mThreadNetworkCountryCode.getCountryCode()).isEqualTo(TEST_COUNTRY_CODE_CN);
}
@Test
public void dump_allCountryCodeInfoAreDumped() {
StringWriter stringWriter = new StringWriter();

View file

@ -16,6 +16,7 @@
package com.android.server.thread;
import static com.android.server.thread.ThreadPersistentSettings.THREAD_COUNTRY_CODE;
import static com.android.server.thread.ThreadPersistentSettings.THREAD_ENABLED;
import static com.google.common.truth.Truth.assertThat;
@ -54,6 +55,8 @@ import java.io.FileOutputStream;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ThreadPersistentSettingsTest {
private static final String TEST_COUNTRY_CODE = "CN";
@Mock private AtomicFile mAtomicFile;
@Mock Resources mResources;
@Mock ConnectivityResources mConnectivityResources;
@ -131,6 +134,28 @@ public class ThreadPersistentSettingsTest {
verify(mAtomicFile).finishWrite(any());
}
@Test
public void put_ThreadCountryCodeString_returnsString() throws Exception {
mThreadPersistentSettings.put(THREAD_COUNTRY_CODE.key, TEST_COUNTRY_CODE);
assertThat(mThreadPersistentSettings.get(THREAD_COUNTRY_CODE)).isEqualTo(TEST_COUNTRY_CODE);
// Confirm that file writes have been triggered.
verify(mAtomicFile).startWrite();
verify(mAtomicFile).finishWrite(any());
}
@Test
public void put_ThreadCountryCodeNull_returnsNull() throws Exception {
mThreadPersistentSettings.put(THREAD_COUNTRY_CODE.key, null);
assertThat(mThreadPersistentSettings.get(THREAD_COUNTRY_CODE)).isNull();
// Confirm that file writes have been triggered.
verify(mAtomicFile).startWrite();
verify(mAtomicFile).finishWrite(any());
}
private byte[] createXmlForParsing(String key, Boolean value) throws Exception {
PersistableBundle bundle = new PersistableBundle();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();