[Thread] add support for configuration at ThreadNetworkController

This CL adds methods for setting the ThreadConfiguration and
subscribing/unsubscribing to Configuration changes. The ThreadConfiguration
is used for controlling Thread features for the device.

Bug: 342519412
Change-Id: I9e20391eb3b2f20312a2cb0b587cc5bdf200d24d
This commit is contained in:
Handa Wang 2024-06-20 09:43:45 +00:00
commit 7520abf333
6 changed files with 343 additions and 1 deletions

View file

@ -0,0 +1,24 @@
/*
* Copyright 2024 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.ThreadConfiguration;
/** Receives the result of a Thread Configuration change. @hide */
oneway interface IConfigurationReceiver {
void onConfigurationChanged(in ThreadConfiguration configuration);
}

View file

@ -19,11 +19,13 @@ package android.net.thread;
import android.net.thread.ActiveOperationalDataset;
import android.net.thread.ChannelMaxPower;
import android.net.thread.IActiveOperationalDatasetReceiver;
import android.net.thread.IOperationalDatasetCallback;
import android.net.thread.IConfigurationReceiver;
import android.net.thread.IOperationReceiver;
import android.net.thread.IOperationalDatasetCallback;
import android.net.thread.IScheduleMigrationReceiver;
import android.net.thread.IStateCallback;
import android.net.thread.PendingOperationalDataset;
import android.net.thread.ThreadConfiguration;
/**
* Interface for communicating with ThreadNetworkControllerService.
@ -46,4 +48,7 @@ interface IThreadNetworkController {
void createRandomizedDataset(String networkName, IActiveOperationalDatasetReceiver receiver);
void setEnabled(boolean enabled, in IOperationReceiver receiver);
void setConfiguration(in ThreadConfiguration config, in IOperationReceiver receiver);
void registerConfigurationCallback(in IConfigurationReceiver receiver);
void unregisterConfigurationCallback(in IConfigurationReceiver receiver);
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2024 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;
parcelable ThreadConfiguration;

View file

@ -0,0 +1,170 @@
/*
* Copyright 2024 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.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* Data interface for Thread device configuration.
*
* <p>An example usage of creating a {@link ThreadConfiguration} that turns on NAT64 feature based
* on an existing {@link ThreadConfiguration}:
*
* <pre>{@code
* ThreadConfiguration config =
* new ThreadConfiguration.Builder(existingConfig).setNat64Enabled(true).build();
* }</pre>
*
* @see ThreadNetworkController#setConfiguration
* @see ThreadNetworkController#registerConfigurationCallback
* @see ThreadNetworkController#unregisterConfigurationCallback
* @hide
*/
// @FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
// @SystemApi
public final class ThreadConfiguration implements Parcelable {
private final boolean mNat64Enabled;
private final boolean mDhcp6PdEnabled;
private ThreadConfiguration(Builder builder) {
this(builder.mNat64Enabled, builder.mDhcp6PdEnabled);
}
private ThreadConfiguration(boolean nat64Enabled, boolean dhcp6PdEnabled) {
this.mNat64Enabled = nat64Enabled;
this.mDhcp6PdEnabled = dhcp6PdEnabled;
}
/** Returns {@code true} if NAT64 is enabled. */
public boolean isNat64Enabled() {
return mNat64Enabled;
}
/** Returns {@code true} if DHCPv6 Prefix Delegation is enabled. */
public boolean isDhcp6PdEnabled() {
return mDhcp6PdEnabled;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof ThreadConfiguration)) {
return false;
} else {
ThreadConfiguration otherConfig = (ThreadConfiguration) other;
return mNat64Enabled == otherConfig.mNat64Enabled
&& mDhcp6PdEnabled == otherConfig.mDhcp6PdEnabled;
}
}
@Override
public int hashCode() {
return Objects.hash(mNat64Enabled, mDhcp6PdEnabled);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('{');
sb.append("Nat64Enabled=").append(mNat64Enabled);
sb.append(", Dhcp6PdEnabled=").append(mDhcp6PdEnabled);
sb.append('}');
return sb.toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeBoolean(mNat64Enabled);
dest.writeBoolean(mDhcp6PdEnabled);
}
public static final @NonNull Creator<ThreadConfiguration> CREATOR =
new Creator<>() {
@Override
public ThreadConfiguration createFromParcel(Parcel in) {
ThreadConfiguration.Builder builder = new ThreadConfiguration.Builder();
builder.setNat64Enabled(in.readBoolean());
builder.setDhcp6PdEnabled(in.readBoolean());
return builder.build();
}
@Override
public ThreadConfiguration[] newArray(int size) {
return new ThreadConfiguration[size];
}
};
/** The builder for creating {@link ThreadConfiguration} objects. */
public static final class Builder {
private boolean mNat64Enabled = false;
private boolean mDhcp6PdEnabled = false;
/** Creates a new {@link Builder} object with all features disabled. */
public Builder() {}
/**
* Creates a new {@link Builder} object from a {@link ThreadConfiguration} object.
*
* @param config the Border Router configurations to be copied
*/
public Builder(@NonNull ThreadConfiguration config) {
Objects.requireNonNull(config);
mNat64Enabled = config.mNat64Enabled;
mDhcp6PdEnabled = config.mDhcp6PdEnabled;
}
/**
* Enables or disables NAT64 for the device.
*
* <p>Enabling this feature will allow Thread devices to connect to the internet/cloud over
* IPv4.
*/
@NonNull
public Builder setNat64Enabled(boolean enabled) {
this.mNat64Enabled = enabled;
return this;
}
/**
* Enables or disables Prefix Delegation for the device.
*
* <p>Enabling this feature will allow Thread devices to connect to the internet/cloud over
* IPv6.
*/
@NonNull
public Builder setDhcp6PdEnabled(boolean enabled) {
this.mDhcp6PdEnabled = enabled;
return this;
}
/** Creates a new {@link ThreadConfiguration} object. */
@NonNull
public ThreadConfiguration build() {
return new ThreadConfiguration(this);
}
}
}

