From 1cec48f3c058d251a45d95fb5de6fe8bb2b6259d Mon Sep 17 00:00:00 2001 From: Kangping Dong Date: Mon, 21 Aug 2023 18:48:14 +0800 Subject: [PATCH] [Thread] initial Thread network service This commit sets up the initial Thread network service for the Android Thread feature which allows an Android device to create a Thread network and being a Border Router. See https://www.threadgroup.org/What-is-Thread for background of Thread. See b/235016403 for the Android Thread feature request. Test: lunch aosp_cf_x86_64_tv-userdebug m && launch_cvd atest CtsThreadNetworkTestCases Bug: 262683651 Change-Id: Ie1bb23084531f67165ec068ea3ca39592dbc01d1 --- Tethering/apex/Android.bp | 1 + framework-t/api/system-current.txt | 13 +++ ...nectivityFrameworkInitializerTiramisu.java | 11 ++ .../ConnectivityServiceInitializer.java | 37 ++++++ thread/TEST_MAPPING | 9 ++ .../net/thread/IThreadNetworkController.aidl | 25 ++++ .../net/thread/IThreadNetworkManager.aidl | 27 +++++ .../net/thread/ThreadNetworkController.java | 62 ++++++++++ .../net/thread/ThreadNetworkManager.java | 107 +++++++++++++++++ thread/service/Android.bp | 6 + .../ThreadNetworkControllerService.java | 31 +++++ .../server/thread/ThreadNetworkService.java | 59 ++++++++++ thread/tests/cts/Android.bp | 50 ++++++++ thread/tests/cts/AndroidManifest.xml | 30 +++++ thread/tests/cts/AndroidTest.xml | 51 ++++++++ thread/tests/cts/OWNERS | 3 + .../cts/ThreadNetworkControllerTest.java | 73 ++++++++++++ .../thread/cts/ThreadNetworkManagerTest.java | 110 ++++++++++++++++++ 18 files changed, 705 insertions(+) create mode 100644 thread/TEST_MAPPING create mode 100644 thread/framework/java/android/net/thread/IThreadNetworkController.aidl create mode 100644 thread/framework/java/android/net/thread/IThreadNetworkManager.aidl create mode 100644 thread/framework/java/android/net/thread/ThreadNetworkController.java create mode 100644 thread/framework/java/android/net/thread/ThreadNetworkManager.java create mode 100644 thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java create mode 100644 thread/service/java/com/android/server/thread/ThreadNetworkService.java create mode 100644 thread/tests/cts/Android.bp create mode 100644 thread/tests/cts/AndroidManifest.xml create mode 100644 thread/tests/cts/AndroidTest.xml create mode 100644 thread/tests/cts/OWNERS create mode 100644 thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java create mode 100644 thread/tests/cts/src/android/net/thread/cts/ThreadNetworkManagerTest.java diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp index 13653d8f12..9757daad9d 100644 --- a/Tethering/apex/Android.bp +++ b/Tethering/apex/Android.bp @@ -213,6 +213,7 @@ bootclasspath_fragment { "android.net.http.apihelpers", "android.net.netstats.provider", "android.net.nsd", + "android.net.thread", "android.net.wear", ], }, diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt index 64762b4050..ea465aa129 100644 --- a/framework-t/api/system-current.txt +++ b/framework-t/api/system-current.txt @@ -388,3 +388,16 @@ package android.net.nsd { } +package android.net.thread { + + public class ThreadNetworkController { + method public int getThreadVersion(); + field public static final int THREAD_VERSION_1_3 = 4; // 0x4 + } + + public class ThreadNetworkManager { + method @NonNull public java.util.List getAllThreadNetworkControllers(); + } + +} + diff --git a/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java b/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java index d9c9d74032..d89964d391 100644 --- a/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java +++ b/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java @@ -24,6 +24,8 @@ import android.net.mdns.aidl.IMDns; import android.net.nsd.INsdManager; import android.net.nsd.MDnsManager; import android.net.nsd.NsdManager; +import android.net.thread.IThreadNetworkManager; +import android.net.thread.ThreadNetworkManager; /** * Class for performing registration for Connectivity services which are exposed via updatable APIs @@ -89,5 +91,14 @@ public final class ConnectivityFrameworkInitializerTiramisu { return new MDnsManager(service); } ); + + SystemServiceRegistry.registerContextAwareService( + ThreadNetworkManager.SERVICE_NAME, + ThreadNetworkManager.class, + (context, serviceBinder) -> { + IThreadNetworkManager managerService = + IThreadNetworkManager.Stub.asInterface(serviceBinder); + return new ThreadNetworkManager(context, managerService); + }); } } diff --git a/service-t/src/com/android/server/ConnectivityServiceInitializer.java b/service-t/src/com/android/server/ConnectivityServiceInitializer.java index 624c5df2b4..003ec8cc89 100644 --- a/service-t/src/com/android/server/ConnectivityServiceInitializer.java +++ b/service-t/src/com/android/server/ConnectivityServiceInitializer.java @@ -16,7 +16,10 @@ package com.android.server; +import android.annotation.Nullable; import android.content.Context; +import android.content.pm.PackageManager; +import android.net.thread.ThreadNetworkManager; import android.util.Log; import com.android.modules.utils.build.SdkLevel; @@ -26,6 +29,7 @@ import com.android.server.ethernet.EthernetService; import com.android.server.ethernet.EthernetServiceImpl; import com.android.server.nearby.NearbyService; import com.android.server.remoteauth.RemoteAuthService; +import com.android.server.thread.ThreadNetworkService; /** * Connectivity service initializer for core networking. This is called by system server to create @@ -40,6 +44,7 @@ public final class ConnectivityServiceInitializer extends SystemService { private final NearbyService mNearbyService; private final EthernetServiceImpl mEthernetServiceImpl; private final RemoteAuthService mRemoteAuthService; + private final ThreadNetworkService mThreadNetworkService; public ConnectivityServiceInitializer(Context context) { super(context); @@ -52,6 +57,7 @@ public final class ConnectivityServiceInitializer extends SystemService { mNsdService = createNsdService(context); mNearbyService = createNearbyService(context); mRemoteAuthService = createRemoteAuthService(context); + mThreadNetworkService = createThreadNetworkService(context); } @Override @@ -93,6 +99,12 @@ public final class ConnectivityServiceInitializer extends SystemService { publishBinderService(RemoteAuthService.SERVICE_NAME, mRemoteAuthService, /* allowIsolated= */ false); } + + if (mThreadNetworkService != null) { + Log.i(TAG, "Registering " + ThreadNetworkManager.SERVICE_NAME); + publishBinderService(ThreadNetworkManager.SERVICE_NAME, mThreadNetworkService, + /* allowIsolated= */ false); + } } @Override @@ -104,6 +116,10 @@ public final class ConnectivityServiceInitializer extends SystemService { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY && mEthernetServiceImpl != null) { mEthernetServiceImpl.start(); } + + if (mThreadNetworkService != null) { + mThreadNetworkService.onBootPhase(phase); + } } /** @@ -171,4 +187,25 @@ public final class ConnectivityServiceInitializer extends SystemService { } return EthernetService.create(context); } + + /** + * Returns Thread network service instance if supported. + * Thread is supported if all of below are satisfied: + * 1. the FEATURE_THREAD_NETWORK is available + * 2. the SDK level is V+, or SDK level is U and the device is a TV + */ + @Nullable + private ThreadNetworkService createThreadNetworkService(final Context context) { + final PackageManager pm = context.getPackageManager(); + if (!pm.hasSystemFeature(ThreadNetworkManager.FEATURE_NAME)) { + return null; + } + if (!SdkLevel.isAtLeastU()) { + return null; + } + if (!SdkLevel.isAtLeastV() && !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + return null; + } + return new ThreadNetworkService(context); + } } diff --git a/thread/TEST_MAPPING b/thread/TEST_MAPPING new file mode 100644 index 0000000000..17a74f68e4 --- /dev/null +++ b/thread/TEST_MAPPING @@ -0,0 +1,9 @@ +{ + // TODO (b/297729075): graduate this test to presubmit once it meets the SLO requirements. + // See go/test-mapping-slo-guide + "postsubmit": [ + { + "name": "CtsThreadNetworkTestCases" + } + ] +} diff --git a/thread/framework/java/android/net/thread/IThreadNetworkController.aidl b/thread/framework/java/android/net/thread/IThreadNetworkController.aidl new file mode 100644 index 0000000000..0219beb524 --- /dev/null +++ b/thread/framework/java/android/net/thread/IThreadNetworkController.aidl @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.thread; + +/** +* Interface for communicating with ThreadNetworkControllerService. +* @hide +*/ +interface IThreadNetworkController { + int getThreadVersion(); +} diff --git a/thread/framework/java/android/net/thread/IThreadNetworkManager.aidl b/thread/framework/java/android/net/thread/IThreadNetworkManager.aidl new file mode 100644 index 0000000000..0e394b1321 --- /dev/null +++ b/thread/framework/java/android/net/thread/IThreadNetworkManager.aidl @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.thread; + +import android.net.thread.IThreadNetworkController; + +/** +* Interface for communicating with ThreadNetworkService. +* @hide +*/ +interface IThreadNetworkManager { + List getAllThreadNetworkControllers(); +} diff --git a/thread/framework/java/android/net/thread/ThreadNetworkController.java b/thread/framework/java/android/net/thread/ThreadNetworkController.java new file mode 100644 index 0000000000..fe189c2621 --- /dev/null +++ b/thread/framework/java/android/net/thread/ThreadNetworkController.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.thread; + +import static java.util.Objects.requireNonNull; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.RemoteException; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Provides the primary API for controlling all aspects of a Thread network. + * + * @hide + */ +@SystemApi +public class ThreadNetworkController { + + /** Thread standard version 1.3. */ + public static final int THREAD_VERSION_1_3 = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({THREAD_VERSION_1_3}) + public @interface ThreadVersion {} + + private final IThreadNetworkController mControllerService; + + ThreadNetworkController(@NonNull IThreadNetworkController controllerService) { + requireNonNull(controllerService, "controllerService cannot be null"); + + mControllerService = controllerService; + } + + /** Returns the Thread version this device is operating on. */ + @ThreadVersion + public int getThreadVersion() { + try { + return mControllerService.getThreadVersion(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/thread/framework/java/android/net/thread/ThreadNetworkManager.java b/thread/framework/java/android/net/thread/ThreadNetworkManager.java new file mode 100644 index 0000000000..2a253a17da --- /dev/null +++ b/thread/framework/java/android/net/thread/ThreadNetworkManager.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.thread; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.os.RemoteException; + +import com.android.net.module.util.CollectionUtils; + +import java.util.Collections; +import java.util.List; + +/** + * Provides the primary API for managing app aspects of Thread network connectivity. + * + * @hide + */ +@SystemApi +@SystemService(ThreadNetworkManager.SERVICE_NAME) +public class ThreadNetworkManager { + /** + * This value tracks {@link Context#THREAD_NETWORK_SERVICE}. + * + *

This is needed because at the time this service is created, it needs to support both + * Android U and V but {@link Context#THREAD_NETWORK_SERVICE} Is only available on the V branch. + * + *

Note that this is not added to NetworkStack ConstantsShim because we need this constant in + * the framework library while ConstantsShim is only linked against the service library. + * + * @hide + */ + public static final String SERVICE_NAME = "thread_network"; + + /** + * This value tracks {@link PackageManager#FEATURE_THREAD_NETWORK}. + * + *

This is needed because at the time this service is created, it needs to support both + * Android U and V but {@link PackageManager#FEATURE_THREAD_NETWORK} Is only available on the V + * branch. + * + *

Note that this is not added to NetworkStack COnstantsShim because we need this constant in + * the framework library while ConstantsShim is only linked against the service library. + * + * @hide + */ + public static final String FEATURE_NAME = "android.hardware.thread_network"; + + @NonNull private final Context mContext; + @NonNull private final List mUnmodifiableControllerServices; + + /** + * Creates a new ThreadNetworkManager instance. + * + * @hide + */ + public ThreadNetworkManager( + @NonNull Context context, @NonNull IThreadNetworkManager managerService) { + this(context, makeControllers(managerService)); + } + + private static List makeControllers( + @NonNull IThreadNetworkManager managerService) { + requireNonNull(managerService, "managerService cannot be null"); + + List controllerServices; + + try { + controllerServices = managerService.getAllThreadNetworkControllers(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return Collections.emptyList(); + } + + return CollectionUtils.map(controllerServices, ThreadNetworkController::new); + } + + private ThreadNetworkManager( + @NonNull Context context, @NonNull List controllerServices) { + mContext = context; + mUnmodifiableControllerServices = Collections.unmodifiableList(controllerServices); + } + + /** Returns the {@link ThreadNetworkController} object of all Thread networks. */ + @NonNull + public List getAllThreadNetworkControllers() { + return mUnmodifiableControllerServices; + } +} diff --git a/thread/service/Android.bp b/thread/service/Android.bp index fda206aa90..f1af653f1c 100644 --- a/thread/service/Android.bp +++ b/thread/service/Android.bp @@ -32,5 +32,11 @@ java_library { // (service-connectivity is only used on 31+) and use 31 here min_sdk_version: "30", srcs: [":service-thread-sources"], + libs: [ + "framework-connectivity-t-pre-jarjar", + ], + static_libs: [ + "net-utils-device-common", + ], apex_available: ["com.android.tethering"], } diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java new file mode 100644 index 0000000000..e8b95bc909 --- /dev/null +++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.thread; + +import static android.net.thread.ThreadNetworkController.THREAD_VERSION_1_3; + +import android.net.thread.IThreadNetworkController; +import android.net.thread.ThreadNetworkController; + +/** Implementation of the {@link ThreadNetworkController} API. */ +public final class ThreadNetworkControllerService extends IThreadNetworkController.Stub { + + @Override + public int getThreadVersion() { + return THREAD_VERSION_1_3; + } +} diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkService.java b/thread/service/java/com/android/server/thread/ThreadNetworkService.java new file mode 100644 index 0000000000..c6d47df60b --- /dev/null +++ b/thread/service/java/com/android/server/thread/ThreadNetworkService.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.thread; + +import android.content.Context; +import android.net.thread.IThreadNetworkController; +import android.net.thread.IThreadNetworkManager; + +import com.android.server.SystemService; + +import java.util.Collections; +import java.util.List; + +/** + * Implementation of the Thread network service. This is the entry point of Android Thread feature. + */ +public class ThreadNetworkService extends IThreadNetworkManager.Stub { + private final ThreadNetworkControllerService mControllerService; + + /** Creates a new {@link ThreadNetworkService} object. */ + public ThreadNetworkService(Context context) { + this(context, new ThreadNetworkControllerService()); + } + + private ThreadNetworkService( + Context context, ThreadNetworkControllerService controllerService) { + mControllerService = controllerService; + } + + /** + * Called by the service initializer. + * + * @see com.android.server.SystemService#onBootPhase + */ + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_BOOT_COMPLETED) { + // TODO: initialize ThreadNetworkManagerService + } + } + + @Override + public List getAllThreadNetworkControllers() { + return Collections.singletonList(mControllerService); + } +} diff --git a/thread/tests/cts/Android.bp b/thread/tests/cts/Android.bp new file mode 100644 index 0000000000..96056c6cdb --- /dev/null +++ b/thread/tests/cts/Android.bp @@ -0,0 +1,50 @@ +// +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test { + name: "CtsThreadNetworkTestCases", + defaults: ["cts_defaults"], + min_sdk_version: "33", + sdk_version: "test_current", + manifest: "AndroidManifest.xml", + test_config: "AndroidTest.xml", + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "cts", + "general-tests", + "mts-tethering", + ], + static_libs: [ + "androidx.test.ext.junit", + "compatibility-device-util-axt", + "ctstestrunner-axt", + "net-tests-utils", + "truth-prebuilt", + ], + libs: [ + "android.test.base", + "android.test.runner", + ], + // Test coverage system runs on different devices. Need to + // compile for all architectures. + compile_multilib: "both", +} diff --git a/thread/tests/cts/AndroidManifest.xml b/thread/tests/cts/AndroidManifest.xml new file mode 100644 index 0000000000..4370fe3554 --- /dev/null +++ b/thread/tests/cts/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + diff --git a/thread/tests/cts/AndroidTest.xml b/thread/tests/cts/AndroidTest.xml new file mode 100644 index 0000000000..5ba605f573 --- /dev/null +++ b/thread/tests/cts/AndroidTest.xml @@ -0,0 +1,51 @@ + + + + +