diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java
index e2732145a8420d6b3265c2388d65fb256a8c1279..0a04c7138b6566686c2d30f8c20157b99e7caa7a 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java
@@ -96,6 +96,12 @@ public interface ContactManager {
 	void setContactActive(Transaction txn, ContactId c, boolean active)
 			throws DbException;
 
+	/**
+	 * Sets an alias name for the contact or unsets it if alias is null.
+	 */
+	void setContactAlias(Transaction txn, ContactId c, @Nullable String alias)
+			throws DbException;
+
 	/**
 	 * Sets an alias name for the contact or unsets it if alias is null.
 	 */
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorInfo.java b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorInfo.java
index 8c90d9aff05e6003c2a30b87d0a1bac37c6ab6f8..26f4f33988f50da126b1fdcd980dd9202229f338 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorInfo.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorInfo.java
@@ -10,7 +10,11 @@ import javax.annotation.concurrent.Immutable;
 public class AuthorInfo {
 
 	public enum Status {
-		NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
+		NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES;
+
+		public boolean isContact() {
+			return this == UNVERIFIED || this == VERIFIED;
+		}
 	}
 
 	private final Status status;
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java
index 71a63c9b57f32dc3ba47c6e90b8c3da8e1b62b03..f7e03c5c4112f0ce2692ab052bcd50258793fae0 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java
@@ -163,14 +163,20 @@ class ContactManagerImpl implements ContactManager {
 	}
 
 	@Override
-	public void setContactAlias(ContactId c, @Nullable String alias)
-			throws DbException {
+	public void setContactAlias(Transaction txn, ContactId c,
+			@Nullable String alias) throws DbException {
 		if (alias != null) {
 			int aliasLength = toUtf8(alias).length;
 			if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
 				throw new IllegalArgumentException();
 		}
-		db.transaction(false, txn -> db.setContactAlias(txn, c, alias));
+		db.setContactAlias(txn, c, alias);
+	}
+
+	@Override
+	public void setContactAlias(ContactId c, @Nullable String alias)
+			throws DbException {
+		db.transaction(false, txn -> setContactAlias(txn, c, alias));
 	}
 
 	@Override
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java
index 1dea4f9df9850cf0b34faed098dfd2cab908abaa..24ecd71c59bc48b21d0f4f1f8bd30ac358ad176d 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java
@@ -204,7 +204,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 
 	@Test(expected = IllegalArgumentException.class)
 	public void testSetContactAliasTooLong() throws Exception {
-		contactManager.setContactAlias(contactId,
+		Transaction txn = new Transaction(null, false);
+		contactManager.setContactAlias(txn, contactId,
 				getRandomString(MAX_AUTHOR_NAME_LENGTH + 1));
 	}
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java
index 92c75f187314745c2c798583a6da9cd9e9d8e4e8..70ae123b3e395c76720146db7505674835772f57 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java
@@ -14,7 +14,6 @@ import android.view.ViewGroup;
 import android.widget.ImageButton;
 import android.widget.TextView;
 
-import org.briarproject.bramble.api.identity.Author;
 import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.briar.R;
 import org.briarproject.briar.android.view.AuthorView;
@@ -98,9 +97,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
 
 		// author and date
 		BlogPostHeader post = item.getPostHeader();
-		Author a = post.getAuthor();
-		author.setAuthor(a);
-		author.setAuthorInfo(post.getAuthorInfo());
+		author.setAuthor(post.getAuthor(), post.getAuthorInfo());
 		author.setDate(post.getTimestamp());
 		author.setPersona(
 				item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
@@ -143,8 +140,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
 
 	private void onBindComment(BlogCommentItem item) {
 		// reblogger
-		reblogger.setAuthor(item.getAuthor());
-		reblogger.setAuthorInfo(item.getAuthorInfo());
+		reblogger.setAuthor(item.getAuthor(), item.getAuthorInfo());
 		reblogger.setDate(item.getTimestamp());
 		if (!fullText) {
 			reblogger.setAuthorClickable(v -> listener.onAuthorClick(item));
@@ -165,8 +161,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
 			AuthorView author = v.findViewById(R.id.authorView);
 			TextView text = v.findViewById(R.id.textView);
 
-			author.setAuthor(c.getAuthor());
-			author.setAuthorInfo(c.getAuthorInfo());
+			author.setAuthor(c.getAuthor(), c.getAuthorInfo());
 			author.setDate(c.getTimestamp());
 			// TODO make author clickable #624
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/BaseContactListAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/BaseContactListAdapter.java
index 1f577c366936ed72507ff598c44bcc0f3dccfd37..5e58597ce98965e836835931c9bec4c87d3a482a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/BaseContactListAdapter.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/BaseContactListAdapter.java
@@ -1,6 +1,7 @@
 package org.briarproject.briar.android.contact;
 
 import android.content.Context;
+import android.support.annotation.NonNull;
 import android.view.View;
 
 import org.briarproject.bramble.api.contact.ContactId;
@@ -9,6 +10,7 @@ import org.briarproject.briar.android.util.BriarAdapter;
 import javax.annotation.Nullable;
 
 import static android.support.v7.util.SortedList.INVALID_POSITION;
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
 
 public abstract class BaseContactListAdapter<I extends ContactItem, VH extends ContactItemViewHolder<I>>
 		extends BriarAdapter<I, VH> {
@@ -23,15 +25,15 @@ public abstract class BaseContactListAdapter<I extends ContactItem, VH extends C
 	}
 
 	@Override
-	public void onBindViewHolder(VH ui, int position) {
+	public void onBindViewHolder(@NonNull VH ui, int position) {
 		I item = items.get(position);
 		ui.bind(item, listener);
 	}
 
 	@Override
 	public int compare(I c1, I c2) {
-		return c1.getContact().getAuthor().getName()
-				.compareTo(c2.getContact().getAuthor().getName());
+		return getContactDisplayName(c1.getContact())
+				.compareTo(getContactDisplayName(c2.getContact()));
 	}
 
 	@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java
index 6bc4f3d493e0b49159859c6289b35599b6d26a11..403994d595e047765737c52bd8b1c6e457c7f689 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java
@@ -16,6 +16,8 @@ import javax.annotation.Nullable;
 
 import im.delight.android.identicons.IdenticonDrawable;
 
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
+
 @UiThread
 @NotNullByDefault
 public class ContactItemViewHolder<I extends ContactItem>
@@ -41,8 +43,7 @@ public class ContactItemViewHolder<I extends ContactItem>
 		Author author = item.getContact().getAuthor();
 		avatar.setImageDrawable(
 				new IdenticonDrawable(author.getId().getBytes()));
-		String contactName = author.getName();
-		name.setText(contactName);
+		name.setText(getContactDisplayName(item.getContact()));
 
 		if (bulb != null) {
 			// online/offline
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ConversationVisitor.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ConversationVisitor.java
index 3c33b27c0ccb7cc64601296551d6c97fda0c80f4..717fee971ec221a71302bbb05e80ebefca793175 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ConversationVisitor.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ConversationVisitor.java
@@ -24,6 +24,7 @@ import static org.briarproject.briar.android.contact.ConversationRequestItem.Req
 import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.FORUM;
 import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.GROUP;
 import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.INTRODUCTION;
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
 
 @UiThread
 @NotNullByDefault
@@ -188,33 +189,36 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
 
 	@Override
 	public ConversationItem visitIntroductionRequest(IntroductionRequest r) {
+		String name = getContactDisplayName(r.getNameable(), r.getAlias());
 		if (r.isLocal()) {
 			String text = ctx.getString(R.string.introduction_request_sent,
-					contactName.getValue(), r.getName());
+					contactName.getValue(), name);
 			return new ConversationNoticeOutItem(text, r);
 		} else {
 			String text = ctx.getString(R.string.introduction_request_received,
-					contactName.getValue(), r.getName());
+					contactName.getValue(), name);
 			return new ConversationRequestItem(text, INTRODUCTION, r);
 		}
 	}
 
 	@Override
 	public ConversationItem visitIntroductionResponse(IntroductionResponse r) {
+		String introducedAuthor =
+				getContactDisplayName(r.getIntroducedAuthor(),
+						r.getIntroducedAuthorInfo().getAlias());
 		if (r.isLocal()) {
 			String text;
 			if (r.wasAccepted()) {
-				String introducee = r.getIntroducedAuthor().getName();
 				text = ctx.getString(
 						R.string.introduction_response_accepted_sent,
-						introducee)
+						introducedAuthor)
 						+ "\n\n" + ctx.getString(
 						R.string.introduction_response_accepted_sent_info,
-						introducee);
+						introducedAuthor);
 			} else {
 				text = ctx.getString(
 						R.string.introduction_response_declined_sent,
-						r.getIntroducedAuthor().getName());
+						introducedAuthor);
 			}
 			return new ConversationNoticeOutItem(text, r);
 		} else {
@@ -223,17 +227,17 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
 				text = ctx.getString(
 						R.string.introduction_response_accepted_received,
 						contactName.getValue(),
-						r.getIntroducedAuthor().getName());
+						introducedAuthor);
 			} else if (r.isIntroducer()) {
 				text = ctx.getString(
 						R.string.introduction_response_declined_received,
 						contactName.getValue(),
-						r.getIntroducedAuthor().getName());
+						introducedAuthor);
 			} else {
 				text = ctx.getString(
 						R.string.introduction_response_declined_received_by_introducee,
 						contactName.getValue(),
-						r.getIntroducedAuthor().getName());
+						introducedAuthor);
 			}
 			return new ConversationNoticeInItem(text, r);
 		}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java
index 194641f075450af33d1f53022bfc0812671f3deb..ea7858e4874328a412f9f0c02cd301fbf411af34 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java
@@ -38,6 +38,7 @@ import static android.view.View.VISIBLE;
 import static android.widget.Toast.LENGTH_SHORT;
 import static java.util.logging.Level.WARNING;
 import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
 import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
 
 public class IntroductionMessageFragment extends BaseFragment
@@ -148,8 +149,8 @@ public class IntroductionMessageFragment extends BaseFragment
 					c2.getAuthor().getId().getBytes()));
 
 			// set contact names
