Skip to content
Snippets Groups Projects
Commit 625353b5 authored by Yang Sun's avatar Yang Sun
Browse files

Add service implementation for ThreadNetworkController#setEnabled

ThreadNetworkControllerService handles setEnabled requests and
asks ot-daemon to enable/disable Thread. The desired Thread enabled state
is saved in thread persistent setttings, the setting is loaded at bootup.

Bug: 299243765

Test: atest CtsThreadNetworkTestCases:android.net.thread.cts.ThreadNetworkControllerTest

Change-Id: Ifd0135433df25d9c6cfd3f365dd06ec7908268e7
parent 0797d9d5
No related branches found
No related tags found
No related merge requests found
......@@ -27,6 +27,9 @@ 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.STATE_DISABLED;
import static android.net.thread.ThreadNetworkController.STATE_DISABLING;
import static android.net.thread.ThreadNetworkController.STATE_ENABLED;
import static android.net.thread.ThreadNetworkController.THREAD_VERSION_1_3;
import static android.net.thread.ThreadNetworkException.ERROR_ABORTED;
import static android.net.thread.ThreadNetworkException.ERROR_BUSY;
......@@ -35,6 +38,7 @@ import static android.net.thread.ThreadNetworkException.ERROR_INTERNAL_ERROR;
import static android.net.thread.ThreadNetworkException.ERROR_REJECTED_BY_PEER;
import static android.net.thread.ThreadNetworkException.ERROR_RESOURCE_EXHAUSTED;
import static android.net.thread.ThreadNetworkException.ERROR_RESPONSE_BAD_FORMAT;
import static android.net.thread.ThreadNetworkException.ERROR_THREAD_DISABLED;
import static android.net.thread.ThreadNetworkException.ERROR_TIMEOUT;
import static android.net.thread.ThreadNetworkException.ERROR_UNSUPPORTED_CHANNEL;
import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
......@@ -48,7 +52,11 @@ import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_
import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_REASSEMBLY_TIMEOUT;
import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_REJECTED;
import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_RESPONSE_TIMEOUT;
import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_THREAD_DISABLED;
import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_UNSUPPORTED_CHANNEL;
import static com.android.server.thread.openthread.IOtDaemon.OT_STATE_DISABLED;
import static com.android.server.thread.openthread.IOtDaemon.OT_STATE_DISABLING;
import static com.android.server.thread.openthread.IOtDaemon.OT_STATE_ENABLED;
import static com.android.server.thread.openthread.IOtDaemon.TUN_IF_NAME;
import android.Manifest.permission;
......@@ -160,6 +168,7 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
private UpstreamNetworkCallback mUpstreamNetworkCallback;
private TestNetworkSpecifier mUpstreamTestNetworkSpecifier;
private final HashMap<Network, String> mNetworkToInterface;
private final ThreadPersistentSettings mPersistentSettings;
private BorderRouterConfigurationParcel mBorderRouterConfig;
......@@ -171,7 +180,8 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
Supplier<IOtDaemon> otDaemonSupplier,
ConnectivityManager connectivityManager,
TunInterfaceController tunIfController,
InfraInterfaceController infraIfController) {
InfraInterfaceController infraIfController,
ThreadPersistentSettings persistentSettings) {
mContext = context;
mHandler = handler;
mNetworkProvider = networkProvider;
......@@ -182,9 +192,11 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
mUpstreamNetworkRequest = newUpstreamNetworkRequest();
mNetworkToInterface = new HashMap<Network, String>();
mBorderRouterConfig = new BorderRouterConfigurationParcel();
mPersistentSettings = persistentSettings;
}
public static ThreadNetworkControllerService newInstance(Context context) {
public static ThreadNetworkControllerService newInstance(
Context context, ThreadPersistentSettings persistentSettings) {
HandlerThread handlerThread = new HandlerThread("ThreadHandlerThread");
handlerThread.start();
NetworkProvider networkProvider =
......@@ -197,7 +209,8 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
() -> IOtDaemon.Stub.asInterface(ServiceManagerWrapper.waitForService("ot_daemon")),
context.getSystemService(ConnectivityManager.class),
new TunInterfaceController(TUN_IF_NAME),
new InfraInterfaceController());
new InfraInterfaceController(),
persistentSettings);
}
private static Inet6Address bytesToInet6Address(byte[] addressBytes) {
......@@ -273,7 +286,9 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
if (otDaemon == null) {
throw new RemoteException("Internal error: failed to start OT daemon");
}
otDaemon.initialize(mTunIfController.getTunFd());
otDaemon.initialize(
mTunIfController.getTunFd(),
mPersistentSettings.get(ThreadPersistentSettings.THREAD_ENABLED));
otDaemon.registerStateCallback(mOtDaemonCallbackProxy, -1);
otDaemon.asBinder().linkToDeath(() -> mHandler.post(this::onOtDaemonDied), 0);
mOtDaemon = otDaemon;
......@@ -308,6 +323,26 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
});
}
public void setEnabled(@NonNull boolean isEnabled, @NonNull IOperationReceiver receiver) {
enforceAllPermissionsGranted(PERMISSION_THREAD_NETWORK_PRIVILEGED);
mHandler.post(() -> setEnabledInternal(isEnabled, new OperationReceiverWrapper(receiver)));
}
private void setEnabledInternal(
@NonNull boolean isEnabled, @Nullable OperationReceiverWrapper receiver) {
// The persistent setting keeps the desired enabled state, thus it's set regardless
// the otDaemon set enabled state operation succeeded or not, so that it can recover
// to the desired value after reboot.
mPersistentSettings.put(ThreadPersistentSettings.THREAD_ENABLED.key, isEnabled);
try {
getOtDaemon().setThreadEnabled(isEnabled, newOtStatusReceiver(receiver));
} catch (RemoteException e) {
Log.e(TAG, "otDaemon.setThreadEnabled failed", e);
receiver.onError(ERROR_INTERNAL_ERROR, "Thread stack error");
}
}
private void requestUpstreamNetwork() {
if (mUpstreamNetworkCallback != null) {
throw new AssertionError("The upstream network request is already there.");
......@@ -658,6 +693,8 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
return ERROR_REJECTED_BY_PEER;
case OT_ERROR_UNSUPPORTED_CHANNEL:
return ERROR_UNSUPPORTED_CHANNEL;
case OT_ERROR_THREAD_DISABLED:
return ERROR_THREAD_DISABLED;
default:
return ERROR_INTERNAL_ERROR;
}
......@@ -1001,6 +1038,15 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
}
}
private void notifyThreadEnabledUpdated(IStateCallback callback, int enabledState) {
try {
callback.onThreadEnableStateChanged(enabledState);
Log.i(TAG, "onThreadEnableStateChanged " + enabledState);
} catch (RemoteException ignored) {
// do nothing if the client is dead
}
}
public void unregisterStateCallback(IStateCallback callback) {
checkOnHandlerThread();
if (!mStateCallbacks.containsKey(callback)) {
......@@ -1064,6 +1110,31 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
}
}
@Override
public void onThreadEnabledChanged(int state) {
mHandler.post(() -> onThreadEnabledChangedInternal(state));
}
private void onThreadEnabledChangedInternal(int state) {
checkOnHandlerThread();
for (IStateCallback callback : mStateCallbacks.keySet()) {
notifyThreadEnabledUpdated(callback, otStateToAndroidState(state));
}
}
private static int otStateToAndroidState(int state) {
switch (state) {
case OT_STATE_ENABLED:
return STATE_ENABLED;
case OT_STATE_DISABLED:
return STATE_DISABLED;
case OT_STATE_DISABLING:
return STATE_DISABLING;
default:
throw new IllegalArgumentException("Unknown ot state " + state);
}
}
@Override
public void onStateChanged(OtDaemonState newState, long listenerId) {
mHandler.post(() -> onStateChangedInternal(newState, listenerId));
......
......@@ -18,16 +18,21 @@ package com.android.server.thread;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ApexEnvironment;
import android.content.Context;
import android.net.thread.IThreadNetworkController;
import android.net.thread.IThreadNetworkManager;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.util.AtomicFile;
import com.android.server.SystemService;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
......@@ -40,11 +45,18 @@ public class ThreadNetworkService extends IThreadNetworkManager.Stub {
private final Context mContext;
@Nullable private ThreadNetworkCountryCode mCountryCode;
@Nullable private ThreadNetworkControllerService mControllerService;
private final ThreadPersistentSettings mPersistentSettings;
@Nullable private ThreadNetworkShellCommand mShellCommand;
/** Creates a new {@link ThreadNetworkService} object. */
public ThreadNetworkService(Context context) {
mContext = context;
mPersistentSettings =
new ThreadPersistentSettings(
new AtomicFile(
new File(
getOrCreateThreadnetworkDir(),
ThreadPersistentSettings.FILE_NAME)));
}
/**
......@@ -54,7 +66,9 @@ public class ThreadNetworkService extends IThreadNetworkManager.Stub {
*/
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mControllerService = ThreadNetworkControllerService.newInstance(mContext);
mPersistentSettings.initialize();
mControllerService =
ThreadNetworkControllerService.newInstance(mContext, mPersistentSettings);
mControllerService.initialize();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
// Country code initialization is delayed to the BOOT_COMPLETED phase because it will
......@@ -109,4 +123,19 @@ public class ThreadNetworkService extends IThreadNetworkManager.Stub {
pw.println();
}
/** Get device protected storage dir for the tethering apex. */
private static File getOrCreateThreadnetworkDir() {
final File threadnetworkDir;
final File apexDataDir =
ApexEnvironment.getApexEnvironment(TETHERING_MODULE_NAME)
.getDeviceProtectedDataDir();
threadnetworkDir = new File(apexDataDir, "thread");
if (threadnetworkDir.exists() || threadnetworkDir.mkdirs()) {
return threadnetworkDir;
}
throw new IllegalStateException(
"Cannot write into thread network data directory: " + threadnetworkDir);
}
}
......@@ -24,6 +24,7 @@ import static com.android.testutils.TestPermissionUtil.runAsShell;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
......@@ -85,6 +86,7 @@ public final class ThreadNetworkControllerServiceTest {
@Mock private TunInterfaceController mMockTunIfController;
@Mock private ParcelFileDescriptor mMockTunFd;
@Mock private InfraInterfaceController mMockInfraIfController;
@Mock private ThreadPersistentSettings mMockPersistentSettings;
private Context mContext;
private TestLooper mTestLooper;
private FakeOtDaemon mFakeOtDaemon;
......@@ -104,6 +106,8 @@ public final class ThreadNetworkControllerServiceTest {
when(mMockTunIfController.getTunFd()).thenReturn(mMockTunFd);
when(mMockPersistentSettings.get(any())).thenReturn(true);
mService =
new ThreadNetworkControllerService(
ApplicationProvider.getApplicationContext(),
......@@ -112,7 +116,8 @@ public final class ThreadNetworkControllerServiceTest {
() -> mFakeOtDaemon,
mMockConnectivityManager,
mMockTunIfController,
mMockInfraIfController);
mMockInfraIfController,
mMockPersistentSettings);
mService.setTestNetworkAgent(mMockNetworkAgent);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment