diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
index 3ff629ac7a..11b74ecd7f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
@@ -33,6 +33,16 @@ interface LocationSharingService {
      */
     suspend fun sendStaticLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable
 
+    /**
+     * Send a live location event to the room.
+     * To get the beacon info event id, [startLiveLocationShare] must be called before sending live location updates.
+     * @param beaconInfoEventId event id of the initial beacon info state event
+     * @param latitude required latitude of the location
+     * @param longitude required longitude of the location
+     * @param uncertainty Accuracy of the location in meters
+     */
+    suspend fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable
+
     /**
      * Starts sharing live location in the room.
      * @param timeoutMillis timeout of the live in milliseconds
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
index 201e6c5c0c..9cf062356f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
@@ -142,15 +142,6 @@ interface SendService {
      */
     fun resendMediaMessage(localEcho: TimelineEvent): Cancelable
 
-    /**
-     * Send a live location event to the room. beacon_info state event has to be sent before sending live location updates.
-     * @param beaconInfoEventId event id of the initial beacon info state event
-     * @param latitude required latitude of the location
-     * @param longitude required longitude of the location
-     * @param uncertainty Accuracy of the location in meters
-     */
-    fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable
-
     /**
      * Remove this failed message from the timeline.
      * @param localEcho the unsent local echo
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt
index ee332890e4..271e82a1e0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt
@@ -51,9 +51,11 @@ import org.matrix.android.sdk.internal.session.room.directory.DefaultSetRoomDire
 import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
 import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
 import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask
+import org.matrix.android.sdk.internal.session.room.location.DefaultSendLiveLocationTask
 import org.matrix.android.sdk.internal.session.room.location.DefaultSendStaticLocationTask
 import org.matrix.android.sdk.internal.session.room.location.DefaultStartLiveLocationShareTask
 import org.matrix.android.sdk.internal.session.room.location.DefaultStopLiveLocationShareTask
+import org.matrix.android.sdk.internal.session.room.location.SendLiveLocationTask
 import org.matrix.android.sdk.internal.session.room.location.SendStaticLocationTask
 import org.matrix.android.sdk.internal.session.room.location.StartLiveLocationShareTask
 import org.matrix.android.sdk.internal.session.room.location.StopLiveLocationShareTask
@@ -314,4 +316,7 @@ internal abstract class RoomModule {
 
     @Binds
     abstract fun bindSendStaticLocationTask(task: DefaultSendStaticLocationTask): SendStaticLocationTask
+
+    @Binds
+    abstract fun bindSendLiveLocationTask(task: DefaultSendLiveLocationTask): SendLiveLocationTask
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt
index 407ae60e4b..ba6e7bd3fa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt
@@ -34,6 +34,7 @@ internal class DefaultLocationSharingService @AssistedInject constructor(
         @Assisted private val roomId: String,
         @SessionDatabase private val monarchy: Monarchy,
         private val sendStaticLocationTask: SendStaticLocationTask,
+        private val sendLiveLocationTask: SendLiveLocationTask,
         private val startLiveLocationShareTask: StartLiveLocationShareTask,
         private val stopLiveLocationShareTask: StopLiveLocationShareTask,
         private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper,
@@ -50,11 +51,22 @@ internal class DefaultLocationSharingService @AssistedInject constructor(
                 latitude = latitude,
                 longitude = longitude,
                 uncertainty = uncertainty,
-                isUserLocation = isUserLocation
+                isUserLocation = isUserLocation,
         )
         return sendStaticLocationTask.execute(params)
     }
 
+    override suspend fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable {
+        val params = SendLiveLocationTask.Params(
+                beaconInfoEventId = beaconInfoEventId,
+                roomId = roomId,
+                latitude = latitude,
+                longitude = longitude,
+                uncertainty = uncertainty,
+        )
+        return sendLiveLocationTask.execute(params)
+    }
+
     override suspend fun startLiveLocationShare(timeoutMillis: Long): String {
         val params = StartLiveLocationShareTask.Params(
                 roomId = roomId,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt
new file mode 100644
index 0000000000..ed40c82b66
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2022 The Matrix.org Foundation C.I.C.
+ *
+ * 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 org.matrix.android.sdk.internal.session.room.location
+
+import org.matrix.android.sdk.api.util.Cancelable
+import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
+import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
+import org.matrix.android.sdk.internal.task.Task
+import javax.inject.Inject
+
+internal interface SendLiveLocationTask : Task<SendLiveLocationTask.Params, Cancelable> {
+    data class Params(
+            val roomId: String,
+            val beaconInfoEventId: String,
+            val latitude: Double,
+            val longitude: Double,
+            val uncertainty: Double?,
+    )
+}
+
+// TODO add unit tests
+internal class DefaultSendLiveLocationTask @Inject constructor(
+        private val localEchoEventFactory: LocalEchoEventFactory,
+        private val eventSenderProcessor: EventSenderProcessor,
+) : SendLiveLocationTask {
+
+    override suspend fun execute(params: SendLiveLocationTask.Params): Cancelable {
+        val event = localEchoEventFactory.createLiveLocationEvent(
+                beaconInfoEventId = params.beaconInfoEventId,
+                roomId = params.roomId,
+                latitude = params.latitude,
+                longitude = params.longitude,
+                uncertainty = params.uncertainty,
+        )
+        localEchoEventFactory.createLocalEcho(event)
+        return eventSenderProcessor.postEvent(event)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
index e83175151e..418000abed 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
@@ -129,12 +129,6 @@ internal class DefaultSendService @AssistedInject constructor(
                 .let { sendEvent(it) }
     }
 
-    override fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable {
-        return localEchoEventFactory.createLiveLocationEvent(beaconInfoEventId, roomId, latitude, longitude, uncertainty)
-                .also { createLocalEcho(it) }
-                .let { sendEvent(it) }
-    }
-
     override fun redactEvent(event: Event, reason: String?): Cancelable {
         // TODO manage media/attachements?
         val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason)
diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt
index 39fc526f80..77f3abcc28 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt
@@ -25,6 +25,7 @@ import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.services.VectorService
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.session.coroutineScope
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import kotlinx.parcelize.Parcelize
 import org.matrix.android.sdk.api.session.Session
@@ -79,13 +80,9 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
             scheduleTimer(roomArgs.roomId, roomArgs.durationMillis)
 
             // Send beacon info state event
-            activeSessionHolder
-                    .getSafeActiveSession()
-                    ?.let { session ->
-                        session.coroutineScope.launch(session.coroutineDispatchers.io) {
-                            sendStartingLiveBeaconInfo(session, roomArgs)
-                        }
-                    }
+            launchInIO { session ->
+                sendStartingLiveBeaconInfo(session, roomArgs)
+            }
         }
 
         return START_STICKY
@@ -143,15 +140,11 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
     }
 
     private fun sendStoppedBeaconInfo(roomId: String) {
-        activeSessionHolder
-                .getSafeActiveSession()
-                ?.let { session ->
-                    session.coroutineScope.launch(session.coroutineDispatchers.io) {
-                        session.getRoom(roomId)
-                                ?.locationSharingService()
-                                ?.stopLiveLocationShare()
-                    }
-                }
+        launchInIO { session ->
+            session.getRoom(roomId)
+                    ?.locationSharingService()
+                    ?.stopLiveLocationShare()
+        }
     }
 
     override fun onLocationUpdate(locationData: LocationData) {
@@ -168,20 +161,16 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
             beaconInfoEventId: String,
             locationData: LocationData
     ) {
-        val session = activeSessionHolder.getSafeActiveSession()
-        val room = session?.getRoom(roomId)
-        val userId = session?.myUserId
-
-        if (room == null || userId == null) {
-            return
+        launchInIO { session ->
+            session.getRoom(roomId)
+                    ?.locationSharingService()
+                    ?.sendLiveLocation(
+                            beaconInfoEventId = beaconInfoEventId,
+                            latitude = locationData.latitude,
+                            longitude = locationData.longitude,
+                            uncertainty = locationData.uncertainty
+                    )
         }
-
-        room.sendService().sendLiveLocation(
-                beaconInfoEventId = beaconInfoEventId,
-                latitude = locationData.latitude,
-                longitude = locationData.longitude,
-                uncertainty = locationData.uncertainty
-        )
     }
 
     override fun onNoLocationProviderAvailable() {
@@ -202,6 +191,16 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
         destroyMe()
     }
 
+    private fun launchInIO(block: suspend CoroutineScope.(Session) -> Unit) =
+            activeSessionHolder
+                    .getSafeActiveSession()
+                    ?.let { session ->
+                        session.coroutineScope.launch(
+                                context = session.coroutineDispatchers.io,
+                                block = { block(session) }
+                        )
+                    }
+
     override fun onBind(intent: Intent?): IBinder {
         return binder
     }