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);
+    }
 }