diff --git a/jami-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.kt b/jami-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.kt
deleted file mode 100644
index fb97c93a54c42e870548ce279c69fef4c8bf674f..0000000000000000000000000000000000000000
--- a/jami-android/app/src/main/java/cx/ring/account/AccountCreationModelImpl.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package cx.ring.account
-
-import net.jami.model.AccountCreationModel
-import ezvcard.VCard
-import ezvcard.property.FormattedName
-import ezvcard.property.Uid
-import android.graphics.Bitmap
-import cx.ring.utils.BitmapUtils
-import ezvcard.property.Photo
-import ezvcard.property.RawProperty
-import io.reactivex.rxjava3.core.Single
-
-class AccountCreationModelImpl : AccountCreationModel() {
-    override fun toVCard(): Single<VCard> = Single.fromCallable {
-        val vcard = VCard()
-        vcard.formattedName = FormattedName(fullName)
-        vcard.uid = Uid(username)
-        val bmp = photo as Bitmap?
-        if (bmp != null) {
-            vcard.removeProperties(Photo::class.java)
-            vcard.addPhoto(BitmapUtils.bitmapToPhoto(bmp))
-        }
-        vcard.removeProperties(RawProperty::class.java)
-        vcard
-    }
-}
\ No newline at end of file
diff --git a/jami-android/app/src/main/java/cx/ring/account/AccountCreationViewModel.kt b/jami-android/app/src/main/java/cx/ring/account/AccountCreationViewModel.kt
index f44959df98265984926b82843dc6e450b170d604..cc2bbcb7c9712c0595e6b890d89ff1726e36c4e5 100644
--- a/jami-android/app/src/main/java/cx/ring/account/AccountCreationViewModel.kt
+++ b/jami-android/app/src/main/java/cx/ring/account/AccountCreationViewModel.kt
@@ -1,7 +1,8 @@
 package cx.ring.account
 
 import androidx.lifecycle.ViewModel
+import net.jami.model.AccountCreationModel
 
 class AccountCreationViewModel: ViewModel() {
-    var model = AccountCreationModelImpl()
+    var model = AccountCreationModel()
 }
\ No newline at end of file
diff --git a/jami-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt b/jami-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt
index b5ebf9b62d88371254756ee1157262a90dd6afbb..dc0876e737df836e2a1d06ef31ae3cb346219bcb 100644
--- a/jami-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt
+++ b/jami-android/app/src/main/java/cx/ring/account/AccountWizardActivity.kt
@@ -19,9 +19,11 @@ package cx.ring.account
 import android.Manifest
 import android.content.Intent
 import android.content.pm.ActivityInfo
+import android.graphics.Bitmap
 import android.os.Build
 import android.os.Bundle
 import android.text.TextUtils
+import android.util.Log
 import android.widget.Toast
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.activity.viewModels
@@ -34,17 +36,15 @@ import cx.ring.databinding.ItemProgressDialogBinding
 import cx.ring.fragments.AccountMigrationFragment
 import cx.ring.fragments.SIPAccountCreationFragment
 import cx.ring.mvp.BaseActivity
-import cx.ring.services.VCardServiceImpl
 import cx.ring.utils.BiometricHelper
+import cx.ring.utils.BitmapUtils
 import dagger.hilt.android.AndroidEntryPoint
-import ezvcard.VCard
 import io.reactivex.rxjava3.core.Single
 import io.reactivex.rxjava3.schedulers.Schedulers
 import net.jami.account.AccountWizardPresenter
 import net.jami.account.AccountWizardView
 import net.jami.model.Account
 import net.jami.model.AccountConfig
-import net.jami.utils.VCardUtils
 
 @AndroidEntryPoint
 class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWizardView {
@@ -91,15 +91,18 @@ class AccountWizardActivity : BaseActivity<AccountWizardPresenter>(), AccountWiz
         super.onDestroy()
     }
 
