Only upload your avatar if it's being changed.

New server param means we don't have to upload the avatar if we want to
keep it the same.
This commit is contained in:
Greyson Parrelli
2022-03-14 11:08:17 -04:00
committed by Cody Henthorne
parent 87ad4be117
commit b45740884b
4 changed files with 76 additions and 31 deletions

View File

@@ -6,20 +6,15 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.google.protobuf.ByteString;
import org.signal.core.util.logging.Log;
import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.profiles.ProfileKey;
import org.thoughtcrime.securesms.badges.models.Badge;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
@@ -44,12 +39,12 @@ import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException;
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.profiles.AvatarUploadParams;
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.services.ProfileService;
import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.ServiceResponse;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
@@ -226,14 +221,12 @@ public final class ProfileUtil {
*/
public static void uploadProfileWithBadges(@NonNull Context context, @NonNull List<Badge> badges) throws IOException {
Log.d(TAG, "uploadProfileWithBadges()");
try (StreamDetails avatar = AvatarHelper.getSelfProfileAvatarStream(context)) {
uploadProfile(Recipient.self().getProfileName(),
Optional.fromNullable(Recipient.self().getAbout()).or(""),
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
getSelfPaymentsAddressProtobuf(),
avatar,
badges);
}
uploadProfile(Recipient.self().getProfileName(),
Optional.fromNullable(Recipient.self().getAbout()).or(""),
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
getSelfPaymentsAddressProtobuf(),
AvatarUploadParams.unchanged(AvatarHelper.hasAvatar(context, Recipient.self().getId())),
badges);
}
/**
@@ -248,7 +241,7 @@ public final class ProfileUtil {
Optional.fromNullable(Recipient.self().getAbout()).or(""),
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
getSelfPaymentsAddressProtobuf(),
avatar,
AvatarUploadParams.unchanged(AvatarHelper.hasAvatar(context, Recipient.self().getId())),
Recipient.self().getBadges());
}
}
@@ -265,7 +258,7 @@ public final class ProfileUtil {
about,
emoji,
getSelfPaymentsAddressProtobuf(),
avatar,
AvatarUploadParams.unchanged(AvatarHelper.hasAvatar(context, Recipient.self().getId())),
Recipient.self().getBadges());
}
}
@@ -291,7 +284,7 @@ public final class ProfileUtil {
Optional.fromNullable(Recipient.self().getAbout()).or(""),
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
getSelfPaymentsAddressProtobuf(),
avatar,
AvatarUploadParams.forAvatar(avatar),
Recipient.self().getBadges());
}
@@ -299,7 +292,7 @@ public final class ProfileUtil {
@Nullable String about,
@Nullable String aboutEmoji,
@Nullable SignalServiceProtos.PaymentAddress paymentsAddress,
@Nullable StreamDetails avatar,
@NonNull AvatarUploadParams avatar,
@NonNull List<Badge> badges)
throws IOException
{
@@ -312,8 +305,13 @@ public final class ProfileUtil {
Log.d(TAG, "Uploading " + (!Util.isEmpty(about) ? "non-" : "") + "empty about.");
Log.d(TAG, "Uploading " + (!Util.isEmpty(aboutEmoji) ? "non-" : "") + "empty emoji.");
Log.d(TAG, "Uploading " + (paymentsAddress != null ? "non-" : "") + "empty payments address.");
Log.d(TAG, "Uploading " + (avatar != null && avatar.getLength() != 0 ? "non-" : "") + "empty avatar.");
Log.d(TAG, "Uploading " + ((!badgeIds.isEmpty()) ? "non-" : "") + "empty badge list");
Log.d(TAG, "Uploading " + ((!badgeIds.isEmpty()) ? "non-" : "") + "empty badge list.");
if (avatar.keepTheSame) {
Log.d(TAG, "Leaving avatar unchanged. We think we " + (avatar.hasAvatar ? "" : "do not ") + "have one.");
} else {
Log.d(TAG, "Uploading " + (avatar.stream != null && avatar.stream.getLength() != 0 ? "non-" : "") + "empty avatar.");
}
ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey();
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
@@ -326,7 +324,9 @@ public final class ProfileUtil {
avatar,
badgeIds).orNull();
SignalStore.registrationValues().markHasUploadedProfile();
SignalDatabase.recipients().setProfileAvatar(Recipient.self().getId(), avatarPath);
if (!avatar.keepTheSame) {
SignalDatabase.recipients().setProfileAvatar(Recipient.self().getId(), avatarPath);
}
ApplicationDependencies.getJobManager().add(new RefreshOwnProfileJob());
}

View File

@@ -32,6 +32,7 @@ import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
import org.whispersystems.signalservice.api.messages.multidevice.VerifyDeviceResponse;
import org.whispersystems.signalservice.api.payments.CurrencyConversions;
import org.whispersystems.signalservice.api.profiles.AvatarUploadParams;
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfileWrite;
import org.whispersystems.signalservice.api.push.ACI;
@@ -53,7 +54,6 @@ import org.whispersystems.signalservice.api.storage.StorageId;
import org.whispersystems.signalservice.api.storage.StorageKey;
import org.whispersystems.signalservice.api.storage.StorageManifestKey;
import org.whispersystems.signalservice.api.util.CredentialsProvider;
import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.internal.ServiceResponse;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.contacts.crypto.ContactDiscoveryCipher;
@@ -811,7 +811,7 @@ public class SignalServiceAccountManager {
String about,
String aboutEmoji,
Optional<SignalServiceProtos.PaymentAddress> paymentsAddress,
StreamDetails avatar,
AvatarUploadParams avatar,
List<String> visibleBadgeIds)
throws IOException
{
@@ -822,13 +822,12 @@ public class SignalServiceAccountManager {
byte[] ciphertextAbout = profileCipher.encryptString(about, ProfileCipher.getTargetAboutLength(about));
byte[] ciphertextEmoji = profileCipher.encryptString(aboutEmoji, ProfileCipher.EMOJI_PADDED_LENGTH);
byte[] ciphertextMobileCoinAddress = paymentsAddress.transform(address -> profileCipher.encryptWithLength(address.toByteArray(), ProfileCipher.PAYMENTS_ADDRESS_CONTENT_SIZE)).orNull();
boolean hasAvatar = avatar != null;
ProfileAvatarData profileAvatarData = null;
if (hasAvatar) {
profileAvatarData = new ProfileAvatarData(avatar.getStream(),
ProfileCipherOutputStream.getCiphertextLength(avatar.getLength()),
avatar.getContentType(),
if (avatar.stream != null && !avatar.keepTheSame) {
profileAvatarData = new ProfileAvatarData(avatar.stream.getStream(),
ProfileCipherOutputStream.getCiphertextLength(avatar.stream.getLength()),
avatar.stream.getContentType(),
new ProfileCipherOutputStreamFactory(profileKey));
}
@@ -837,7 +836,8 @@ public class SignalServiceAccountManager {
ciphertextAbout,
ciphertextEmoji,
ciphertextMobileCoinAddress,
hasAvatar,
avatar.hasAvatar,
avatar.keepTheSame,
profileKey.getCommitment(aci.uuid()).serialize(),
visibleBadgeIds),
profileAvatarData);

View File

@@ -0,0 +1,41 @@
package org.whispersystems.signalservice.api.profiles;
import org.whispersystems.signalservice.api.util.StreamDetails;
/**
* A model to represent the attributes of an avatar upload.
*/
public class AvatarUploadParams {
/** Whether or not you should keep the avatar the same on the server. */
public final boolean keepTheSame;
/** Whether or not you want an avatar. */
public final boolean hasAvatar;
/** A stream representing the content of the avatar to be uploaded. */
public final StreamDetails stream;
/**
* Indicates that you want to leave the avatar as it already is on the server.
* @param hasAvatar Whether or not you have an avatar. If true, the avatar you have on the server will remain as it is.
* If this is false, it *will* delete the avatar. Weird to have this with the 'unchanged' bit, I know,
* but this boolean already existed before we added the 'keepTheSame' functionality, so we gotta deal
* with it.
*/
public static AvatarUploadParams unchanged(boolean hasAvatar) {
return new AvatarUploadParams(true, hasAvatar, null);
}
/**
* Indicates that you'd like set the contents of this stream as your avatar. If null, the avatar will be removed.
*/
public static AvatarUploadParams forAvatar(StreamDetails avatarStream) {
return new AvatarUploadParams(false, avatarStream != null, avatarStream);
}
private AvatarUploadParams(boolean keepTheSame, boolean hasAvatar, StreamDetails stream) {
this.keepTheSame = keepTheSame;
this.hasAvatar = hasAvatar;
this.stream = stream;
}
}

View File

@@ -25,6 +25,9 @@ public class SignalServiceProfileWrite {
@JsonProperty
private boolean avatar;
@JsonProperty
private boolean sameAvatar;
@JsonProperty
private byte[] commitment;
@@ -35,13 +38,14 @@ public class SignalServiceProfileWrite {
public SignalServiceProfileWrite(){
}
public SignalServiceProfileWrite(String version, byte[] name, byte[] about, byte[] aboutEmoji, byte[] paymentAddress, boolean avatar, byte[] commitment, List<String> badgeIds) {
public SignalServiceProfileWrite(String version, byte[] name, byte[] about, byte[] aboutEmoji, byte[] paymentAddress, boolean hasAvatar, boolean sameAvatar, byte[] commitment, List<String> badgeIds) {
this.version = version;
this.name = name;
this.about = about;
this.aboutEmoji = aboutEmoji;
this.paymentAddress = paymentAddress;
this.avatar = avatar;
this.avatar = hasAvatar;
this.sameAvatar = sameAvatar;
this.commitment = commitment;
this.badgeIds = badgeIds;
}