View file

@ -41,6 +41,7 @@ import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* Provides the primary APIs for controlling all aspects of a Thread network.
@ -124,6 +125,12 @@ public final class ThreadNetworkController {
private final Map<OperationalDatasetCallback, OperationalDatasetCallbackProxy>
mOpDatasetCallbackMap = new HashMap<>();
private final Object mConfigurationCallbackMapLock = new Object();
@GuardedBy("mConfigurationCallbackMapLock")
private final Map<Consumer<ThreadConfiguration>, ConfigurationCallbackProxy>
mConfigurationCallbackMap = new HashMap<>();
/** @hide */
public ThreadNetworkController(@NonNull IThreadNetworkController controllerService) {
requireNonNull(controllerService, "controllerService cannot be null");
@ -578,6 +585,97 @@ public final class ThreadNetworkController {
}
}
/**
* Configures the Thread features for this device.
*
* <p>This method sets the {@link ThreadConfiguration} for this device. On success, the {@link
* OutcomeReceiver#onResult} will be called, and the {@code configuration} will be applied and
* persisted to the device; the configuration changes can be observed by {@link
* #registerConfigurationCallback}. On failure, {@link OutcomeReceiver#onError} of {@code
* receiver} will be invoked with a specific error.
*
* @param configuration the configuration to set
* @param executor the executor to execute {@code receiver}
* @param receiver the receiver to receive result of this operation
* @hide
*/
// @FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
// @RequiresPermission(permission.THREAD_NETWORK_PRIVILEGED)
public void setConfiguration(
@NonNull ThreadConfiguration configuration,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Void, ThreadNetworkException> receiver) {
requireNonNull(configuration, "Configuration cannot be null");
requireNonNull(executor, "executor cannot be null");
requireNonNull(receiver, "receiver cannot be null");
try {
mControllerService.setConfiguration(
configuration, new OperationReceiverProxy(executor, receiver));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Registers a callback to be called when the configuration is changed.
*
* <p>Upon return of this method, {@code callback} will be invoked immediately with the new
* {@link ThreadConfiguration}.
*
* @param executor the executor to execute the {@code callback}
* @param callback the callback to receive Thread configuration changes
* @throws IllegalArgumentException if {@code callback} has already been registered
* @hide
*/
// @FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
// @RequiresPermission(permission.THREAD_NETWORK_PRIVILEGED)
public void registerConfigurationCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<ThreadConfiguration> callback) {
requireNonNull(executor, "executor cannot be null");
requireNonNull(callback, "callback cannot be null");
synchronized (mConfigurationCallbackMapLock) {
if (mConfigurationCallbackMap.containsKey(callback)) {
throw new IllegalArgumentException("callback has already been registered");
}
ConfigurationCallbackProxy callbackProxy =
new ConfigurationCallbackProxy(executor, callback);
mConfigurationCallbackMap.put(callback, callbackProxy);
try {
mControllerService.registerConfigurationCallback(callbackProxy);
} catch (RemoteException e) {
mConfigurationCallbackMap.remove(callback);
e.rethrowFromSystemServer();
}
}
}
/**
* Unregisters the configuration callback.
*
* @param callback the callback which has been registered with {@link
* #registerConfigurationCallback}
* @throws IllegalArgumentException if {@code callback} hasn't been registered
* @hide
*/
// @FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
// @RequiresPermission(permission.THREAD_NETWORK_PRIVILEGED)
public void unregisterConfigurationCallback(@NonNull Consumer<ThreadConfiguration> callback) {
requireNonNull(callback, "callback cannot be null");
synchronized (mConfigurationCallbackMapLock) {
ConfigurationCallbackProxy callbackProxy = mConfigurationCallbackMap.get(callback);
if (callbackProxy == null) {
throw new IllegalArgumentException("callback hasn't been registered");
}
try {
mControllerService.unregisterConfigurationCallback(callbackProxy);
mConfigurationCallbackMap.remove(callbackProxy.mConfigurationConsumer);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
}
/**
* Sets to use a specified test network as the upstream.
*
@ -764,4 +862,26 @@ public final class ThreadNetworkController {
propagateError(mExecutor, mResultReceiver, errorCode, errorMessage);
}
}
private static final class ConfigurationCallbackProxy extends IConfigurationReceiver.Stub {
final Executor mExecutor;
final Consumer<ThreadConfiguration> mConfigurationConsumer;
ConfigurationCallbackProxy(
@CallbackExecutor Executor executor,
Consumer<ThreadConfiguration> ConfigurationConsumer) {
this.mExecutor = executor;
this.mConfigurationConsumer = ConfigurationConsumer;
}
@Override
public void onConfigurationChanged(ThreadConfiguration configuration) {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> mConfigurationConsumer.accept(configuration));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
}

View file

@ -27,5 +27,9 @@ public final class ThreadNetworkFlags {
/** @hide */
public static final String FLAG_THREAD_ENABLED = "com.android.net.thread.flags.thread_enabled";
/** @hide */
public static final String FLAG_CONFIGURATION_ENABLED =
"com.android.net.thread.flags.configuration_enabled";
private ThreadNetworkFlags() {}
}