Snap for 11383711 from 3ce95aef2059dfbda8d0994220610d9b76b3900e to mainline-cellbroadcast-release
Change-Id: Ic11263fa52ca3eb14aa60ba6da194d167770fa19
diff --git a/Android.bp b/Android.bp
index 357b58f..2b818cb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -90,9 +90,6 @@
cflags: [
// Optimize for debugging
"-Og",
-
- // Enable daemon CLI for debugging
- "-DOPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE=1",
]
}
},
@@ -125,6 +122,7 @@
cflags: [
"-Wall",
"-Wextra",
+ "-Wno-unused-function",
// The HAL client implementation requires features which are
// available on only 31+, but it's guaranteed that ot-daemon
diff --git a/src/agent/main.cpp b/src/agent/main.cpp
index 5409671..4ed5e27 100644
--- a/src/agent/main.cpp
+++ b/src/agent/main.cpp
@@ -78,7 +78,9 @@
OTBR_OPT_REST_LISTEN_PORT,
};
-static jmp_buf sResetJump;
+#ifndef __ANDROID__
+static jmp_buf sResetJump;
+#endif
static otbr::Application *gApp = nullptr;
void __gcov_flush();
@@ -317,12 +319,19 @@
gApp->Deinit();
gApp = nullptr;
+#ifndef __ANDROID__
longjmp(sResetJump, 1);
assert(false);
+#else
+ // Exits immediately on Android. The Android system_server will receive the
+ // signal and decide whether (and how) to restart the ot-daemon
+ exit(0);
+#endif
}
int main(int argc, char *argv[])
{
+#ifndef __ANDROID__
if (setjmp(sResetJump))
{
std::vector<char *> args = AppendAutoAttachDisableArg(argc, argv);
@@ -334,6 +343,6 @@
execvp(args[0], args.data());
}
-
+#endif
return realmain(argc, argv);
}
diff --git a/src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl b/src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl
index 61cae13..3c51df3 100644
--- a/src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl
+++ b/src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl
@@ -46,9 +46,17 @@
*/
const String TUN_IF_NAME = "thread-wpan";
+ /** Thread radio is disabled. */
+ const int OT_STATE_DISABLED = 0;
+ /** Thread radio is enabled. */
+ const int OT_STATE_ENABLED = 1;
+ /** Thread radio is being disabled. */
+ const int OT_STATE_DISABLING = 2;
+
// The error code below MUST be consistent with openthread/include/openthread/error.h
// TODO: add a unit test to make sure that values are always match
enum ErrorCode {
+ OT_ERROR_THREAD_DISABLED = -2,
// TODO: Add this error code to OpenThread and make sure `otDatasetSetActiveTlvs()` returns
// this error code when an unsupported channel is provided
OT_ERROR_UNSUPPORTED_CHANNEL = -1,
@@ -65,13 +73,26 @@
}
/**
- * Initializes this service with Thread tunnel interface FD and stack callback.
+ * Initializes this service with Thread tunnel interface FD.
*
* @param tunFd the Thread tunnel interface FD which can be used to transmit/receive
* packets to/from Thread PAN
- * @param callback the cllback for receiving all Thread stack events
+ * @param enabled the Thead enabled state from Persistent Settings
*/
- void initialize(in ParcelFileDescriptor tunFd);
+ void initialize(in ParcelFileDescriptor tunFd, in boolean enabled);
+
+ /**
+ * Enables/disables Thread.
+ *
+ * When disables Thread, it will first detach from the network without erasing the
+ * active dataset, and then disable Thread radios.
+ *
+ * If called with same Thread enabled state as current state, the method succeeds with
+ * no-op.
+ *
+ * @sa android.net.thread.ThreadNetworkController#setThreadEnabled
+ */
+ void setThreadEnabled(in boolean enabled, in IOtStatusReceiver receiver);
/**
* Registers a callback to receive OpenThread daemon state changes.
diff --git a/src/android/aidl/com/android/server/thread/openthread/IOtDaemonCallback.aidl b/src/android/aidl/com/android/server/thread/openthread/IOtDaemonCallback.aidl
index fb20dc3..28686b1 100644
--- a/src/android/aidl/com/android/server/thread/openthread/IOtDaemonCallback.aidl
+++ b/src/android/aidl/com/android/server/thread/openthread/IOtDaemonCallback.aidl
@@ -62,4 +62,12 @@
* Otherwise, this multicast address is being removed
*/
void onMulticastForwardingAddressChanged(in byte[] ipv6Address, boolean isAdded);
+
+ /**
+ * Called when Thread enabled state has changed. Valid values are STATE_* defined in
+ * {@link ThreadNetworkController}.
+ *
+ * @param enabled {@code true} if Thread is enabled, {@code false} if Thread is disabled.
+ */
+ void onThreadEnabledChanged(in int enabled);
}
diff --git a/src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java b/src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java
index 0a84418..e69201b 100644
--- a/src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java
+++ b/src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java
@@ -28,6 +28,9 @@
package com.android.server.thread.openthread.testing;
+import static com.android.server.thread.openthread.IOtDaemon.OT_STATE_DISABLED;
+import static com.android.server.thread.openthread.IOtDaemon.OT_STATE_ENABLED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
@@ -60,6 +63,7 @@
private final Handler mHandler;
private final OtDaemonState mState;
+ private int mThreadEnabled = OT_STATE_ENABLED;
@Nullable private DeathRecipient mDeathRecipient;
@@ -106,8 +110,26 @@
}
@Override
- public void initialize(ParcelFileDescriptor tunFd) throws RemoteException {
+ public void initialize(ParcelFileDescriptor tunFd, boolean enabled) throws RemoteException {
mTunFd = tunFd;
+ mThreadEnabled = enabled ? OT_STATE_ENABLED : OT_STATE_DISABLED;
+ }
+
+ @Override
+ public void setThreadEnabled(boolean enabled, IOtStatusReceiver receiver) {
+ mHandler.post(
+ () -> {
+ mThreadEnabled = enabled ? OT_STATE_ENABLED : OT_STATE_DISABLED;
+ try {
+ receiver.onSuccess();
+ } catch (RemoteException e) {
+ throw new AssertionError(e);
+ }
+ });
+ }
+
+ public int getEnabledState() {
+ return mThreadEnabled;
}
/**
diff --git a/src/android/otdaemon_server.cpp b/src/android/otdaemon_server.cpp
index 8eb2384..1af673a 100644
--- a/src/android/otdaemon_server.cpp
+++ b/src/android/otdaemon_server.cpp
@@ -67,7 +67,7 @@
static const char OTBR_SERVICE_NAME[] = "ot_daemon";
static constexpr size_t kMaxIp6Size = 1280;
-static void PropagateResult(otError aError,
+static void PropagateResult(int aError,
const std::string &aMessage,
const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
@@ -316,14 +316,83 @@
}
}
-Status OtDaemonServer::initialize(const ScopedFileDescriptor &aTunFd)
+Status OtDaemonServer::initialize(const ScopedFileDescriptor &aTunFd, const bool enabled)
{
otbrLogDebug("OT daemon is initialized by the binder client (tunFd=%d)", aTunFd.get());
- mTunFd = aTunFd.dup();
+ mTunFd = aTunFd.dup();
+ mThreadEnabled = enabled ? IOtDaemon::OT_STATE_ENABLED : IOtDaemon::OT_STATE_DISABLED;
return Status::ok();
}
+void OtDaemonServer::updateThreadEnabledState(const int enabled, const std::shared_ptr<IOtStatusReceiver> &aReceiver)
+{
+ mThreadEnabled = enabled;
+ aReceiver->onSuccess();
+
+ if (mCallback != nullptr)
+ {
+ mCallback->onThreadEnabledChanged(mThreadEnabled);
+ }
+}
+
+void OtDaemonServer::enableThread(const std::shared_ptr<IOtStatusReceiver> &aReceiver)
+{
+ otOperationalDatasetTlvs datasetTlvs;
+
+ if (otDatasetGetActiveTlvs(GetOtInstance(), &datasetTlvs) != OT_ERROR_NOT_FOUND && datasetTlvs.mLength > 0 &&
+ !isAttached())
+ {
+ (void)otIp6SetEnabled(GetOtInstance(), true);
+ (void)otThreadSetEnabled(GetOtInstance(), true);
+ }
+ updateThreadEnabledState(IOtDaemon::OT_STATE_ENABLED, aReceiver);
+}
+
+Status OtDaemonServer::setThreadEnabled(const bool enabled, const std::shared_ptr<IOtStatusReceiver> &aReceiver)
+{
+ int error = OT_ERROR_NONE;
+ std::string message;
+
+ VerifyOrExit(GetOtInstance() != nullptr, error = OT_ERROR_INVALID_STATE, message = "OT is not initialized");
+
+ VerifyOrExit(mThreadEnabled != IOtDaemon::OT_STATE_DISABLING, error = OT_ERROR_BUSY,
+ message = "Thread is disabling");
+
+ if ((mThreadEnabled == IOtDaemon::OT_STATE_ENABLED) == enabled)
+ {
+ aReceiver->onSuccess();
+ ExitNow();
+ }
+
+ if (enabled)
+ {
+ enableThread(aReceiver);
+ }
+ else
+ {
+ mThreadEnabled = IOtDaemon::OT_STATE_DISABLING;
+ if (mCallback != nullptr)
+ {
+ mCallback->onThreadEnabledChanged(mThreadEnabled);
+ }
+
+ LeaveGracefully([aReceiver, this]() {
+ // Ignore errors as those operations should always succeed
+ (void)otThreadSetEnabled(GetOtInstance(), false);
+ (void)otIp6SetEnabled(GetOtInstance(), false);
+ updateThreadEnabledState(IOtDaemon::OT_STATE_DISABLED, aReceiver);
+ });
+ }
+
+exit:
+ if (error != OT_ERROR_NONE)
+ {
+ PropagateResult(error, message, aReceiver);
+ }
+ return Status::ok();
+}
+
Status OtDaemonServer::registerStateCallback(const std::shared_ptr<IOtDaemonCallback> &aCallback, int64_t listenerId)
{
VerifyOrExit(GetOtInstance() != nullptr, otbrLogWarning("OT is not initialized"));
@@ -338,6 +407,7 @@
// state callback, here needs to invoke the callback
RefreshOtDaemonState(/* aFlags */ 0xffffffff);
mCallback->onStateChanged(mState, listenerId);
+ mCallback->onThreadEnabledChanged(mThreadEnabled);
exit:
return Status::ok();
@@ -422,10 +492,16 @@
Status OtDaemonServer::join(const std::vector<uint8_t> &aActiveOpDatasetTlvs,
const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
- otError error = OT_ERROR_NONE;
+ int error = OT_ERROR_NONE;
std::string message;
otOperationalDatasetTlvs datasetTlvs;
+ VerifyOrExit(mThreadEnabled != IOtDaemon::OT_STATE_DISABLING, error = OT_ERROR_BUSY,
+ message = "Thread is disabling");
+
+ VerifyOrExit(mThreadEnabled == IOtDaemon::OT_STATE_ENABLED,
+ error = (int)IOtDaemon::ErrorCode::OT_ERROR_THREAD_DISABLED, message = "Thread is disabled");
+
otbrLogInfo("Start joining...");
VerifyOrExit(GetOtInstance() != nullptr, error = OT_ERROR_INVALID_STATE, message = "OT is not initialized");
@@ -464,15 +540,31 @@
Status OtDaemonServer::leave(const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
- if (GetOtInstance() == nullptr)
+ std::string message;
+ int error = OT_ERROR_NONE;
+
+ VerifyOrExit(GetOtInstance() != nullptr, error = OT_ERROR_INVALID_STATE, message = "OT is not initialized");
+
+ VerifyOrExit(mThreadEnabled != IOtDaemon::OT_STATE_DISABLING, error = OT_ERROR_BUSY,
+ message = "Thread is disabling");
+
+ if (mThreadEnabled == IOtDaemon::OT_STATE_DISABLED)
{
- PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver);
- }
- else
- {
- LeaveGracefully([=]() { aReceiver->onSuccess(); });
+ (void)otInstanceErasePersistentInfo(GetOtInstance());
+ aReceiver->onSuccess();
+ ExitNow();
}
+ LeaveGracefully([aReceiver, this]() {
+ (void)otInstanceErasePersistentInfo(GetOtInstance());
+ aReceiver->onSuccess();
+ });
+
+exit:
+ if (error != OT_ERROR_NONE)
+ {
+ PropagateResult(error, message, aReceiver);
+ }
return Status::ok();
}
@@ -492,21 +584,17 @@
void OtDaemonServer::DetachGracefullyCallback(void)
{
- // Ignore errors as those operations should always succeed
- (void)otThreadSetEnabled(GetOtInstance(), false);
- (void)otIp6SetEnabled(GetOtInstance(), false);
- (void)otInstanceErasePersistentInfo(GetOtInstance());
- otbrLogInfo("leave() success...");
+ otbrLogInfo("detach success...");
if (mJoinReceiver != nullptr)
{
- mJoinReceiver->onError(OT_ERROR_ABORT, "Aborted by leave() operation");
+ mJoinReceiver->onError(OT_ERROR_ABORT, "Aborted by leave/disable operation");
mJoinReceiver = nullptr;
}
if (mMigrationReceiver != nullptr)
{
- mMigrationReceiver->onError(OT_ERROR_ABORT, "Aborted by leave() operation");
+ mMigrationReceiver->onError(OT_ERROR_ABORT, "Aborted by leave/disable operation");
mMigrationReceiver = nullptr;
}
@@ -527,10 +615,16 @@
Status OtDaemonServer::scheduleMigration(const std::vector<uint8_t> &aPendingOpDatasetTlvs,
const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
- otError error = OT_ERROR_NONE;
+ int error;
std::string message;
otOperationalDataset emptyDataset;
+ VerifyOrExit(mThreadEnabled != IOtDaemon::OT_STATE_DISABLING, error = OT_ERROR_BUSY,
+ message = "Thread is disabling");
+
+ VerifyOrExit(mThreadEnabled == IOtDaemon::OT_STATE_ENABLED,
+ error = (int)IOtDaemon::ErrorCode::OT_ERROR_THREAD_DISABLED, message = "Thread is disabled");
+
if (GetOtInstance() == nullptr)
{
message = "OT is not initialized";
@@ -624,11 +718,14 @@
message = "failed to initialize border routing");
SuccessOrExit(error = otBorderRoutingSetEnabled(GetOtInstance(), true /* aEnabled */),
message = "failed to enable border routing");
+ // TODO: b/320836258 - Make BBR independently configurable
+ otBackboneRouterSetEnabled(GetOtInstance(), true /* aEnabled */);
}
else
{
SuccessOrExit(error = otBorderRoutingSetEnabled(GetOtInstance(), false /* aEnabled */),
message = "failed to disable border routing");
+ otBackboneRouterSetEnabled(GetOtInstance(), false /* aEnabled */);
}
}
diff --git a/src/android/otdaemon_server.hpp b/src/android/otdaemon_server.hpp
index c195baa..9a069ab 100644
--- a/src/android/otdaemon_server.hpp
+++ b/src/android/otdaemon_server.hpp
@@ -34,12 +34,13 @@
#include <vector>
#include <aidl/com/android/server/thread/openthread/BnOtDaemon.h>
+#include <aidl/com/android/server/thread/openthread/IOtDaemon.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#include "agent/vendor.hpp"
-#include "common/time.hpp"
#include "common/mainloop.hpp"
+#include "common/time.hpp"
#include "ncp/ncp_openthread.hpp"
namespace otbr {
@@ -50,6 +51,7 @@
using Status = ::ndk::ScopedAStatus;
using aidl::com::android::server::thread::openthread::BnOtDaemon;
using aidl::com::android::server::thread::openthread::BorderRouterConfigurationParcel;
+using aidl::com::android::server::thread::openthread::IOtDaemon;
using aidl::com::android::server::thread::openthread::IOtDaemonCallback;
using aidl::com::android::server::thread::openthread::IOtStatusReceiver;
using aidl::com::android::server::thread::openthread::Ipv6AddressInfo;
@@ -84,7 +86,8 @@
// Implements IOtDaemon.aidl
- Status initialize(const ScopedFileDescriptor &aTunFd) override;
+ Status initialize(const ScopedFileDescriptor &aTunFd, const bool enabled) override;
+ Status setThreadEnabled(const bool enabled, const std::shared_ptr<IOtStatusReceiver> &aReceiver) override;
Status registerStateCallback(const std::shared_ptr<IOtDaemonCallback> &aCallback, int64_t listenerId) override;
bool isAttached(void);
Status join(const std::vector<uint8_t> &aActiveOpDatasetTlvs,
@@ -112,7 +115,10 @@
otBackboneRouterMulticastListenerEvent aEvent,
const otIp6Address *aAddress);
void PushTelemetryIfConditionMatch();
+ void updateThreadEnabledState(const int aEnabled, const std::shared_ptr<IOtStatusReceiver> &aReceiver);
+ void enableThread(const std::shared_ptr<IOtStatusReceiver> &aReceiver);
+ int mThreadEnabled = IOtDaemon::OT_STATE_DISABLED;
otbr::Ncp::ControllerOpenThread &mNcp;
TaskRunner mTaskRunner;
ScopedFileDescriptor mTunFd;
diff --git a/src/android/otdaemon_telemetry.cpp b/src/android/otdaemon_telemetry.cpp
index 6026a1e..f53cfde 100644
--- a/src/android/otdaemon_telemetry.cpp
+++ b/src/android/otdaemon_telemetry.cpp
@@ -649,6 +649,7 @@
ThreadnetworkTopoEntryRepeated topoEntryRepeated;
ThreadnetworkDeviceInfoReported deviceInfoReported;
+ otbrLogInfo("Try to push threadnetwork ATOMs.");
if (RetrieveTelemetryAtom(otInstance, nullptr, telemetryDataReported, topoEntryRepeated, deviceInfoReported) !=
OTBR_ERROR_NONE)
{
@@ -666,6 +667,7 @@
{
otbrLogWarning("Failed to push ThreadnetworkDeviceInfoReported");
}
+ otbrLogInfo("Pushed threadnetwork ATOMs.");
}
} // namespace Android
} // namespace otbr
diff --git a/tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java b/tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java
index 4b84553..18dd400 100644
--- a/tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java
+++ b/tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java
@@ -28,6 +28,9 @@
package com.android.server.thread.openthread.testing;
+import static com.android.server.thread.openthread.IOtDaemon.OT_STATE_DISABLED;
+import static com.android.server.thread.openthread.IOtDaemon.OT_STATE_ENABLED;
+
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
@@ -92,14 +95,14 @@
@Test
public void initialize_succeed_tunFdIsSet() throws Exception {
- mFakeOtDaemon.initialize(mMockTunFd);
+ mFakeOtDaemon.initialize(mMockTunFd, true);
assertThat(mFakeOtDaemon.getTunFd()).isEqualTo(mMockTunFd);
}
@Test
public void registerStateCallback_noStateChange_callbackIsInvoked() throws Exception {
- mFakeOtDaemon.initialize(mMockTunFd);
+ mFakeOtDaemon.initialize(mMockTunFd, true);
final AtomicReference<OtDaemonState> stateRef = new AtomicReference<>();
final AtomicLong listenerIdRef = new AtomicLong();
@@ -176,4 +179,23 @@
assertThat(state.activeDatasetTlvs).isEqualTo(DEFAULT_ACTIVE_DATASET_TLVS);
assertThat(state.multicastForwardingEnabled).isTrue();
}
+
+ @Test
+ public void setThreadEnabled_disableThread_succeed() throws Exception {
+ assertThat(mFakeOtDaemon.getEnabledState()).isEqualTo(OT_STATE_ENABLED);
+
+ final AtomicBoolean succeedRef = new AtomicBoolean(false);
+ mFakeOtDaemon.setThreadEnabled(
+ false,
+ new IOtStatusReceiver.Default() {
+ @Override
+ public void onSuccess() {
+ succeedRef.set(true);
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ assertThat(succeedRef.get()).isTrue();
+ assertThat(mFakeOtDaemon.getEnabledState()).isEqualTo(OT_STATE_DISABLED);
+ }
}