Index: asterisk-22.8.2/res/res_pjsip/location.c
===================================================================
--- asterisk-22.8.2.orig/res/res_pjsip/location.c
+++ asterisk-22.8.2/res/res_pjsip/location.c
@@ -355,7 +355,7 @@ struct ast_sip_contact *ast_sip_location
 struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor,
 	const char *uri, struct timeval expiration_time, const char *path_info,
 	const char *user_agent, const char *via_addr, int via_port, const char *call_id,
-	int prune_on_boot, struct ast_sip_endpoint *endpoint)
+	int prune_on_boot, struct ast_sip_endpoint *endpoint, const char* mobility)
 {
 	struct ast_sip_contact *contact;
 	char name[MAX_OBJECT_FIELD * 2 + 3];
@@ -400,6 +400,10 @@ struct ast_sip_contact *ast_sip_location
 		ast_string_field_set(contact, call_id, call_id);
 	}
 
+	if (!ast_strlen_zero(mobility)) {
+		ast_string_field_set(contact, mobility, mobility);
+	}
+
 	contact->endpoint = ao2_bump(endpoint);
 	if (endpoint) {
 		ast_string_field_set(contact, endpoint_name, ast_sorcery_object_get_id(endpoint));
@@ -420,9 +424,8 @@ int ast_sip_location_add_contact_nolock(
 		struct ast_sip_endpoint *endpoint)
 {
 	struct ast_sip_contact *contact;
-
 	contact = ast_sip_location_create_contact(aor, uri, expiration_time, path_info,
-		user_agent, via_addr, via_port, call_id, 0, endpoint);
+		user_agent, via_addr, via_port, call_id, 0, endpoint, NULL);
 	ao2_cleanup(contact);
 	return contact ? 0 : -1;
 }
@@ -1410,6 +1413,7 @@ int ast_sip_initialize_sorcery_location(
 	ast_sorcery_object_field_register(sorcery, "contact", "via_port", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact, via_port));
 	ast_sorcery_object_field_register(sorcery, "contact", "call_id", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, call_id));
 	ast_sorcery_object_field_register(sorcery, "contact", "prune_on_boot", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, prune_on_boot));
+	ast_sorcery_object_field_register(sorcery, "contact", "mobility", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, mobility));
 
 	ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration));
Index: asterisk-22.8.2/res/res_pjsip_registrar.c
===================================================================
--- asterisk-22.8.2.orig/res/res_pjsip_registrar.c
+++ asterisk-22.8.2/res/res_pjsip_registrar.c
@@ -625,6 +625,35 @@ static int vec_contact_add(void *obj, vo
 	return 0;
 }
 