-    override fun saveProfile(account: Account): Single<VCard> {
-        val filedir = filesDir
+    override fun saveProfile(account: Account) {
         val model: AccountCreationViewModel by viewModels()
-        return model.model.toVCard()
-            .flatMap { vcard: VCard ->
-                account.loadedProfile = Single.fromCallable { VCardServiceImpl.readData(vcard) }.cache()
-                VCardUtils.saveLocalProfileToDisk(vcard, account.accountId, filedir)
-            }
-            .subscribeOn(Schedulers.io())
+        val name = model.model.fullName
+        val photo = model.model.photo as? Bitmap?
+        if (photo != null) {
+            Single.just(photo).map { BitmapUtils.bitmapToBase64(it)!! }
+                .observeOn(Schedulers.computation())
+                .subscribe({ presenter.updateProfile(account.accountId, name, it, "PNG") })
+                { e -> Log.e(TAG, "Error updating profile", e) }
+        } else {
+            presenter.updateProfile(account.accountId, model.model.fullName, "", "")
+        }
     }
 
     fun createAccount() {
diff --git a/jami-android/app/src/main/java/cx/ring/account/HomeAccountCreationFragment.kt b/jami-android/app/src/main/java/cx/ring/account/HomeAccountCreationFragment.kt
index 7186e99a2e779a6c4cca2073843572671fa65623..27f6d38772a339f3254bf1e8f1f17d3b94863d23 100644
--- a/jami-android/app/src/main/java/cx/ring/account/HomeAccountCreationFragment.kt
+++ b/jami-android/app/src/main/java/cx/ring/account/HomeAccountCreationFragment.kt
@@ -35,6 +35,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 import io.reactivex.rxjava3.disposables.CompositeDisposable
 import net.jami.account.HomeAccountCreationPresenter
 import net.jami.account.HomeAccountCreationView
+import net.jami.model.AccountCreationModel
 import net.jami.utils.Log
 import java.io.File
 
@@ -51,7 +52,7 @@ class HomeAccountCreationFragment :
                 getCacheFile(requireContext(), uri)
                     .observeOn(AndroidSchedulers.mainThread())
                     .subscribe({ file: File ->
-                        model.model = AccountCreationModelImpl().apply {
+                        model.model = AccountCreationModel().apply {
                             isLink = true
                             archive = file
                         }
@@ -89,19 +90,19 @@ class HomeAccountCreationFragment :
     }
 
     override fun goToAccountCreation() {
-        model.model = AccountCreationModelImpl()
+        model.model = AccountCreationModel()
         replaceFragmentWithSlide(JamiAccountCreationFragment(), JamiAccountCreationFragment.TAG, R.id.wizard_container)
     }
 
     override fun goToAccountLink() {
-        model.model = AccountCreationModelImpl().apply {
+        model.model = AccountCreationModel().apply {
             isLink = true
         }
         replaceFragmentWithSlide(JamiLinkAccountFragment(), JamiLinkAccountFragment.TAG, R.id.wizard_container)
     }
 
     override fun goToAccountConnect() {
-        model.model = AccountCreationModelImpl().apply {
+        model.model = AccountCreationModel().apply {
             isLink = true
         }
         replaceFragmentWithSlide(JamiAccountConnectFragment(), JamiAccountConnectFragment.TAG, R.id.wizard_container)
diff --git a/jami-android/app/src/main/java/cx/ring/account/JamiAccountSummaryFragment.kt b/jami-android/app/src/main/java/cx/ring/account/JamiAccountSummaryFragment.kt
index ebb05e61457450b777ae9211dd6b782a1711777d..6d12eafd1239ec5e073565c1189d144a4b7b0f76 100644
--- a/jami-android/app/src/main/java/cx/ring/account/JamiAccountSummaryFragment.kt
+++ b/jami-android/app/src/main/java/cx/ring/account/JamiAccountSummaryFragment.kt
@@ -80,6 +80,7 @@ import net.jami.model.Account
 import net.jami.model.Contact
 import net.jami.model.Profile
 import net.jami.services.AccountService
+import net.jami.utils.VCardUtils
 import java.io.File
 import javax.inject.Inject
 
@@ -271,7 +272,7 @@ class JamiAccountSummaryFragment :
             binding.username.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus: Boolean ->
                 val name = binding.username.text
                 if (!hasFocus) {
-                    presenter.saveVCardFormattedName(name.toString())
+                    presenter.updateProfile(name.toString())
                 }
             }
         }
@@ -343,9 +344,11 @@ class JamiAccountSummaryFragment :
             .setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.cancel() }
             .setPositiveButton(android.R.string.ok) { dialog, which ->
                 mSourcePhoto?.let { source ->
-                    presenter.saveVCard(mBinding!!.username.text.toString(),
-                        Single.just(source).map { obj -> BitmapUtils.bitmapToPhoto(obj) })
-                } ?: presenter.saveVCard(mBinding!!.username.text.toString(), null)
+                    Single.just(source).map { BitmapUtils.bitmapToBase64(it)!! }
+                        .observeOn(Schedulers.computation())
+                        .subscribe({ presenter.updateProfile(mBinding!!.username.text.toString(), it, "PNG") })
+                        { e -> Log.e(TAG, "Error updating profile", e) }
+                } ?: presenter.updateProfile(mBinding!!.username.text.toString(), "", "")
             }
             .setOnDismissListener {
                 dialogDisposableBag.dispose()
diff --git a/jami-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt b/jami-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt
index 4a0846169bfa02c86b0d25c4b252ff3d1b5b45be..70675a81af2ee73fd4f9f7cdaec8ff4202cb465a 100644
--- a/jami-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt
+++ b/jami-android/app/src/main/java/cx/ring/tv/account/TVAccountWizard.kt
@@ -17,6 +17,7 @@
 package cx.ring.tv.account
 
 import android.content.Intent
+import android.graphics.Bitmap
 import android.os.Build
 import android.os.Bundle
 import android.widget.Toast
@@ -29,16 +30,13 @@ import cx.ring.account.AccountCreationViewModel
 import cx.ring.application.JamiApplication
 import cx.ring.databinding.ItemProgressDialogBinding
 import cx.ring.mvp.BaseActivity
-import cx.ring.services.VCardServiceImpl
+import cx.ring.utils.BitmapUtils
 import dagger.hilt.android.AndroidEntryPoint
-import ezvcard.VCard
-import io.reactivex.rxjava3.core.Single
-import io.reactivex.rxjava3.schedulers.Schedulers
 import net.jami.account.AccountWizardPresenter
 import net.jami.account.AccountWizardView
 import net.jami.model.Account
 import net.jami.model.AccountConfig
-import net.jami.utils.VCardUtils
+import net.jami.model.AccountCreationModel
 
 @AndroidEntryPoint
 class TVAccountWizard : BaseActivity<AccountWizardPresenter>(), AccountWizardView {
@@ -165,16 +163,14 @@ class TVAccountWizard : BaseActivity<AccountWizardPresenter>(), AccountWizardVie
         }
     }
 
-    override fun saveProfile(account: Account): Single<VCard> {
-        val filedir = filesDir
+    override fun saveProfile(account: Account){
         val model: AccountCreationViewModel by viewModels()
-        return model.model.toVCard()
-            .flatMap { vcard ->
-                account.loadedProfile =
-                    Single.fromCallable { VCardServiceImpl.readData(vcard) }.cache()
-                VCardUtils.saveLocalProfileToDisk(vcard, account.accountId, filedir)
-            }
-            .subscribeOn(Schedulers.io())
+        val base64img = BitmapUtils.bitmapToBase64(model.model.photo as? Bitmap)
+        if (base64img != null) {
+            presenter.updateProfile(account.accountId, model.model.fullName, base64img, "PNG")
+        } else {
+            presenter.updateProfile(account.accountId, model.model.fullName, "", "")
+        }
     }
 
     override fun displayGenericError() {
diff --git a/jami-android/app/src/main/java/cx/ring/tv/account/TVHomeAccountCreationFragment.kt b/jami-android/app/src/main/java/cx/ring/tv/account/TVHomeAccountCreationFragment.kt
index 06f4496885405f56579f3af896f846b26177c6e4..06022db504f8e0d838fa50193db91526b509ef62 100644
--- a/jami-android/app/src/main/java/cx/ring/tv/account/TVHomeAccountCreationFragment.kt
+++ b/jami-android/app/src/main/java/cx/ring/tv/account/TVHomeAccountCreationFragment.kt
@@ -21,11 +21,11 @@ import androidx.fragment.app.activityViewModels
 import androidx.leanback.widget.GuidanceStylist.Guidance
 import androidx.leanback.widget.GuidedAction
 import cx.ring.R
-import cx.ring.account.AccountCreationModelImpl
 import cx.ring.account.AccountCreationViewModel
 import dagger.hilt.android.AndroidEntryPoint
 import net.jami.account.HomeAccountCreationPresenter
 import net.jami.account.HomeAccountCreationView
+import net.jami.model.AccountCreationModel
 
 @AndroidEntryPoint
 class TVHomeAccountCreationFragment : JamiGuidedStepFragment<HomeAccountCreationPresenter, HomeAccountCreationView>(),
@@ -33,21 +33,21 @@ class TVHomeAccountCreationFragment : JamiGuidedStepFragment<HomeAccountCreation
     private val model: AccountCreationViewModel by activityViewModels()
 
     override fun goToAccountCreation() {
-        model.model = AccountCreationModelImpl().apply {
+        model.model = AccountCreationModel().apply {
             isLink = false
         }
         add(parentFragmentManager, TVJamiAccountCreationFragment())
     }
 
     override fun goToAccountLink() {
-        model.model = AccountCreationModelImpl().apply {
+        model.model = AccountCreationModel().apply {
             isLink = true
         }
         add(parentFragmentManager, TVJamiLinkAccountFragment())
     }
 
     override fun goToAccountConnect() {
-        model.model = AccountCreationModelImpl().apply {
+        model.model = AccountCreationModel().apply {
             isLink = true
         }
         add(parentFragmentManager, TVJamiAccountConnectFragment())
diff --git a/jami-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.kt b/jami-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.kt
index 3e641ff1c8acb831383f23b47a3300a79a97abd4..a6eb0419b877fc7602e5bba359625b5d5d9cbf4d 100644
--- a/jami-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.kt
+++ b/jami-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.kt
@@ -19,7 +19,6 @@ package cx.ring.tv.account
 import android.Manifest
 import android.app.Activity
 import android.content.Intent
-import android.graphics.Bitmap
 import android.graphics.BitmapFactory
 import android.net.Uri
 import android.os.Bundle
@@ -35,12 +34,13 @@ import cx.ring.account.ProfileCreationFragment
 import cx.ring.tv.camera.CustomCameraActivity
 import cx.ring.utils.AndroidFileUtils
 import cx.ring.utils.BitmapUtils
+import cx.ring.utils.ContentUri.getUri
 import cx.ring.views.AvatarDrawable
 import dagger.hilt.android.AndroidEntryPoint
-import io.reactivex.rxjava3.core.Single
 import net.jami.navigation.HomeNavigationPresenter
 import net.jami.navigation.HomeNavigationView
 import net.jami.navigation.HomeNavigationViewModel
+import net.jami.utils.VCardUtils
 
 @AndroidEntryPoint
 class TVProfileEditingFragment : JamiGuidedStepFragment<HomeNavigationPresenter, HomeNavigationView>(), HomeNavigationView {
@@ -49,8 +49,7 @@ class TVProfileEditingFragment : JamiGuidedStepFragment<HomeNavigationPresenter,
     private val pickProfilePicture =
         registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
             if (uri != null)
-                presenter.saveVCardPhoto(AndroidFileUtils.loadBitmap(requireContext(), uri)
-                    .map { BitmapUtils.bitmapToPhoto(it) })
+                setProfilePicture(uri)
         }
 
     private val requestCameraPermission =
@@ -68,12 +67,8 @@ class TVProfileEditingFragment : JamiGuidedStepFragment<HomeNavigationPresenter,
                     Log.e(TAG, "onActivityResult: Not able to get picture from extra")
                     return
                 }
-                val uri = extras[MediaStore.EXTRA_OUTPUT] as Uri?
-                if (uri != null) {
-                    val cr = requireContext().contentResolver
-                    presenter.saveVCardPhoto(Single.fromCallable {
-                        cr.openInputStream(uri).use { BitmapFactory.decodeStream(it) }
-                    }.map { obj: Bitmap -> BitmapUtils.bitmapToPhoto(obj) })
+                extras.getUri(MediaStore.EXTRA_OUTPUT)?.let {
+                    setProfilePicture(it)
                 }
             }
             else -> {
@@ -81,6 +76,23 @@ class TVProfileEditingFragment : JamiGuidedStepFragment<HomeNavigationPresenter,
         }
     }
 
+    private fun setProfilePicture(uri: Uri) {
+        val displayName = getCurrentDisplayname() ?: ""
+        AndroidFileUtils.getCacheFile(requireContext(), uri)
+            .subscribe({ file ->
+                val fileType = VCardUtils.pictureTypeFromMime(AndroidFileUtils.getMimeType(requireContext().contentResolver, uri))
+                presenter.updateProfile(displayName, file, fileType)
+            }) {
+                presenter.updateProfile(displayName)
+            }
+    }
+
+    private fun getCurrentDisplayname(): String? {
+        val usernameAction = actions.find { it.id == USER_NAME }
+        return usernameAction?.editTitle?.toString()
+    }
+
+
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         iconSize = resources.getDimension(R.dimen.tv_avatar_size).toInt()
@@ -104,7 +116,7 @@ class TVProfileEditingFragment : JamiGuidedStepFragment<HomeNavigationPresenter,
 
     override fun onGuidedActionEditedAndProceed(action: GuidedAction): Long {
         when (action.id) {
-            USER_NAME -> presenter.saveVCardFormattedName(action.editTitle.toString())
+            USER_NAME -> presenter.updateProfile(action.editTitle.toString())
             CAMERA -> presenter.cameraClicked()
             GALLERY -> presenter.galleryClicked()
         }
@@ -142,8 +154,6 @@ class TVProfileEditingFragment : JamiGuidedStepFragment<HomeNavigationPresenter,
         startActivityForResult(Intent(activity, CustomCameraActivity::class.java), ProfileCreationFragment.REQUEST_CODE_PHOTO)
     }
 
-
-
     override fun askCameraPermission() {
         requestCameraPermission.launch(Manifest.permission.CAMERA)
     }
diff --git a/jami-android/app/src/main/java/cx/ring/utils/BitmapUtils.kt b/jami-android/app/src/main/java/cx/ring/utils/BitmapUtils.kt
index 4b11860daff4687ed44dd5973a498b3abbd4838c..8639fe9acc4e6d9257952dd8b23e1fd72f967834 100644
--- a/jami-android/app/src/main/java/cx/ring/utils/BitmapUtils.kt
+++ b/jami-android/app/src/main/java/cx/ring/utils/BitmapUtils.kt
@@ -58,6 +58,19 @@ object BitmapUtils {
         null
     }
 
+    fun bitmapToBase64(bitmap: Bitmap?): String? {
+        if (bitmap == null) return null
+        return try {
+            val byteArrayOutputStream = ByteArrayOutputStream()
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
+            val byteArray = byteArrayOutputStream.toByteArray()
+            Base64.encodeToString(byteArray, Base64.NO_WRAP)
+        } catch (e: Exception) {
+            Log.e(TAG, "Error converting Bitmap to Base64", e)
+            null
+        }
+    }
+
     fun bytesToBitmap(imageData: ByteArray?): Bitmap? = if (imageData != null && imageData.isNotEmpty()) {
         BitmapFactory.decodeByteArray(imageData, 0, imageData.size)
     } else null
diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardPresenter.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardPresenter.kt
index 942a7018eb1c2405b103506b79c83869940b9735..2c253348e3269ec22d705e910e08824e8805145d 100644
--- a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardPresenter.kt
+++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardPresenter.kt
@@ -210,8 +210,8 @@ class AccountWizardPresenter @Inject constructor(
         }
             .firstOrError()
         if (saveProfile) {
-            newAccount = newAccount.flatMap { a: Account ->
-                view!!.saveProfile(a).map { a }
+            newAccount = newAccount.doOnSuccess { account ->
+                view!!.saveProfile(account)
             }
         }
         mCompositeDisposable.add(newAccount
@@ -238,6 +238,10 @@ class AccountWizardPresenter @Inject constructor(
             })
     }
 
+    fun updateProfile(accountId:String, displayName: String, avatar: String, fileType: String) {
+        mAccountService.updateProfile(accountId, displayName, avatar, fileType)
+    }
+
     fun errorDialogClosed() {
         view!!.goToHomeCreation()
     }
diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardView.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardView.kt
index cd06bc4d2ab834cd768f1ef9f2314170cfe563ee..7a5ce22d40a785d5c200a5ecbe2f6d68eaed8a5d 100644
--- a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardView.kt
+++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/AccountWizardView.kt
@@ -28,7 +28,7 @@ interface AccountWizardView {
     fun displayCreationError()
     fun blockOrientation()
     fun finish(affinity: Boolean)
-    fun saveProfile(account: Account): Single<VCard>
+    fun saveProfile(account: Account)
     fun displayGenericError()
     fun displayNetworkError()
     fun displayCannotBeFoundError()
diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiAccountSummaryPresenter.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiAccountSummaryPresenter.kt
index d4053e6df0433edf72b297ac079853e9637b6afe..c1e65e1152de62bbe616fc507ce0d19f77004ac0 100644
--- a/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiAccountSummaryPresenter.kt
+++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/account/JamiAccountSummaryPresenter.kt
@@ -16,13 +16,7 @@
  */
 package net.jami.account
 
-import ezvcard.VCard
-import ezvcard.property.Photo
-import ezvcard.property.RawProperty
-import ezvcard.property.Uid
 import io.reactivex.rxjava3.core.Scheduler
-import io.reactivex.rxjava3.core.Single
-import io.reactivex.rxjava3.schedulers.Schedulers
 import net.jami.model.Account
 import net.jami.mvp.RootPresenter
 import net.jami.services.AccountService
@@ -30,7 +24,6 @@ import net.jami.services.DeviceRuntimeService
 import net.jami.services.HardwareService
 import net.jami.services.VCardService
 import net.jami.utils.Log
-import net.jami.utils.VCardUtils
 import java.io.File
 import java.net.SocketException
 import javax.inject.Inject
@@ -40,7 +33,6 @@ class JamiAccountSummaryPresenter @Inject constructor(
     private val mAccountService: AccountService,
     private val mDeviceRuntimeService: DeviceRuntimeService,
     private val mHardwareService: HardwareService,
-    private val mVcardService: VCardService,
     @param:Named("UiScheduler") private val mUiScheduler: Scheduler
 ) : RootPresenter<JamiAccountSummaryView>() {
     private var mAccountID: String? = null
@@ -107,75 +99,12 @@ class JamiAccountSummaryPresenter @Inject constructor(
                 { view?.passwordChangeEnded(accountId, false) })
     }
 
-    fun saveVCardFormattedName(username: String?) {
+    fun updateProfile(displayName: String, avatar: String? = null, fileType: String = "") {
         val accountId = mAccountID ?: return
-        val account = mAccountService.getAccount(accountId)
-        val filesDir = mDeviceRuntimeService.provideFilesDir()
-        mCompositeDisposable.add(VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId)
-            .doOnSuccess { vcard: VCard ->
-                val previousName = vcard.formattedName?.value
-                if ((previousName.isNullOrEmpty() && username.isNullOrEmpty()) || previousName == username)
-                    throw IllegalArgumentException("Name didn't change")
-                vcard.setFormattedName(username)
-                vcard.removeProperties(RawProperty::class.java)
-                account?.loadedProfile = mVcardService.loadVCardProfile(vcard).cache()
-            }
-            .flatMap { vcard: VCard -> VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir) }
-            .subscribeOn(Schedulers.io())
-            .subscribe({}) { e: Throwable -> Log.e(TAG, "Error saving vCard " + e.message) })
-    }
-
-    /**
-     * Save the vCard to the disk.
-     * @param username: the username to save, if null, the username will be removed from the vCard
-     * @param photo: the photo to save, if null, the photo will be removed from the vCard
-     */
-    fun saveVCard(username: String?, photo: Single<Photo>?) {
-        val accountId = mAccountID ?: return
-        val account = mAccountService.getAccount(accountId)!!
-        val ringId = account.username
-        val filesDir = mDeviceRuntimeService.provideFilesDir()
-
-        if (photo == null) {
-            mCompositeDisposable.add(
-                VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId)
-                    .subscribeOn(Schedulers.io())
-                    .map { vcard: VCard ->
-                        vcard.uid = Uid(ringId)
-                        if (!username.isNullOrEmpty()) vcard.setFormattedName(username)
-                        vcard.removeProperties(Photo::class.java)
-                        vcard.removeProperties(RawProperty::class.java)
-                        vcard
-                    }
-                    .flatMap { vcard: VCard ->
-                        VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir)
-                    }
-                    .subscribe({ vcard: VCard ->
-                        account.loadedProfile = mVcardService.loadVCardProfile(vcard).cache()
-                    }) { e: Throwable -> Log.e(TAG, "Error saving vCard !", e) }
-            )
-        } else {
-            mCompositeDisposable.add(
-                Single.zip(
-                    VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId)
-                        .subscribeOn(Schedulers.io()),
-                    photo
-                ) { vcard: VCard, pic: Photo ->
-                    vcard.uid = Uid(ringId)
-                    if (!username.isNullOrEmpty()) vcard.setFormattedName(username)
-                    vcard.removeProperties(Photo::class.java)
-                    vcard.addPhoto(pic)
-                    vcard.removeProperties(RawProperty::class.java)
-                    vcard
-                }
-                    .flatMap { vcard: VCard ->
-                        VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir)
-                    }
-                    .subscribeOn(Schedulers.io())
-                    .subscribe({ vcard: VCard ->
-                        account.loadedProfile = mVcardService.loadVCardProfile(vcard).cache()
-                    }) { e: Throwable -> Log.e(TAG, "Error saving vCard !", e) })
-        }
+        if (avatar == null)
+            mAccountService.updateProfile(accountId, displayName)
+        else
+            mAccountService.updateProfile(accountId, displayName, avatar, fileType)
     }
 
     fun cameraClicked() {
diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/model/AccountCreationModel.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/model/AccountCreationModel.kt
index fd7058d2a8e6230166585aae79851cdb3a07efef..448e5158d17ad6ff9d2bc956979c665691b8112b 100644
--- a/jami-android/libjamiclient/src/main/kotlin/net/jami/model/AccountCreationModel.kt
+++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/model/AccountCreationModel.kt
@@ -23,7 +23,7 @@ import io.reactivex.rxjava3.subjects.BehaviorSubject
 import io.reactivex.rxjava3.subjects.Subject
 import java.io.File
 
-abstract class AccountCreationModel {
+class AccountCreationModel {
     var managementServer: String? = null
     var username = ""
     var password = ""
@@ -57,8 +57,6 @@ abstract class AccountCreationModel {
             field = pin.uppercase()
         }
 
-    abstract fun toVCard(): Single<VCard>
-
     val profileUpdates: Observable<AccountCreationModel>
         get() = profile
 }
diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/navigation/HomeNavigationPresenter.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/navigation/HomeNavigationPresenter.kt
index aee60c4f49d571f36d78bb3151f015d22b352807..d4fabd255bba21d082a2d788bddb53977ff0edc7 100644
--- a/jami-android/libjamiclient/src/main/kotlin/net/jami/navigation/HomeNavigationPresenter.kt
+++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/navigation/HomeNavigationPresenter.kt
@@ -16,21 +16,14 @@
  */
 package net.jami.navigation
 
-import ezvcard.VCard
-import ezvcard.property.Photo
-import ezvcard.property.RawProperty
-import ezvcard.property.Uid
 import io.reactivex.rxjava3.core.Scheduler
-import io.reactivex.rxjava3.core.Single
-import io.reactivex.rxjava3.schedulers.Schedulers
-import net.jami.model.Account
 import net.jami.mvp.RootPresenter
 import net.jami.services.AccountService
 import net.jami.services.DeviceRuntimeService
 import net.jami.services.HardwareService
 import net.jami.services.VCardService
 import net.jami.utils.Log
-import net.jami.utils.VCardUtils
+import java.io.File
 import javax.inject.Inject
 import javax.inject.Named
 
@@ -52,75 +45,6 @@ class HomeNavigationPresenter @Inject constructor(
             }) { e: Throwable -> Log.e(TAG, "Error loading account list !", e) })
     }
 
-    fun saveVCardPhoto(photo: Single<Photo>) {
-        val account = mAccountService.currentAccount!!
-        val accountId = account.accountId
-        val ringId = account.username
-        val filesDir = mDeviceRuntimeService.provideFilesDir()
-        mCompositeDisposable.add(Single.zip(
-            VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId).subscribeOn(Schedulers.io()),
-            photo.subscribeOn(Schedulers.io())
-        ) { vcard: VCard, pic: Photo ->
-            vcard.apply {
-                uid = Uid(ringId)
-                removeProperties(Photo::class.java)
-                addPhoto(pic)
-                removeProperties(RawProperty::class.java)
-            }
-        }
-            .subscribeOn(Schedulers.io())
-            .subscribe({ vcard ->
-                account.loadedProfile = mVCardService.loadVCardProfile(vcard).cache()
-                VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir)
-                    .subscribeOn(Schedulers.io())
-                    .subscribe()
-            }) { e: Throwable -> Log.e(TAG, "Error saving vCard !", e) })
-    }
-
-    fun saveVCardFormattedName(username: String?) {
-        val account = mAccountService.currentAccount!!
-        val accountId = account.accountId
-        val filesDir = mDeviceRuntimeService.provideFilesDir()
-        mCompositeDisposable.add(VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId)
-            .doOnSuccess { vcard: VCard ->
-                vcard.setFormattedName(username)
-                vcard.removeProperties(RawProperty::class.java)
-            }
-            .subscribeOn(Schedulers.io())
-            .subscribe({ vcard ->
-                account.loadedProfile = mVCardService.loadVCardProfile(vcard).cache()
-                VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir)
-                    .subscribeOn(Schedulers.io())
-                    .subscribe()
-            }) { e: Throwable -> Log.e(TAG, "Error saving vCard !", e) })
-    }
-
-    fun saveVCard(account: Account, username: String?, photo: Single<Photo>) {
-        val accountId = account.accountId
-        val ringId = account.username
-        val filesDir = mDeviceRuntimeService.provideFilesDir()
-        mCompositeDisposable.add(Single.zip(
-            VCardUtils.loadLocalProfileFromDiskWithDefault(filesDir, accountId).subscribeOn(Schedulers.io()),
-            photo
-        ) { vcard: VCard, pic: Photo ->
-            vcard.uid = Uid(ringId)
-            if (!username.isNullOrEmpty()) {
-                vcard.setFormattedName(username)
-            }
-            vcard.removeProperties(Photo::class.java)
-            vcard.addPhoto(pic)
-            vcard.removeProperties(RawProperty::class.java)
-            account.loadedProfile = mVCardService.loadVCardProfile(vcard).cache()
-            vcard
-        }
-            .flatMap { vcard -> VCardUtils.saveLocalProfileToDisk(vcard, accountId, filesDir) }
-            .subscribeOn(Schedulers.io())
-            .subscribe({}) { e: Throwable -> Log.e(TAG, "Error saving vCard !", e) })
-    }
-
-    fun getUri(account: Account, defaultNameSip: CharSequence): String? =
-        if (account.isIP2IP) defaultNameSip.toString() else account.displayUri
-
     fun cameraClicked() {
         if (mDeviceRuntimeService.hasVideoPermission())
             view?.gotToImageCapture()
@@ -132,6 +56,18 @@ class HomeNavigationPresenter @Inject constructor(
         view?.goToGallery()
     }
 
+    fun updateProfile(displayName: String, avatar: String? = null, fileType: String = "") {
+        val account = mAccountService.currentAccount ?: return
+        if (avatar == null)
+            mAccountService.updateProfile(account.accountId, displayName, "", "")
+        else
+            mAccountService.updateProfile(account.accountId, displayName, avatar, fileType)
+    }
+    fun updateProfile(displayName: String, avatar: File, fileType: String) {
+        val account = mAccountService.currentAccount ?: return
+        mAccountService.updateProfile(account.accountId, displayName, avatar, fileType)
+    }
+
     fun cameraPermissionChanged(isGranted: Boolean) {
         if (isGranted && mHardwareService.isVideoAvailable) {
             mHardwareService.initVideo()
diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/services/AccountService.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/services/AccountService.kt
index 704892793dca330290ff8c9dd30d68e61832efb0..a93648410247db8bb0b4e7a7ba69408f13592e23 100644
--- a/jami-android/libjamiclient/src/main/kotlin/net/jami/services/AccountService.kt
+++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/services/AccountService.kt
@@ -610,6 +610,16 @@ class AccountService(
         mHistoryService.clearHistory(accountId).subscribe()
     }
 
+    fun updateProfile(accountId: String, displayName: String, avatarBase64: String, fileType: String) {
+        JamiService.updateProfile(accountId, displayName, avatarBase64, fileType, if (avatarBase64.isEmpty()) 2 else 1)
+    }
+    fun updateProfile(accountId: String, displayName: String, avatarPath: File, fileType: String) {
+        JamiService.updateProfile(accountId, displayName, avatarPath.absolutePath, fileType, 0)
+    }
+    fun updateProfile(accountId: String, displayName: String) {
+        JamiService.updateProfile(accountId, displayName, "", "", 0)
+    }
+
     /**
      * Exports the account on the DHT (used for multi-devices feature)
      */
diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/utils/VCardUtils.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/utils/VCardUtils.kt
index e6c7f8a56f4d7cf52d3b4a2210ddfdc321794e54..659cfe50538ab7bd991d2ba341d190ffac55b8f1 100644
--- a/jami-android/libjamiclient/src/main/kotlin/net/jami/utils/VCardUtils.kt
+++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/utils/VCardUtils.kt
@@ -37,10 +37,7 @@ import java.util.HashMap
 
 object VCardUtils {
     val TAG = VCardUtils::class.simpleName!!
-    const val MIME_PROFILE_VCARD = "x-ring/ring.profile.vcard"
     const val VCARD_KEY_MIME_TYPE = "mimeType"
-    const val VCARD_KEY_PART = "part"
-    const val VCARD_KEY_OF = "of"
     const val LOCAL_USER_VCARD_NAME = "profile.vcf"
     private const val VCARD_MAX_SIZE = 1024L * 1024L * 8
 
@@ -131,6 +128,14 @@ object VCardUtils {
         }
     }
 
+    fun pictureTypeFromMime(mimeType: String?): String =
+        when (mimeType?.lowercase()) {
+            null -> ""
+            "image/jpeg" -> "JPEG"
+            "image/png" -> "PNG"
+            else -> "JPEG"
+        }
+
     @Throws(IOException::class)
     fun loadPeerProfileFromDisk(filesDir: File, cacheDir: File, filename: String, accountId: String): Pair<String?, ByteArray?> {
         val cacheFolder = peerProfileCachePath(cacheDir, accountId)