-			ui.contactName1.setText(c1.getAuthor().getName());
-			ui.contactName2.setText(c2.getAuthor().getName());
+			ui.contactName1.setText(getContactDisplayName(c1));
+			ui.contactName2.setText(getContactDisplayName(c2));
 
 			// hide progress bar
 			ui.progressBar.setVisibility(GONE);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/JoinMessageItemViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/JoinMessageItemViewHolder.java
index 09f50711e0ba16b757f5cbcaf142089c4f6d1244..5b1274fd77f42636a8871128391a96cb4302e02f 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/JoinMessageItemViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/JoinMessageItemViewHolder.java
@@ -36,24 +36,25 @@ class JoinMessageItemViewHolder
 		if (item.isInitial()) {
 			textView.setText(R.string.groups_member_created_you);
 		} else {
-			textView.setText(
-					getContext().getString(R.string.groups_member_joined,
-							item.getAuthor().getName()));
+			String name = item.getAuthorName();
+			textView.setText(getContext()
+					.getString(R.string.groups_member_joined, name));
 		}
 	}
 
 	private void bind(JoinMessageItem item) {
 		Context ctx = getContext();
+		String name = item.getAuthorName();
 
 		if (item.isInitial()) {
-			textView.setText(ctx.getString(R.string.groups_member_created,
-					item.getAuthor().getName()));
+			textView.setText(
+					ctx.getString(R.string.groups_member_created, name));
 		} else {
 			if (item.getAuthorInfo().getStatus() == OURSELVES) {
 				textView.setText(R.string.groups_member_joined_you);
 			} else {
-				textView.setText(ctx.getString(R.string.groups_member_joined,
-						item.getAuthor().getName()));
+				textView.setText(
+						ctx.getString(R.string.groups_member_joined, name));
 			}
 		}
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/invitation/GroupInvitationViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/invitation/GroupInvitationViewHolder.java
index fae57b85e4a3dc4aa069fcdc7f7b1699daa285e2..b205e2409e2c9311249e08ce40185ee53a11411b 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/invitation/GroupInvitationViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/invitation/GroupInvitationViewHolder.java
@@ -9,6 +9,8 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationItem;
 
 import javax.annotation.Nullable;
 
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
+
 class GroupInvitationViewHolder
 		extends InvitationViewHolder<GroupInvitationItem> {
 
@@ -24,7 +26,7 @@ class GroupInvitationViewHolder
 
 		sharedBy.setText(
 				sharedBy.getContext().getString(R.string.groups_created_by,
-						item.getCreator().getAuthor().getName()));
+						getContactDisplayName(item.getCreator())));
 	}
 
 }
