[Linux] BLE advertising: Handle adapter removal (#32434)

* New error code for indicating missing BLE adapter

* [Linux] BLE advertising: Handle adapter removal

* Return adapter unavailable from RegisterGattApplication too

* Account for different timings of adapter removal

* Forward error from async request to the application

* Handle various scenarios when communicating with BlueZ

* Improve readability of error handling blocks
diff --git a/docs/ERROR_CODES.md b/docs/ERROR_CODES.md
index 122d188..ecfda40 100644
--- a/docs/ERROR_CODES.md
+++ b/docs/ERROR_CODES.md
@@ -202,6 +202,7 @@
 
 | Decimal   | Hex   | Name                                        |
 |-----------|-------|---------------------------------------------|
+| 1025      | 0x401 | `BLE_ERROR_ADAPTER_UNAVAILABLE`             |
 | 1027      | 0x403 | `BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK` |
 | 1028      | 0x404 | `BLE_ERROR_CENTRAL_UNSUBSCRIBED`            |
 | 1029      | 0x405 | `BLE_ERROR_GATT_SUBSCRIBE_FAILED`           |
diff --git a/src/ble/BleError.cpp b/src/ble/BleError.cpp
index 641efc2..10ff637 100644
--- a/src/ble/BleError.cpp
+++ b/src/ble/BleError.cpp
@@ -55,6 +55,9 @@
 #if !CHIP_CONFIG_SHORT_ERROR_STR
     switch (err.AsInteger())
     {
+    case BLE_ERROR_ADAPTER_UNAVAILABLE.AsInteger():
+        desc = "BLE adapter unavailable";
+        break;
     case BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK.AsInteger():
         desc = "No chip over BLE connection received callback set";
         break;
diff --git a/src/ble/BleError.h b/src/ble/BleError.h
index f2fb342..a95681a 100644
--- a/src/ble/BleError.h
+++ b/src/ble/BleError.h
@@ -44,7 +44,15 @@
  *  @{
  */
 
-// unused                                                  CHIP_BLE_ERROR(0x01)
+/**
+ *  @def BLE_ERROR_ADAPTER_UNAVAILABLE
+ *
+ *  @brief
+ *    Bluetooth LE adapter is (currently) unavailable.
+ *
+ */
+#define BLE_ERROR_ADAPTER_UNAVAILABLE                      CHIP_BLE_ERROR(0x01)
+
 // unused                                                  CHIP_BLE_ERROR(0x02)
 
 /**
diff --git a/src/ble/tests/TestBleErrorStr.cpp b/src/ble/tests/TestBleErrorStr.cpp
index e2b13f4..caaa8d9 100644
--- a/src/ble/tests/TestBleErrorStr.cpp
+++ b/src/ble/tests/TestBleErrorStr.cpp
@@ -41,6 +41,7 @@
 // clang-format off
 static const CHIP_ERROR kTestElements[] =
 {
+    BLE_ERROR_ADAPTER_UNAVAILABLE,
     BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK,
     BLE_ERROR_CENTRAL_UNSUBSCRIBED,
     BLE_ERROR_GATT_SUBSCRIBE_FAILED,
diff --git a/src/platform/Linux/BLEManagerImpl.cpp b/src/platform/Linux/BLEManagerImpl.cpp
index dbf3791..e472e80 100644
--- a/src/platform/Linux/BLEManagerImpl.cpp
+++ b/src/platform/Linux/BLEManagerImpl.cpp
@@ -287,7 +287,7 @@
                                  PacketBufferHandle::Adopt(apEvent->Platform.BLEIndicationReceived.mData));
         break;
     case DeviceEventType::kPlatformLinuxBLEPeripheralAdvStartComplete:
-        VerifyOrExit(apEvent->Platform.BLEPeripheralAdvStartComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE);
+        SuccessOrExit(err = apEvent->Platform.BLEPeripheralAdvStartComplete.mError);
         sInstance.mFlags.Clear(Flags::kControlOpInProgress).Clear(Flags::kAdvertisingRefreshNeeded);
         // Do not restart the timer if it is still active. This is to avoid the timer from being restarted
         // if the advertising is stopped due to a premature release.
@@ -299,7 +299,7 @@
         sInstance.mFlags.Set(Flags::kAdvertising);
         break;
     case DeviceEventType::kPlatformLinuxBLEPeripheralAdvStopComplete:
-        VerifyOrExit(apEvent->Platform.BLEPeripheralAdvStopComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE);
+        SuccessOrExit(err = apEvent->Platform.BLEPeripheralAdvStopComplete.mError);
         sInstance.mFlags.Clear(Flags::kControlOpInProgress).Clear(Flags::kAdvertisingRefreshNeeded);
         DeviceLayer::SystemLayer().CancelTimer(HandleAdvertisingTimer, this);
 
@@ -316,7 +316,7 @@
         DriveBLEState();
         break;
     case DeviceEventType::kPlatformLinuxBLEPeripheralRegisterAppComplete:
-        VerifyOrExit(apEvent->Platform.BLEPeripheralRegisterAppComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE);
+        SuccessOrExit(err = apEvent->Platform.BLEPeripheralRegisterAppComplete.mError);
         mFlags.Set(Flags::kAppRegistered);
         controlOpComplete = true;
         break;
@@ -656,6 +656,7 @@
     if (err != CHIP_NO_ERROR)
     {
         ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err));
+        DeviceLayer::SystemLayer().CancelTimer(HandleAdvertisingTimer, this);
         mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled;
     }
 }
@@ -790,27 +791,27 @@
     return CHIP_NO_ERROR;
 }
 
-void BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(bool aIsSuccess)
+void BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(CHIP_ERROR error)
 {
     ChipDeviceEvent event;
-    event.Type                                                 = DeviceEventType::kPlatformLinuxBLEPeripheralRegisterAppComplete;
-    event.Platform.BLEPeripheralRegisterAppComplete.mIsSuccess = aIsSuccess;
+    event.Type                                             = DeviceEventType::kPlatformLinuxBLEPeripheralRegisterAppComplete;
+    event.Platform.BLEPeripheralRegisterAppComplete.mError = error;
     PlatformMgr().PostEventOrDie(&event);
 }
 
-void BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(bool aIsSuccess)
+void BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(CHIP_ERROR error)
 {
     ChipDeviceEvent event;
-    event.Type                                              = DeviceEventType::kPlatformLinuxBLEPeripheralAdvStartComplete;
-    event.Platform.BLEPeripheralAdvStartComplete.mIsSuccess = aIsSuccess;
+    event.Type                                          = DeviceEventType::kPlatformLinuxBLEPeripheralAdvStartComplete;
+    event.Platform.BLEPeripheralAdvStartComplete.mError = error;
     PlatformMgr().PostEventOrDie(&event);
 }
 
-void BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(bool aIsSuccess)
+void BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(CHIP_ERROR error)
 {
     ChipDeviceEvent event;
-    event.Type                                             = DeviceEventType::kPlatformLinuxBLEPeripheralAdvStopComplete;
-    event.Platform.BLEPeripheralAdvStopComplete.mIsSuccess = aIsSuccess;
+    event.Type                                         = DeviceEventType::kPlatformLinuxBLEPeripheralAdvStopComplete;
+    event.Platform.BLEPeripheralAdvStopComplete.mError = error;
     PlatformMgr().PostEventOrDie(&event);
 }
 
diff --git a/src/platform/Linux/BLEManagerImpl.h b/src/platform/Linux/BLEManagerImpl.h
index 6e3c1a6..790373c 100644
--- a/src/platform/Linux/BLEManagerImpl.h
+++ b/src/platform/Linux/BLEManagerImpl.h
@@ -92,9 +92,9 @@
     static void HandleTXCharCCCDWrite(BLE_CONNECTION_OBJECT user_data);
     static void HandleTXComplete(BLE_CONNECTION_OBJECT user_data);
 
-    static void NotifyBLEPeripheralRegisterAppComplete(bool aIsSuccess);
-    static void NotifyBLEPeripheralAdvStartComplete(bool aIsSuccess);
-    static void NotifyBLEPeripheralAdvStopComplete(bool aIsSuccess);
+    static void NotifyBLEPeripheralRegisterAppComplete(CHIP_ERROR error);
+    static void NotifyBLEPeripheralAdvStartComplete(CHIP_ERROR error);
+    static void NotifyBLEPeripheralAdvStopComplete(CHIP_ERROR error);
     static void NotifyBLEPeripheralAdvReleased();
 
 private:
diff --git a/src/platform/Linux/CHIPDevicePlatformEvent.h b/src/platform/Linux/CHIPDevicePlatformEvent.h
index b6e0b55..019dcb3 100644
--- a/src/platform/Linux/CHIPDevicePlatformEvent.h
+++ b/src/platform/Linux/CHIPDevicePlatformEvent.h
@@ -91,15 +91,15 @@
         } BLEIndicationReceived;
         struct
         {
-            bool mIsSuccess;
+            CHIP_ERROR mError;
         } BLEPeripheralRegisterAppComplete;
         struct
         {
-            bool mIsSuccess;
+            CHIP_ERROR mError;
         } BLEPeripheralAdvStartComplete;
         struct
         {
-            bool mIsSuccess;
+            CHIP_ERROR mError;
         } BLEPeripheralAdvStopComplete;
     };
 };
diff --git a/src/platform/Linux/bluez/BluezAdvertisement.cpp b/src/platform/Linux/bluez/BluezAdvertisement.cpp
index d1e43c5..db44bd6 100644
--- a/src/platform/Linux/bluez/BluezAdvertisement.cpp
+++ b/src/platform/Linux/bluez/BluezAdvertisement.cpp
@@ -24,6 +24,7 @@
 #include <glib-object.h>
 #include <glib.h>
 
+#include <ble/BleError.h>
 #include <lib/support/CodeUtils.h>
 #include <lib/support/logging/CHIPLogging.h>
 #include <platform/ConfigurationManager.h>
@@ -109,15 +110,13 @@
 
 CHIP_ERROR BluezAdvertisement::Init(const BluezEndpoint & aEndpoint, const char * aAdvUUID, const char * aAdvName)
 {
-    GAutoPtr<char> rootPath;
-    CHIP_ERROR err;
-
-    VerifyOrExit(!mAdv, err = CHIP_ERROR_INCORRECT_STATE;
-                 ChipLogError(DeviceLayer, "FAIL: BLE advertisement already initialized in %s", __func__));
+    VerifyOrReturnError(!mAdv, CHIP_ERROR_INCORRECT_STATE,
+                        ChipLogError(DeviceLayer, "FAIL: BLE advertisement already initialized in %s", __func__));
 
     mRoot.reset(reinterpret_cast<GDBusObjectManagerServer *>(g_object_ref(aEndpoint.GetGattApplicationObjectManager())));
     mAdapter.reset(reinterpret_cast<BluezAdapter1 *>(g_object_ref(aEndpoint.GetAdapter())));
 
+    GAutoPtr<char> rootPath;
     g_object_get(G_OBJECT(mRoot.get()), "object-path", &rootPath.GetReceiver(), nullptr);
     g_snprintf(mAdvPath, sizeof(mAdvPath), "%s/advertising", rootPath.get());
     g_strlcpy(mAdvUUID, aAdvUUID, sizeof(mAdvUUID));
@@ -132,15 +131,13 @@
         g_snprintf(mAdvName, sizeof(mAdvName), "%s%04x", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, getpid() & 0xffff);
     }
 
-    err = PlatformMgrImpl().GLibMatterContextInvokeSync(
+    CHIP_ERROR err = PlatformMgrImpl().GLibMatterContextInvokeSync(
         +[](BluezAdvertisement * self) { return self->InitImpl(); }, this);
-    VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_ERROR_INCORRECT_STATE,
+    VerifyOrReturnError(err == CHIP_NO_ERROR, err,
                         ChipLogError(Ble, "Failed to schedule BLE advertisement Init() on CHIPoBluez thread"));
 
     mIsInitialized = true;
-
-exit:
-    return err;
+    return CHIP_NO_ERROR;
 }
 
 CHIP_ERROR BluezAdvertisement::SetIntervals(AdvertisingIntervals aAdvIntervals)
@@ -224,39 +221,49 @@
 
 void BluezAdvertisement::StartDone(GObject * aObject, GAsyncResult * aResult)
 {
-    auto * advMgr = reinterpret_cast<BluezLEAdvertisingManager1 *>(aObject);
     GAutoPtr<GError> error;
-    gboolean success = FALSE;
-
-    success = bluez_leadvertising_manager1_call_register_advertisement_finish(advMgr, aResult, &error.GetReceiver());
-    VerifyOrExit(success == TRUE, ChipLogError(DeviceLayer, "FAIL: RegisterAdvertisement : %s", error->message));
+    if (!bluez_leadvertising_manager1_call_register_advertisement_finish(reinterpret_cast<BluezLEAdvertisingManager1 *>(aObject),
+                                                                         aResult, &error.GetReceiver()))
+    {
+        ChipLogError(DeviceLayer, "FAIL: RegisterAdvertisement: %s", error->message);
+        switch (error->code)
+        {
+        case G_DBUS_ERROR_NO_REPLY:        // BlueZ crashed or the D-Bus connection is broken
+        case G_DBUS_ERROR_SERVICE_UNKNOWN: // BlueZ service is not available on the bus
+        case G_DBUS_ERROR_UNKNOWN_OBJECT:  // Requested BLE adapter is not available
+            BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(BLE_ERROR_ADAPTER_UNAVAILABLE);
+            break;
+        default:
+            BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(CHIP_ERROR_INTERNAL);
+        }
+        return;
+    }
 
     mIsAdvertising = true;
 
-    ChipLogDetail(DeviceLayer, "RegisterAdvertisement complete");
-
-exit:
-    BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(success == TRUE);
+    ChipLogDetail(DeviceLayer, "BLE advertisement started successfully");
+    BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(CHIP_NO_ERROR);
 }
 
 CHIP_ERROR BluezAdvertisement::StartImpl()
 {
-    GDBusObject * adapterObject;
-    GAutoPtr<BluezLEAdvertisingManager1> advMgr;
+    VerifyOrReturnError(mAdapter, CHIP_ERROR_UNINITIALIZED);
+
+    // If the adapter configured in the Init() was unplugged, the g_dbus_interface_get_object()
+    // or bluez_object_get_leadvertising_manager1() might return nullptr (depending on the timing,
+    // since the D-Bus communication is handled on a separate thread). In such case, we should not
+    // report internal error, but adapter unavailable, so the application can handle the situation
+    // properly.
+
+    GDBusObject * adapterObject = g_dbus_interface_get_object(reinterpret_cast<GDBusInterface *>(mAdapter.get()));
+    VerifyOrReturnError(adapterObject != nullptr, BLE_ERROR_ADAPTER_UNAVAILABLE);
+    GAutoPtr<BluezLEAdvertisingManager1> advMgr(
+        bluez_object_get_leadvertising_manager1(reinterpret_cast<BluezObject *>(adapterObject)));
+    VerifyOrReturnError(advMgr, BLE_ERROR_ADAPTER_UNAVAILABLE);
+
     GVariantBuilder optionsBuilder;
-    GVariant * options;
-
-    VerifyOrExit(!mIsAdvertising, ChipLogError(DeviceLayer, "FAIL: Advertising has already been enabled in %s", __func__));
-    VerifyOrExit(mAdapter, ChipLogError(DeviceLayer, "FAIL: NULL mAdapter in %s", __func__));
-
-    adapterObject = g_dbus_interface_get_object(G_DBUS_INTERFACE(mAdapter.get()));
-    VerifyOrExit(adapterObject != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL adapterObject in %s", __func__));
-
-    advMgr.reset(bluez_object_get_leadvertising_manager1(reinterpret_cast<BluezObject *>(adapterObject)));
-    VerifyOrExit(advMgr, ChipLogError(DeviceLayer, "FAIL: NULL advMgr in %s", __func__));
-
     g_variant_builder_init(&optionsBuilder, G_VARIANT_TYPE("a{sv}"));
-    options = g_variant_builder_end(&optionsBuilder);
+    GVariant * options = g_variant_builder_end(&optionsBuilder);
 
     bluez_leadvertising_manager1_call_register_advertisement(
         advMgr.get(), mAdvPath, options, nullptr,
@@ -265,51 +272,58 @@
         },
         this);
 
-exit:
     return CHIP_NO_ERROR;
 }
 
 CHIP_ERROR BluezAdvertisement::Start()
 {
     VerifyOrReturnError(mIsInitialized, CHIP_ERROR_INCORRECT_STATE);
-
-    CHIP_ERROR err = PlatformMgrImpl().GLibMatterContextInvokeSync(
+    VerifyOrReturnValue(!mIsAdvertising, CHIP_NO_ERROR, ChipLogDetail(DeviceLayer, "BLE advertising already started"));
+    return PlatformMgrImpl().GLibMatterContextInvokeSync(
         +[](BluezAdvertisement * self) { return self->StartImpl(); }, this);
-    VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_ERROR_INCORRECT_STATE,
-                        ChipLogError(Ble, "Failed to schedule BLE advertisement Start() on CHIPoBluez thread"));
-    return err;
 }
 
 void BluezAdvertisement::StopDone(GObject * aObject, GAsyncResult * aResult)
 {
-    auto * advMgr = reinterpret_cast<BluezLEAdvertisingManager1 *>(aObject);
     GAutoPtr<GError> error;
-    gboolean success = FALSE;
-
-    success = bluez_leadvertising_manager1_call_unregister_advertisement_finish(advMgr, aResult, &error.GetReceiver());
-    VerifyOrExit(success == TRUE, ChipLogError(DeviceLayer, "FAIL: UnregisterAdvertisement: %s", error->message));
+    if (!bluez_leadvertising_manager1_call_unregister_advertisement_finish(reinterpret_cast<BluezLEAdvertisingManager1 *>(aObject),
+                                                                           aResult, &error.GetReceiver()))
+    {
+        ChipLogError(DeviceLayer, "FAIL: UnregisterAdvertisement: %s", error->message);
+        switch (error->code)
+        {
+        case G_DBUS_ERROR_NO_REPLY:        // BlueZ crashed or the D-Bus connection is broken
+        case G_DBUS_ERROR_SERVICE_UNKNOWN: // BlueZ service is not available on the bus
+        case G_DBUS_ERROR_UNKNOWN_OBJECT:  // Requested BLE adapter is not available
+            BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(BLE_ERROR_ADAPTER_UNAVAILABLE);
+            break;
+        default:
+            BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(CHIP_ERROR_INTERNAL);
+        }
+        return;
+    }
 
     mIsAdvertising = false;
 
-    ChipLogDetail(DeviceLayer, "UnregisterAdvertisement complete");
-
-exit:
-    BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(success == TRUE);
+    ChipLogDetail(DeviceLayer, "BLE advertisement stopped successfully");
+    BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(CHIP_NO_ERROR);
 }
 
 CHIP_ERROR BluezAdvertisement::StopImpl()
 {
-    GDBusObject * adapterObject;
-    GAutoPtr<BluezLEAdvertisingManager1> advMgr;
+    VerifyOrReturnError(mAdapter, CHIP_ERROR_UNINITIALIZED);
 
-    VerifyOrExit(mIsAdvertising, ChipLogError(DeviceLayer, "FAIL: Advertising has already been disabled in %s", __func__));
-    VerifyOrExit(mAdapter, ChipLogError(DeviceLayer, "FAIL: NULL mAdapter in %s", __func__));
+    // If the adapter configured in the Init() was unplugged, the g_dbus_interface_get_object()
+    // or bluez_object_get_leadvertising_manager1() might return nullptr (depending on the timing,
+    // since the D-Bus communication is handled on a separate thread). In such case, we should not
+    // report internal error, but adapter unavailable, so the application can handle the situation
+    // properly.
 
-    adapterObject = g_dbus_interface_get_object(G_DBUS_INTERFACE(mAdapter.get()));
-    VerifyOrExit(adapterObject != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL adapterObject in %s", __func__));
-
-    advMgr.reset(bluez_object_get_leadvertising_manager1(reinterpret_cast<BluezObject *>(adapterObject)));
-    VerifyOrExit(advMgr, ChipLogError(DeviceLayer, "FAIL: NULL advMgr in %s", __func__));
+    GDBusObject * adapterObject = g_dbus_interface_get_object(reinterpret_cast<GDBusInterface *>(mAdapter.get()));
+    VerifyOrReturnError(adapterObject != nullptr, BLE_ERROR_ADAPTER_UNAVAILABLE);
+    GAutoPtr<BluezLEAdvertisingManager1> advMgr(
+        bluez_object_get_leadvertising_manager1(reinterpret_cast<BluezObject *>(adapterObject)));
+    VerifyOrReturnError(advMgr, BLE_ERROR_ADAPTER_UNAVAILABLE);
 
     bluez_leadvertising_manager1_call_unregister_advertisement(
         advMgr.get(), mAdvPath, nullptr,
@@ -318,19 +332,15 @@
         },
         this);
 
-exit:
     return CHIP_NO_ERROR;
 }
 
 CHIP_ERROR BluezAdvertisement::Stop()
 {
     VerifyOrReturnError(mIsInitialized, CHIP_ERROR_INCORRECT_STATE);
-
-    CHIP_ERROR err = PlatformMgrImpl().GLibMatterContextInvokeSync(
+    VerifyOrReturnValue(mIsAdvertising, CHIP_NO_ERROR, ChipLogDetail(DeviceLayer, "BLE advertising already stopped"));
+    return PlatformMgrImpl().GLibMatterContextInvokeSync(
         +[](BluezAdvertisement * self) { return self->StopImpl(); }, this);
-    VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_ERROR_INCORRECT_STATE,
-                        ChipLogError(Ble, "Failed to schedule BLE advertisement Stop() on CHIPoBluez thread"));
-    return err;
 }
 
 } // namespace Internal
diff --git a/src/platform/Linux/bluez/BluezEndpoint.cpp b/src/platform/Linux/bluez/BluezEndpoint.cpp
index c32b1ec..e622407 100644
--- a/src/platform/Linux/bluez/BluezEndpoint.cpp
+++ b/src/platform/Linux/bluez/BluezEndpoint.cpp
@@ -57,6 +57,7 @@
 #include <glib-object.h>
 #include <glib.h>
 
+#include <ble/BleError.h>
 #include <lib/support/BitFlags.h>
 #include <lib/support/CHIPMem.h>
 #include <lib/support/CodeUtils.h>
@@ -263,35 +264,45 @@
 void BluezEndpoint::RegisterGattApplicationDone(GObject * aObject, GAsyncResult * aResult)
 {
     GAutoPtr<GError> error;
-    gboolean success = bluez_gatt_manager1_call_register_application_finish(reinterpret_cast<BluezGattManager1 *>(aObject), aResult,
-                                                                            &error.GetReceiver());
+    if (!bluez_gatt_manager1_call_register_application_finish(reinterpret_cast<BluezGattManager1 *>(aObject), aResult,
+                                                              &error.GetReceiver()))
+    {
+        ChipLogError(DeviceLayer, "FAIL: RegisterGattApplication: %s", error->message);
+        switch (error->code)
+        {
+        case G_DBUS_ERROR_NO_REPLY:        // BlueZ crashed or the D-Bus connection is broken
+        case G_DBUS_ERROR_SERVICE_UNKNOWN: // BlueZ service is not available on the bus
+        case G_DBUS_ERROR_UNKNOWN_OBJECT:  // Requested BLE adapter is not available
+            BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(BLE_ERROR_ADAPTER_UNAVAILABLE);
+            break;
+        default:
+            BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(CHIP_ERROR_INTERNAL);
+        }
+        return;
+    }
 
-    VerifyOrReturn(success == TRUE, {
-        ChipLogError(DeviceLayer, "FAIL: RegisterApplication : %s", error->message);
-        BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(false);
-    });
-
-    BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(true);
-    ChipLogDetail(DeviceLayer, "BluezPeripheralRegisterAppDone done");
+    ChipLogDetail(DeviceLayer, "GATT application registered successfully");
+    BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(CHIP_NO_ERROR);
 }
 
 CHIP_ERROR BluezEndpoint::RegisterGattApplicationImpl()
 {
-    GDBusObject * adapterObject;
-    GAutoPtr<BluezGattManager1> gattMgr;
+    VerifyOrReturnError(mAdapter, CHIP_ERROR_UNINITIALIZED);
+
+    // If the adapter configured in the Init() was unplugged, the g_dbus_interface_get_object()
+    // or bluez_object_get_gatt_manager1() might return nullptr (depending on the timing, since
+    // the D-Bus communication is handled on a separate thread). In such case, we should not
+    // report internal error, but adapter unavailable, so the application can handle the situation
+    // properly.
+
+    GDBusObject * adapterObject = g_dbus_interface_get_object(reinterpret_cast<GDBusInterface *>(mAdapter.get()));
+    VerifyOrReturnError(adapterObject != nullptr, BLE_ERROR_ADAPTER_UNAVAILABLE);
+    GAutoPtr<BluezGattManager1> gattMgr(bluez_object_get_gatt_manager1(reinterpret_cast<BluezObject *>(adapterObject)));
+    VerifyOrReturnError(gattMgr, BLE_ERROR_ADAPTER_UNAVAILABLE);
+
     GVariantBuilder optionsBuilder;
-    GVariant * options;
-
-    VerifyOrExit(mAdapter, ChipLogError(DeviceLayer, "FAIL: NULL mAdapter in %s", __func__));
-
-    adapterObject = g_dbus_interface_get_object(G_DBUS_INTERFACE(mAdapter.get()));
-    VerifyOrExit(adapterObject != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL adapterObject in %s", __func__));
-
-    gattMgr.reset(bluez_object_get_gatt_manager1(reinterpret_cast<BluezObject *>(adapterObject)));
-    VerifyOrExit(gattMgr, ChipLogError(DeviceLayer, "FAIL: NULL gattMgr in %s", __func__));
-
     g_variant_builder_init(&optionsBuilder, G_VARIANT_TYPE("a{sv}"));
-    options = g_variant_builder_end(&optionsBuilder);
+    GVariant * options = g_variant_builder_end(&optionsBuilder);
 
     bluez_gatt_manager1_call_register_application(
         gattMgr.get(), mpRootPath, options, nullptr,
@@ -300,7 +311,6 @@
         },
         this);
 
-exit:
     return CHIP_NO_ERROR;
 }
 
@@ -624,11 +634,8 @@
 
 CHIP_ERROR BluezEndpoint::RegisterGattApplication()
 {
-    CHIP_ERROR err = PlatformMgrImpl().GLibMatterContextInvokeSync(
+    return PlatformMgrImpl().GLibMatterContextInvokeSync(
         +[](BluezEndpoint * self) { return self->RegisterGattApplicationImpl(); }, this);
-    VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_ERROR_INCORRECT_STATE,
-                        ChipLogError(Ble, "Failed to schedule RegisterGattApplication() on CHIPoBluez thread"));
-    return err;
 }
 
 CHIP_ERROR BluezEndpoint::Init(bool aIsCentral, uint32_t aAdapterId)