+static int vec_contact_add_mobile(void *obj, void *arg, int flags)
+{
+	struct ast_sip_contact *contact = obj;
+	struct excess_contact_vector *contact_vec = arg;
+
+	/* We only care about mobile contacts here */
+	if (strcmp(contact->mobility, "mobile")) {
+		return 0;
+	}
+
+	/*
+	 * Performance wise, an insertion sort is fine because we
+	 * shouldn't need to remove more than a handful of contacts.
+	 * I expect we'll typically be removing only one contact.
+	 */
+	AST_VECTOR_ADD_SORTED(contact_vec, contact, vec_contact_cmp);
+	if (AST_VECTOR_SIZE(contact_vec) == AST_VECTOR_MAX_SIZE(contact_vec)) {
+		/*
+		 * We added a contact over the number we need to remove.
+		 * Remove the longest to expire contact from the vector
+		 * which is the last element in the vector.  It may be
+		 * the one we just added or the one we just added pushed
+		 * out an earlier contact from removal consideration.
+		 */
+		--AST_VECTOR_SIZE(contact_vec);
+	}
+	return 0;
+}
+
 /*!
  * \internal
  * \brief Remove excess existing contacts that are unavailable or expire soonest.
@@ -677,6 +706,51 @@ static void remove_excess_contacts(struc
 	AST_VECTOR_FREE(&contact_vec);
 }
 
+/*!
+ * \internal
+ * \brief Remove existing mobile contacts.
+ * \since Wazo 22.08
+ *
+ * \param contacts Container of unmodified contacts that could remove.
+ */
+static void remove_remaining_mobile_contacts(struct ao2_container *contacts, struct ao2_container *response_contacts)
+{
+	struct excess_contact_vector contact_vec;
+	int to_remove = 1; /* There will always be a maximum of 1 mobile contact */
+
+	/*
+	 * Create a sorted vector to hold the to_remove soonest to
+	 * expire contacts.  The vector has an extra space to
+	 * temporarily hold the longest to expire contact that we
+	 * won't remove.
+	 */
+	if (AST_VECTOR_INIT(&contact_vec, to_remove + 1)) {
+		return;
+	}
+	ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, vec_contact_add_mobile, &contact_vec);
+
+	/*
+	 * The vector should always be populated with the number
+	 * of contacts we need to remove.  Just in case, we will
+	 * remove all contacts in the vector even if the contacts
+	 * container had fewer contacts than there should be.
+	 */
+	ast_assert(AST_VECTOR_SIZE(&contact_vec) == to_remove);
+	to_remove = AST_VECTOR_SIZE(&contact_vec);
+
+	/* Remove the excess contacts that are unavailable or expire the soonest */
+	while (to_remove--) {
+		struct ast_sip_contact *contact;
+
+		contact = AST_VECTOR_GET(&contact_vec, to_remove);
+		registrar_contact_delete(CONTACT_DELETE_EXISTING, NULL, contact, contact->aor);
+
+		ao2_unlink(response_contacts, contact);
+	}
+
+	AST_VECTOR_FREE(&contact_vec);
+}
+
 /*! \brief Callback function which adds non-permanent contacts to a container */
 static int registrar_add_non_permanent(void *obj, void *arg, int flags)
 {
@@ -736,6 +810,7 @@ static void register_aor_core(pjsip_rx_d
 	int deleted = 0;
 	int permanent = 0;
 	int contact_count;
+	int mobile = 0;
 	struct ao2_container *existing_contacts = NULL;
 	struct ao2_container *unavail_contacts = NULL;
 	pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
@@ -915,6 +990,9 @@ static void register_aor_core(pjsip_rx_d
 
 		if (!contact) {
 			int prune_on_boot;
+			char* mobility = NULL;
+			pj_str_t mobility_str = pj_str("mobility");
+			pjsip_param* mobility_param = NULL;
 
 			/* If they are actually trying to delete a contact that does not exist... be forgiving */
 			if (!expiration) {
@@ -923,12 +1001,20 @@ static void register_aor_core(pjsip_rx_d
 				continue;
 			}
 
+			mobility_param = pjsip_param_find(&contact_hdr->other_param, &mobility_str);
+			if (mobility_param) {
+				ast_copy_pj_str2(&mobility, &mobility_param->value);
+				mobile = strcmp(mobility, "mobile") ? 0 : 1;
+			}
+
 			prune_on_boot = !ast_sip_will_uri_survive_restart(details.uri, endpoint, rdata);
 
 			contact = ast_sip_location_create_contact(aor, contact_uri,
 				ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)),
 				path_str ? ast_str_buffer(path_str) : NULL,
-				user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint);
+				user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint, mobility);
+			ast_free(mobility);
+
 			if (!contact) {
 				ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
 					contact_uri, aor_name);
@@ -1041,9 +1127,16 @@ static void register_aor_core(pjsip_rx_d
 			remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts,
 				aor->remove_existing);
 		}
+		/*
+		* If the new contact is mobile then any existing mobile contact should be removed
+		*/
+		if (mobile) {
+			remove_remaining_mobile_contacts(existing_contacts, contacts);
+		}
 		ao2_ref(existing_contacts, -1);
 	}
 
+
 	response_contact = ao2_callback(contacts, 0, NULL, NULL);
 
 	/* Send a response containing all of the contacts (including static) that are present on this AOR */
Index: asterisk-22.8.2/include/asterisk/res_pjsip.h
===================================================================
--- asterisk-22.8.2.orig/include/asterisk/res_pjsip.h
+++ asterisk-22.8.2/include/asterisk/res_pjsip.h
@@ -409,6 +409,8 @@ struct ast_sip_contact {
 		AST_STRING_FIELD(call_id);
 		/*! The name of the endpoint that added the contact */
 		AST_STRING_FIELD(endpoint_name);
+		/*! The SIP mobility feature */
+		AST_STRING_FIELD(mobility);
 	);
 	/*! Absolute time that this contact is no longer valid after */
 	struct timeval expiration_time;
@@ -1849,7 +1851,7 @@ int ast_sip_location_add_contact_nolock(
 struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor,
 	const char *uri, struct timeval expiration_time, const char *path_info,
 	const char *user_agent, const char *via_addr, int via_port, const char *call_id,
-	int prune_on_boot, struct ast_sip_endpoint *endpoint);
+	int prune_on_boot, struct ast_sip_endpoint *endpoint, const char* mobility);
 
 /*!
  * \brief Update a contact
Index: asterisk-22.8.2/res/res_pjsip/pjsip_config.xml
===================================================================
--- asterisk-22.8.2.orig/res/res_pjsip/pjsip_config.xml
+++ asterisk-22.8.2/res/res_pjsip/pjsip_config.xml
@@ -2735,6 +2735,12 @@
 						on a reliable transport and is not intended to be configured manually.
 					</para></description>
 				</configOption>
+				<configOption name="mobility">
+					<synopsis>If a contact is 'mobile' or 'fixed'.</synopsis>
+					<description><para>
+						Can take values 'mobile' or 'fixed' depending on the type of contact.
+					</para></description>
+				</configOption>
 			</configObject>
 			<configObject name="aor">
 				<since>