\ No newline at end of file
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupItem.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupItem.java
index 03a9d40b07a345396dfdc3ef615c1a9e1f629d1e..e4a69c3660216e22ddbc8e0b6c24eac227dfb63d 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupItem.java
@@ -1,6 +1,7 @@
 package org.briarproject.briar.android.privategroup.list;
 
 import org.briarproject.bramble.api.identity.Author;
+import org.briarproject.bramble.api.identity.AuthorInfo;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.briar.api.client.MessageTracker.GroupCount;
@@ -12,12 +13,15 @@ import org.briarproject.briar.api.privategroup.PrivateGroup;
 class GroupItem {
 
 	private final PrivateGroup privateGroup;
+	private final AuthorInfo authorInfo;
 	private int messageCount, unreadCount;
 	private long timestamp;
 	private boolean dissolved;
 
-	GroupItem(PrivateGroup privateGroup, GroupCount count, boolean dissolved) {
+	GroupItem(PrivateGroup privateGroup, AuthorInfo authorInfo,
+			GroupCount count, boolean dissolved) {
 		this.privateGroup = privateGroup;
+		this.authorInfo = authorInfo;
 		this.messageCount = count.getMsgCount();
 		this.unreadCount = count.getUnreadCount();
 		this.timestamp = count.getLatestMsgTime();
@@ -46,6 +50,10 @@ class GroupItem {
 		return privateGroup.getCreator();
 	}
 
+	AuthorInfo getCreatorInfo() {
+		return authorInfo;
+	}
+
 	String getName() {
 		return privateGroup.getName();
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListControllerImpl.java
index b568fdcef5a431655e814ec82d9ccb1ac13376f4..e60f6ebd3e682fa53f5ff0f76356ed8a44be1d2b 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListControllerImpl.java
@@ -2,12 +2,15 @@ package org.briarproject.briar.android.privategroup.list;
 
 import android.support.annotation.CallSuper;
 
+import org.briarproject.bramble.api.contact.ContactManager;
 import org.briarproject.bramble.api.db.DatabaseExecutor;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.NoSuchGroupException;
 import org.briarproject.bramble.api.event.Event;
 import org.briarproject.bramble.api.event.EventBus;
 import org.briarproject.bramble.api.event.EventListener;
+import org.briarproject.bramble.api.identity.AuthorId;
+import org.briarproject.bramble.api.identity.AuthorInfo;
 import org.briarproject.bramble.api.lifecycle.LifecycleManager;
 import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
 import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -30,7 +33,9 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.logging.Logger;
 
@@ -52,6 +57,7 @@ class GroupListControllerImpl extends DbControllerImpl
 
 	private final PrivateGroupManager groupManager;
 	private final GroupInvitationManager groupInvitationManager;
+	private final ContactManager contactManager;
 	private final AndroidNotificationManager notificationManager;
 	private final EventBus eventBus;
 
@@ -61,10 +67,12 @@ class GroupListControllerImpl extends DbControllerImpl
 	GroupListControllerImpl(@DatabaseExecutor Executor dbExecutor,
 			LifecycleManager lifecycleManager, PrivateGroupManager groupManager,
 			GroupInvitationManager groupInvitationManager,
+			ContactManager contactManager,
 			AndroidNotificationManager notificationManager, EventBus eventBus) {
 		super(dbExecutor, lifecycleManager);
 		this.groupManager = groupManager;
 		this.groupInvitationManager = groupInvitationManager;
+		this.contactManager = contactManager;
 		this.notificationManager = notificationManager;
 		this.eventBus = eventBus;
 	}
@@ -153,12 +161,22 @@ class GroupListControllerImpl extends DbControllerImpl
 				Collection<PrivateGroup> groups =
 						groupManager.getPrivateGroups();
 				List<GroupItem> items = new ArrayList<>(groups.size());
+				Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
 				for (PrivateGroup g : groups) {
 					try {
 						GroupId id = g.getId();
+						AuthorId authorId = g.getCreator().getId();
+						AuthorInfo authorInfo;
+						if (authorInfos.containsKey(authorId)) {
+							authorInfo = authorInfos.get(authorId);
+						} else {
+							authorInfo = contactManager.getAuthorInfo(authorId);
+							authorInfos.put(authorId, authorInfo);
+						}
 						GroupCount count = groupManager.getGroupCount(id);
 						boolean dissolved = groupManager.isDissolved(id);
-						items.add(new GroupItem(g, count, dissolved));
+						items.add(
+								new GroupItem(g, authorInfo, count, dissolved));
 					} catch (NoSuchGroupException e) {
 						// Continue
 					}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupViewHolder.java
index b64eb25a29ac555c63f1ad4fe27139eec899c218..ce63ad29feef1f9ff95384b1224146bb8c737085 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupViewHolder.java
@@ -20,6 +20,7 @@ import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
 import static org.briarproject.briar.android.activity.BriarActivity.GROUP_NAME;
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -60,8 +61,9 @@ class GroupViewHolder extends RecyclerView.ViewHolder {
 		name.setText(group.getName());
 
 		// Creator
-		creator.setText(ctx.getString(R.string.groups_created_by,
-				group.getCreator().getName()));
+		String creatorName = getContactDisplayName(group.getCreator(),
+				group.getCreatorInfo().getAlias());
+		creator.setText(ctx.getString(R.string.groups_created_by, creatorName));
 
 		if (!group.isDissolved()) {
 			// full visibility
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListAdapter.java
index ceab47514612354a8f1d0852716ca626509f89b7..2b1634c4f28e1a7bb5602b32af523efddd6a575c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListAdapter.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListAdapter.java
@@ -1,6 +1,7 @@
 package org.briarproject.briar.android.privategroup.memberlist;
 
 import android.content.Context;
+import android.support.annotation.NonNull;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -8,6 +9,8 @@ import android.view.ViewGroup;
 import org.briarproject.briar.R;
 import org.briarproject.briar.android.util.BriarAdapter;
 
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
+
 class MemberListAdapter extends
 		BriarAdapter<MemberListItem, MemberListItemHolder> {
 
@@ -15,8 +18,9 @@ class MemberListAdapter extends
 		super(context, MemberListItem.class);
 	}
 
+	@NonNull
 	@Override
-	public MemberListItemHolder onCreateViewHolder(ViewGroup viewGroup,
+	public MemberListItemHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
 			int i) {
 		View v = LayoutInflater.from(ctx).inflate(
 				R.layout.list_item_group_member, viewGroup, false);
@@ -24,13 +28,18 @@ class MemberListAdapter extends
 	}
 
 	@Override
-	public void onBindViewHolder(MemberListItemHolder ui, int position) {
+	public void onBindViewHolder(@NonNull MemberListItemHolder ui,
+			int position) {
 		ui.bind(items.get(position));
 	}
 
 	@Override
 	public int compare(MemberListItem m1, MemberListItem m2) {
-		return m1.getMember().getName().compareTo(m2.getMember().getName());
+		String n1 = getContactDisplayName(m1.getMember(),
+				m1.getAuthorInfo().getAlias());
+		String n2 = getContactDisplayName(m2.getMember(),
+				m2.getAuthorInfo().getAlias());
+		return n1.compareTo(n2);
 	}
 
 	@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java
index accaced350ef942df4167b48ecb8106518d33df3..ce9eb7fb9fe3b299bb2c2bfa87c46419f82989a4 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java
@@ -13,6 +13,7 @@ import org.briarproject.briar.android.view.AuthorView;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
 
 @UiThread
 @NotNullByDefault
@@ -31,8 +32,7 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
 
 	protected void bind(MemberListItem item) {
 		// member name, avatar and author info
-		author.setAuthor(item.getMember());
-		author.setAuthorInfo(item.getAuthorInfo());
+		author.setAuthor(item.getMember(), item.getAuthorInfo());
 
 		// online status of visible contacts
 		if (item.getContactId() != null) {
@@ -52,9 +52,10 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
 			if (item.getStatus() == OURSELVES) {
 				creator.setText(R.string.groups_member_created_you);
 			} else {
+				String name = getContactDisplayName(item.getMember(),
+						item.getAuthorInfo().getAlias());
 				creator.setText(creator.getContext()
-						.getString(R.string.groups_member_created,
-								item.getMember().getName()));
+						.getString(R.string.groups_member_created, name));
 			}
 		} else {
 			creator.setVisibility(GONE);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealableContactViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealableContactViewHolder.java
index ab7777f4bdffc38789b722d627c35f4f9e40d563..f32c34522dd28631db1b6b8db0adfcbc5ee70f3c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealableContactViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealableContactViewHolder.java
@@ -14,6 +14,7 @@ import javax.annotation.Nullable;
 import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityIcon;
 import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityString;
 import static org.briarproject.briar.android.util.UiUtils.GREY_OUT;
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
 
 @UiThread
 @NotNullByDefault
@@ -36,7 +37,7 @@ class RevealableContactViewHolder
 		icon.setImageResource(getVisibilityIcon(item.getVisibility()));
 		info.setText(
 				getVisibilityString(info.getContext(), item.getVisibility(),
-						item.getContact().getAuthor().getName()));
+						getContactDisplayName(item.getContact())));
 	}
 
 	@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingInvitationViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingInvitationViewHolder.java
index 6d4eae929244fafcb5e8662d9ecd6a0d5d1d36e8..9b65e1c8c91015bba3cad265f6537105b4fe7bc6 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingInvitationViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingInvitationViewHolder.java
@@ -13,6 +13,8 @@ import java.util.Collection;
 
 import javax.annotation.Nullable;
 
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
+
 class SharingInvitationViewHolder
 		extends InvitationViewHolder<SharingInvitationItem> {
 
@@ -28,7 +30,7 @@ class SharingInvitationViewHolder
 
 		Collection<String> names = new ArrayList<>();
 		for (Contact c : item.getNewSharers())
-			names.add(c.getAuthor().getName());
+			names.add(getContactDisplayName(c));
 		sharedBy.setText(
 				sharedBy.getContext().getString(R.string.shared_by_format,
 						StringUtils.join(names, ", ")));
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/BaseThreadItemViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/BaseThreadItemViewHolder.java
index a88f709ed1572f5e3e9eecc7fc43a5384ebc1fb9..529fa7d0013ab617acf79c4384d41ea0d4970290 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/BaseThreadItemViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/threaded/BaseThreadItemViewHolder.java
@@ -43,9 +43,8 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
 	public void bind(I item, ThreadItemListener<I> listener) {
 		textView.setText(StringUtils.trim(item.getText()));
 
-		author.setAuthor(item.getAuthor());
+		author.setAuthor(item.getAuthor(), item.getAuthorInfo());
 		author.setDate(item.getTimestamp());
-		author.setAuthorInfo(item.getAuthorInfo());
 
 		if (item.isHighlighted()) {
 			layout.setActivated(true);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItem.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItem.java
index 79cc0ab186a57e210c76993c6a99c685be2cc918..4a9a516409ab29995483c29f09b0a18e9f7f8546 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItem.java
@@ -10,6 +10,7 @@ import javax.annotation.Nullable;
 import javax.annotation.concurrent.NotThreadSafe;
 
 import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UNDEFINED;
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
 
 @NotThreadSafe
 @NotNullByDefault
@@ -70,6 +71,13 @@ public abstract class ThreadItem implements MessageNode {
 		return authorInfo;
 	}
 
+	/**
+	 * Returns the author's name, with an alias if one exists.
+	 */
+	public String getAuthorName() {
+		return getContactDisplayName(author, authorInfo.getAlias());
+	}
+
 	@Override
 	public void setLevel(int level) {
 		this.level = level;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
index ff18520f4d4ade8b28e8c928666505120b8e0343..27c24083c9dea7e496370285aef55c68a1976df0 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
@@ -33,7 +33,9 @@ import android.view.View;
 import android.widget.TextView;
 
 import org.acra.ACRA;
+import org.briarproject.bramble.api.contact.Contact;
 import org.briarproject.bramble.api.contact.ContactId;
+import org.briarproject.bramble.api.identity.Author;
 import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
 import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
 import org.briarproject.bramble.api.system.AndroidExecutor;
@@ -74,6 +76,17 @@ public class UiUtils {
 	public static final int TEASER_LENGTH = 320;
 	public static final float GREY_OUT = 0.5f;
 
+	public static String getContactDisplayName(Author author,
+			@Nullable String alias) {
+		String name = author.getName();
+		if (alias == null) return name;
+		else return String.format("%s (%s)", alias, name);
+	}
+
+	public static String getContactDisplayName(Contact c) {
+		return getContactDisplayName(c.getAuthor(), c.getAlias());
+	}
+
 	public static void setError(TextInputLayout til, @Nullable String error,
 			boolean set) {
 		if (set) {
@@ -127,7 +140,9 @@ public class UiUtils {
 		return builder;
 	}
 
-	public static Spanned getSpanned(String s) {
+	public static Spanned getSpanned(@Nullable String s) {
+		// TODO move to HtmlCompat #1435
+		// https://commonsware.com/blog/2018/05/29/at-last-htmlcompat.html
 		return Html.fromHtml(s);
 	}
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
index 783c3dcd09cca33dfc1bcce7c5d3cfc9d5bbdcca..86994db22b94c2d4cf37e4ea28784ef3dd9322fa 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
@@ -26,6 +26,7 @@ import static android.graphics.Typeface.BOLD;
 import static android.util.TypedValue.COMPLEX_UNIT_PX;
 import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
 import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
+import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
 import static org.briarproject.briar.android.util.UiUtils.resolveAttribute;
 
 @UiThread
@@ -70,16 +71,12 @@ public class AuthorView extends ConstraintLayout {
 		this(context, null);
 	}
 
-	public void setAuthor(Author author) {
-		authorName.setText(author.getName());
+	public void setAuthor(Author author, AuthorInfo authorInfo) {
+		authorName
+				.setText(getContactDisplayName(author, authorInfo.getAlias()));
 		IdenticonDrawable d = new IdenticonDrawable(author.getId().getBytes());
 		avatar.setImageDrawable(d);
 
-		invalidate();
-		requestLayout();
-	}
-
-	public void setAuthorInfo(AuthorInfo authorInfo) {
 		if (authorInfo.getStatus() != NONE) {
 			trustIndicator.setTrustLevel(authorInfo.getStatus());
 			trustIndicator.setVisibility(VISIBLE);
@@ -123,7 +120,7 @@ public class AuthorView extends ConstraintLayout {
 	 *
 	 * Attention: RSS_FEED and RSS_FEED_REBLOGGED change the avatar
 	 *            and override the one set by
-	 *            {@link AuthorView#setAuthor(Author)}.
+	 *            {@link AuthorView#setAuthor(Author, AuthorInfo)}.
 	 */
 	public void setPersona(int persona) {
 		switch (persona) {
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionRequest.java b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionRequest.java
index 5cbe9e0dd98f8bdc3aef1b5c09e4f9bced589df9..1bc2cd5cbe5378e9c9f7d93de22846e99366dab5 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionRequest.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionRequest.java
@@ -1,6 +1,7 @@
 package org.briarproject.briar.api.introduction;
 
 import org.briarproject.bramble.api.identity.Author;
+import org.briarproject.bramble.api.identity.AuthorInfo;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.bramble.api.sync.MessageId;
@@ -15,19 +16,24 @@ import javax.annotation.concurrent.Immutable;
 @NotNullByDefault
 public class IntroductionRequest extends PrivateRequest<Author> {
 
-	private final boolean contact;
+	private final AuthorInfo authorInfo;
 
 	public IntroductionRequest(MessageId messageId, GroupId groupId,
 			long time, boolean local, boolean sent, boolean seen, boolean read,
 			SessionId sessionId, Author author, @Nullable String text,
-			boolean answered, boolean contact) {
+			boolean answered, AuthorInfo authorInfo) {
 		super(messageId, groupId, time, local, sent, seen, read, sessionId,
 				author, text, answered);
-		this.contact = contact;
+		this.authorInfo = authorInfo;
+	}
+
+	@Nullable
+	public String getAlias() {
+		return authorInfo.getAlias();
 	}
 
 	public boolean isContact() {
-		return contact;
+		return authorInfo.getStatus().isContact();
 	}
 
 	@Override
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionResponse.java b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionResponse.java
index b03f8d7ca9e3dc781799edbdd786198661a2878e..960c55e4d6b97693927c9c22bf7a9f4e4b813e67 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionResponse.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionResponse.java
@@ -1,6 +1,7 @@
 package org.briarproject.briar.api.introduction;
 
 import org.briarproject.bramble.api.identity.Author;
+import org.briarproject.bramble.api.identity.AuthorInfo;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.bramble.api.sync.MessageId;
@@ -17,14 +18,17 @@ import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
 public class IntroductionResponse extends PrivateResponse {
 
 	private final Author introducedAuthor;
+	private final AuthorInfo introducedAuthorInfo;
 	private final Role ourRole;
 
 	public IntroductionResponse(MessageId messageId, GroupId groupId, long time,
 			boolean local, boolean sent, boolean seen, boolean read,
-			SessionId sessionId, boolean accepted, Author author, Role role) {
+			SessionId sessionId, boolean accepted, Author author,
+			AuthorInfo introducedAuthorInfo, Role role) {
 		super(messageId, groupId, time, local, sent, seen, read, sessionId,
 				accepted);
 		this.introducedAuthor = author;
+		this.introducedAuthorInfo = introducedAuthorInfo;
 		this.ourRole = role;
 	}
 
@@ -32,6 +36,10 @@ public class IntroductionResponse extends PrivateResponse {
 		return introducedAuthor;
 	}
 
+	public AuthorInfo getIntroducedAuthorInfo() {
+		return introducedAuthorInfo;
+	}
+
 	public boolean isIntroducer() {
 		return ourRole == INTRODUCER;
 	}
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
index 8438974e6743b88519cec5b31e2834d280c3ea6c..53ad7486113f624c9a652c1848fc26668f9aedc8 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.Transaction;
 import org.briarproject.bramble.api.identity.Author;
 import org.briarproject.bramble.api.identity.AuthorId;
+import org.briarproject.bramble.api.identity.AuthorInfo;
 import org.briarproject.bramble.api.identity.IdentityManager;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.plugin.TransportId;
@@ -149,11 +150,13 @@ abstract class AbstractProtocolEngine<S extends Session>
 			throws DbException {
 		AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
 		Contact c = contactManager.getContact(txn, sender, localAuthorId);
+		AuthorInfo otherAuthorInfo =
+				contactManager.getAuthorInfo(txn, otherAuthor.getId());
 		IntroductionResponse response =
 				new IntroductionResponse(m.getMessageId(), m.getGroupId(),
 						m.getTimestamp(), false, false, false, false,
 						s.getSessionId(), m instanceof AcceptMessage,
-						otherAuthor, s.getRole());
+						otherAuthor, otherAuthorInfo, s.getRole());
 		IntroductionResponseReceivedEvent e =
 				new IntroductionResponseReceivedEvent(response, c.getId());
 		txn.attach(e);
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
index b63da62078dd6699788d6425ceff50861e83e26c..ae055bb9b73316146837bf69613fb87376073f6d 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.db.ContactExistsException;
 import org.briarproject.bramble.api.db.DatabaseComponent;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.Transaction;
+import org.briarproject.bramble.api.identity.AuthorInfo;
 import org.briarproject.bramble.api.identity.IdentityManager;
 import org.briarproject.bramble.api.identity.LocalAuthor;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -251,12 +252,12 @@ class IntroduceeProtocolEngine
 		LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
 		Contact c = contactManager.getContact(txn, s.getIntroducer().getId(),
 				localAuthor.getId());
-		boolean contactExists = contactManager
-				.contactExists(txn, m.getAuthor().getId(), localAuthor.getId());
+		AuthorInfo authorInfo =
+				contactManager.getAuthorInfo(txn, m.getAuthor().getId());
 		IntroductionRequest request = new IntroductionRequest(m.getMessageId(),
 				m.getGroupId(), m.getTimestamp(), false, false, false, false,
 				s.getSessionId(), m.getAuthor(), m.getText(), false,
-				contactExists);
+				authorInfo);
 		IntroductionRequestReceivedEvent e =
 				new IntroductionRequestReceivedEvent(request, c.getId());
 		txn.attach(e);
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java
index 6d6727aec755013b0a4e5486a45bc5ee3f017698..2879957aa782ea7e53bc023ee9110f9971cdf3f7 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java
@@ -15,6 +15,8 @@ import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.Metadata;
 import org.briarproject.bramble.api.db.Transaction;
 import org.briarproject.bramble.api.identity.Author;
+import org.briarproject.bramble.api.identity.AuthorId;
+import org.briarproject.bramble.api.identity.AuthorInfo;
 import org.briarproject.bramble.api.identity.IdentityManager;
 import org.briarproject.bramble.api.identity.LocalAuthor;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -39,6 +41,7 @@ import org.briarproject.briar.introduction.IntroducerSession.Introducee;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -408,6 +411,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
 					.getMessageMetadataAsDictionary(txn, contactGroupId, query);
 			List<PrivateMessageHeader> messages =
 					new ArrayList<>(results.size());
+			Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
 			for (Entry<MessageId, BdfDictionary> e : results.entrySet()) {
 				MessageId m = e.getKey();
 				MessageMetadata meta =
@@ -417,17 +421,14 @@ class IntroductionManagerImpl extends ConversationClientImpl
 				if (ss == null) throw new AssertionError();
 				MessageType type = meta.getMessageType();
 				if (type == REQUEST) {
-					messages.add(
-							parseInvitationRequest(txn, contactGroupId, m,
-									meta, status, ss.bdfSession));
+					messages.add(parseInvitationRequest(txn, contactGroupId, m,
+							meta, status, ss.bdfSession, authorInfos));
 				} else if (type == ACCEPT) {
-					messages.add(
-							parseInvitationResponse(contactGroupId, m, meta,
-									status, ss.bdfSession, true));
+					messages.add(parseInvitationResponse(txn, contactGroupId, m,
+							meta, status, ss.bdfSession, authorInfos, true));
 				} else if (type == DECLINE) {
-					messages.add(
-							parseInvitationResponse(contactGroupId, m, meta,
-									status, ss.bdfSession, false));
+					messages.add(parseInvitationResponse(txn, contactGroupId, m,
+							meta, status, ss.bdfSession, authorInfos, false));
 				}
 			}
 			return messages;
@@ -438,43 +439,41 @@ class IntroductionManagerImpl extends ConversationClientImpl
 
 	private IntroductionRequest parseInvitationRequest(Transaction txn,
 			GroupId contactGroupId, MessageId m, MessageMetadata meta,
-			MessageStatus status, BdfDictionary bdfSession)
+			MessageStatus status, BdfDictionary bdfSession,
+			Map<AuthorId, AuthorInfo> authorInfos)
 			throws DbException, FormatException {
 		Role role = sessionParser.getRole(bdfSession);
 		SessionId sessionId;
-		Author author;
 		if (role == INTRODUCER) {
 			IntroducerSession session =
 					sessionParser.parseIntroducerSession(bdfSession);
 			sessionId = session.getSessionId();
-			if (contactGroupId.equals(session.getIntroduceeA().groupId)) {
-				author = session.getIntroduceeB().author;
-			} else {
-				author = session.getIntroduceeA().author;
-			}
 		} else if (role == INTRODUCEE) {
 			IntroduceeSession session = sessionParser
 					.parseIntroduceeSession(contactGroupId, bdfSession);
 			sessionId = session.getSessionId();
-			author = session.getRemote().author;
 		} else throw new AssertionError();
 		Message msg = clientHelper.getMessage(txn, m);
 		BdfList body = clientHelper.toList(msg);
 		RequestMessage rm = messageParser.parseRequestMessage(msg, body);
 		String text = rm.getText();
-		LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
-		boolean contactExists = contactManager
-				.contactExists(txn, rm.getAuthor().getId(),
-						localAuthor.getId());
+		Author author = rm.getAuthor();
+		AuthorInfo authorInfo = authorInfos.get(author.getId());
+		if (authorInfo == null) {
+			authorInfo = contactManager.getAuthorInfo(txn, author.getId());
+			authorInfos.put(author.getId(), authorInfo);
+		}
 		return new IntroductionRequest(m, contactGroupId, meta.getTimestamp(),
 				meta.isLocal(), status.isSent(), status.isSeen(), meta.isRead(),
 				sessionId, author, text, !meta.isAvailableToAnswer(),
-				contactExists);
+				authorInfo);
 	}
 
-	private IntroductionResponse parseInvitationResponse(GroupId contactGroupId,
-			MessageId m, MessageMetadata meta, MessageStatus status,
-			BdfDictionary bdfSession, boolean accept) throws FormatException {
+	private IntroductionResponse parseInvitationResponse(Transaction txn,
+			GroupId contactGroupId, MessageId m, MessageMetadata meta,
+			MessageStatus status, BdfDictionary bdfSession,
+			Map<AuthorId, AuthorInfo> authorInfos, boolean accept)
+			throws FormatException, DbException {
 		Role role = sessionParser.getRole(bdfSession);
 		SessionId sessionId;
 		Author author;
@@ -493,9 +492,14 @@ class IntroductionManagerImpl extends ConversationClientImpl
 			sessionId = session.getSessionId();
 			author = session.getRemote().author;
 		} else throw new AssertionError();
+		AuthorInfo authorInfo = authorInfos.get(author.getId());
+		if (authorInfo == null) {
+			authorInfo = contactManager.getAuthorInfo(txn, author.getId());
+			authorInfos.put(author.getId(), authorInfo);
+		}
 		return new IntroductionResponse(m, contactGroupId, meta.getTimestamp(),
 				meta.isLocal(), status.isSent(), status.isSeen(), meta.isRead(),
-				sessionId, accept, author, role);
+				sessionId, accept, author, authorInfo, role);
 	}
 
 	private void removeSessionWithIntroducer(Transaction txn,
diff --git a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
index e8519e21905c7d5a22bfbb924a8bf790ea9ff456..f0d37a2c36828230b278d53105eb03309b2db4a5 100644
--- a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
@@ -174,6 +174,10 @@ public class TestDataCreatorImpl implements TestDataCreator {
 			ContactId contactId = contactManager
 					.addContact(txn, author, localAuthorId, secretKey,
 							timestamp, true, verified, true);
+			if (random.nextBoolean()) {
+				contactManager
+						.setContactAlias(txn, contactId, getRandomAuthorName());
+			}
 			transportPropertyManager.addRemoteProperties(txn, contactId, props);
 			contact = db.getContact(txn, contactId);
 			db.commitTransaction(txn);
@@ -202,10 +206,13 @@ public class TestDataCreatorImpl implements TestDataCreator {
 		return authorFactory.createLocalAuthor(name, publicKey, privateKey);
 	}
 
-	private LocalAuthor getRandomAuthor() {
+	private String getRandomAuthorName() {
 		int i = random.nextInt(AUTHOR_NAMES.length);
-		String authorName = AUTHOR_NAMES[i];
-		return getAuthor(authorName);
+		return AUTHOR_NAMES[i];
+	}
+
+	private LocalAuthor getRandomAuthor() {
+		return getAuthor(getRandomAuthorName());
 	}
 
 	private SecretKey getSecretKey() {
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt
index f5728603d95f2eaa0a92972db11e29af077a24b4..88e4715980e8cbbb6a6cd3664d4fca6aa07ece32 100644
--- a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt
@@ -7,6 +7,9 @@ import io.javalin.json.JavalinJson.toJson
 import io.mockk.*
 import org.briarproject.bramble.api.contact.ContactId
 import org.briarproject.bramble.api.db.NoSuchContactException
+import org.briarproject.bramble.api.identity.AuthorInfo
+import org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED
+import org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED
 import org.briarproject.bramble.test.ImmediateExecutor
 import org.briarproject.bramble.test.TestUtils.getRandomId
 import org.briarproject.bramble.util.StringUtils.getRandomString
@@ -61,7 +64,7 @@ internal class MessagingControllerImplTest : ControllerTest() {
     fun listIntroductionRequest() {
         val request = IntroductionRequest(
             message.id, group.id, timestamp, true, true, false, true, sessionId, author, text,
-            false, false
+            false, AuthorInfo(UNVERIFIED)
         )
 
         expectGetContact()
@@ -200,7 +203,7 @@ internal class MessagingControllerImplTest : ControllerTest() {
     fun testIntroductionRequestWithNullText() {
         val request = IntroductionRequest(
             message.id, group.id, timestamp, true, true, false, true, sessionId, author, null,
-            false, false
+            false, AuthorInfo(VERIFIED)
         )
         val json = """
             {