mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Convert provisioning and certificate endpoints to WebSocket and finalize attachments.
This commit is contained in:
@@ -106,14 +106,6 @@ public class SignalServiceAccountManager {
|
||||
this.configuration = pushServiceSocket.getConfiguration();
|
||||
}
|
||||
|
||||
public byte[] getSenderCertificate() throws IOException {
|
||||
return this.pushServiceSocket.getSenderCertificate();
|
||||
}
|
||||
|
||||
public byte[] getSenderCertificateForPhoneNumberPrivacy() throws IOException {
|
||||
return this.pushServiceSocket.getUuidOnlySenderCertificate();
|
||||
}
|
||||
|
||||
public SecureValueRecoveryV2 getSecureValueRecoveryV2(String mrEnclave) {
|
||||
return new SecureValueRecoveryV2(configuration, mrEnclave, authWebSocket);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage;
|
||||
import org.signal.libsignal.protocol.state.PreKeyBundle;
|
||||
import org.signal.libsignal.protocol.state.SessionRecord;
|
||||
import org.signal.libsignal.zkgroup.groupsend.GroupSendFullToken;
|
||||
import org.whispersystems.signalservice.api.attachment.AttachmentApi;
|
||||
import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil;
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||
import org.whispersystems.signalservice.api.crypto.EnvelopeContent;
|
||||
@@ -77,14 +78,12 @@ import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||
import org.whispersystems.signalservice.api.services.AttachmentService;
|
||||
import org.whispersystems.signalservice.api.util.AttachmentPointerUtil;
|
||||
import org.whispersystems.signalservice.api.util.CredentialsProvider;
|
||||
import org.whispersystems.signalservice.api.util.Preconditions;
|
||||
import org.whispersystems.signalservice.api.util.Uint64RangeException;
|
||||
import org.whispersystems.signalservice.api.util.Uint64Util;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
import org.whispersystems.signalservice.api.websocket.SignalWebSocket;
|
||||
import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException;
|
||||
import org.whispersystems.signalservice.internal.crypto.AttachmentDigest;
|
||||
import org.whispersystems.signalservice.internal.crypto.PaddingInputStream;
|
||||
@@ -175,9 +174,9 @@ public class SignalServiceMessageSender {
|
||||
private final Optional<EventListener> eventListener;
|
||||
private final IdentityKeyPair localPniIdentity;
|
||||
|
||||
private final AttachmentService attachmentService;
|
||||
private final MessageApi messageApi;
|
||||
private final KeysApi keysApi;
|
||||
private final AttachmentApi attachmentApi;
|
||||
private final MessageApi messageApi;
|
||||
private final KeysApi keysApi;
|
||||
|
||||
private final Scheduler scheduler;
|
||||
private final long maxEnvelopeSize;
|
||||
@@ -185,7 +184,7 @@ public class SignalServiceMessageSender {
|
||||
public SignalServiceMessageSender(PushServiceSocket pushServiceSocket,
|
||||
SignalServiceDataStore store,
|
||||
SignalSessionLock sessionLock,
|
||||
SignalWebSocket.AuthenticatedWebSocket authWebSocket,
|
||||
AttachmentApi attachmentApi,
|
||||
MessageApi messageApi,
|
||||
KeysApi keysApi,
|
||||
Optional<EventListener> eventListener,
|
||||
@@ -194,19 +193,19 @@ public class SignalServiceMessageSender {
|
||||
{
|
||||
CredentialsProvider credentialsProvider = pushServiceSocket.getCredentialsProvider();
|
||||
|
||||
this.socket = pushServiceSocket;
|
||||
this.aciStore = store.aci();
|
||||
this.sessionLock = sessionLock;
|
||||
this.localAddress = new SignalServiceAddress(credentialsProvider.getAci(), credentialsProvider.getE164());
|
||||
this.localDeviceId = credentialsProvider.getDeviceId();
|
||||
this.localPni = credentialsProvider.getPni();
|
||||
this.attachmentService = new AttachmentService(authWebSocket);
|
||||
this.messageApi = messageApi;
|
||||
this.eventListener = eventListener;
|
||||
this.maxEnvelopeSize = maxEnvelopeSize;
|
||||
this.localPniIdentity = store.pni().getIdentityKeyPair();
|
||||
this.scheduler = Schedulers.from(executor, false, false);
|
||||
this.keysApi = keysApi;
|
||||
this.socket = pushServiceSocket;
|
||||
this.aciStore = store.aci();
|
||||
this.sessionLock = sessionLock;
|
||||
this.localAddress = new SignalServiceAddress(credentialsProvider.getAci(), credentialsProvider.getE164());
|
||||
this.localDeviceId = credentialsProvider.getDeviceId();
|
||||
this.localPni = credentialsProvider.getPni();
|
||||
this.attachmentApi = attachmentApi;
|
||||
this.messageApi = messageApi;
|
||||
this.eventListener = eventListener;
|
||||
this.maxEnvelopeSize = maxEnvelopeSize;
|
||||
this.localPniIdentity = store.pni().getIdentityKeyPair();
|
||||
this.scheduler = Schedulers.from(executor, false, false);
|
||||
this.keysApi = keysApi;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -810,24 +809,8 @@ public class SignalServiceMessageSender {
|
||||
}
|
||||
|
||||
public ResumableUploadSpec getResumableUploadSpec() throws IOException {
|
||||
AttachmentUploadForm v4UploadAttributes = null;
|
||||
|
||||
Log.d(TAG, "Using pipe to retrieve attachment upload attributes...");
|
||||
try {
|
||||
v4UploadAttributes = new AttachmentService.AttachmentAttributesResponseProcessor<>(attachmentService.getAttachmentV4UploadAttributes().blockingGet()).getResultOrThrow();
|
||||
} catch (WebSocketUnavailableException e) {
|
||||
Log.w(TAG, "[getResumableUploadSpec] Pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")");
|
||||
} catch (IOException e) {
|
||||
if (e instanceof RateLimitException) {
|
||||
throw e;
|
||||
}
|
||||
Log.w(TAG, "Failed to retrieve attachment upload attributes using pipe. Falling back...");
|
||||
}
|
||||
|
||||
if (v4UploadAttributes == null) {
|
||||
Log.d(TAG, "Not using pipe to retrieve attachment upload attributes...");
|
||||
v4UploadAttributes = socket.getAttachmentV4UploadAttributes();
|
||||
}
|
||||
AttachmentUploadForm v4UploadAttributes = NetworkResultUtil.toBasicLegacy(attachmentApi.getAttachmentV4UploadForm());
|
||||
|
||||
return socket.getResumableUploadSpec(v4UploadAttributes);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.certificate
|
||||
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
import org.whispersystems.signalservice.api.websocket.SignalWebSocket
|
||||
import org.whispersystems.signalservice.internal.get
|
||||
import org.whispersystems.signalservice.internal.push.SenderCertificate
|
||||
import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage
|
||||
|
||||
/**
|
||||
* Endpoints to get [SenderCertificate]s.
|
||||
*/
|
||||
class CertificateApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSocket) {
|
||||
|
||||
/**
|
||||
* GET /v1/certificate/delivery
|
||||
* - 200: Success
|
||||
*/
|
||||
fun getSenderCertificate(): NetworkResult<ByteArray> {
|
||||
val request = WebSocketRequestMessage.get("/v1/certificate/delivery")
|
||||
return NetworkResult.fromWebSocketRequest(authWebSocket, request, SenderCertificate::class)
|
||||
.map { it.certificate }
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /v1/certificate/delivery?includeE164=false
|
||||
* - 200: Success
|
||||
*/
|
||||
fun getSenderCertificateForPhoneNumberPrivacy(): NetworkResult<ByteArray> {
|
||||
val request = WebSocketRequestMessage.get("/v1/certificate/delivery?includeE164=false")
|
||||
return NetworkResult.fromWebSocketRequest(authWebSocket, request, SenderCertificate::class)
|
||||
.map { it.certificate }
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import org.whispersystems.signalservice.api.backup.MediaRootBackupKey
|
||||
import org.whispersystems.signalservice.api.backup.MessageBackupKey
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo
|
||||
import org.whispersystems.signalservice.api.provisioning.ProvisioningMessage
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.PNI
|
||||
import org.whispersystems.signalservice.api.websocket.SignalWebSocket
|
||||
@@ -24,7 +25,6 @@ import org.whispersystems.signalservice.internal.delete
|
||||
import org.whispersystems.signalservice.internal.get
|
||||
import org.whispersystems.signalservice.internal.push.DeviceInfoList
|
||||
import org.whispersystems.signalservice.internal.push.ProvisionMessage
|
||||
import org.whispersystems.signalservice.internal.push.ProvisioningMessage
|
||||
import org.whispersystems.signalservice.internal.push.ProvisioningVersion
|
||||
import org.whispersystems.signalservice.internal.put
|
||||
import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.provisioning
|
||||
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.urlEncode
|
||||
import org.signal.libsignal.protocol.ecc.ECPublicKey
|
||||
import org.signal.registration.proto.RegistrationProvisionMessage
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
import org.whispersystems.signalservice.api.registration.RestoreMethodBody
|
||||
import org.whispersystems.signalservice.api.websocket.SignalWebSocket
|
||||
import org.whispersystems.signalservice.internal.crypto.PrimaryProvisioningCipher
|
||||
import org.whispersystems.signalservice.internal.get
|
||||
import org.whispersystems.signalservice.internal.put
|
||||
import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* Linked and new device provisioning endpoints.
|
||||
*/
|
||||
class ProvisioningApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSocket) {
|
||||
|
||||
/**
|
||||
* Encrypts and sends the [registrationProvisionMessage] from the current primary (old device) to the new device over
|
||||
* the provisioning web socket identified by [deviceIdentifier].
|
||||
*
|
||||
* PUT /v1/provisioning/[deviceIdentifier]
|
||||
* - 204: Success
|
||||
* - 400: Message was too large
|
||||
* - 404: No provisioning socket for [deviceIdentifier]
|
||||
*/
|
||||
fun sendReRegisterDeviceProvisioningMessage(
|
||||
deviceIdentifier: String,
|
||||
deviceKey: ECPublicKey,
|
||||
registrationProvisionMessage: RegistrationProvisionMessage
|
||||
): NetworkResult<Unit> {
|
||||
val cipherText = PrimaryProvisioningCipher(deviceKey).encrypt(registrationProvisionMessage)
|
||||
|
||||
val request = WebSocketRequestMessage.put("/v1/provisioning/${deviceIdentifier.urlEncode()}", ProvisioningMessage(Base64.encodeWithPadding(cipherText)))
|
||||
return NetworkResult.fromWebSocketRequest(authWebSocket, request)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the [RestoreMethod] to be set on the server by the new device. This is a long polling operation.
|
||||
*
|
||||
* GET /v1/devices/restore_account/[token]?timeout=[timeout]
|
||||
* - 200: A request was received for the given token
|
||||
* - 204: No request given yet, may repeat call to continue waiting
|
||||
* - 400: Invalid [token] or [timeout]
|
||||
* - 429: Rate limited
|
||||
*/
|
||||
fun waitForRestoreMethod(token: String, timeout: Int = 30): NetworkResult<RestoreMethod> {
|
||||
val request = WebSocketRequestMessage.get("/v1/devices/restore_account/${token.urlEncode()}?timeout=$timeout")
|
||||
|
||||
return NetworkResult.fromWebSocket(NetworkResult.LongPollingWebSocketConverter(RestoreMethodBody::class)) {
|
||||
authWebSocket.request(request, timeout.seconds)
|
||||
}.map {
|
||||
it.method ?: RestoreMethod.DECLINE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
package org.whispersystems.signalservice.api.provisioning;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.registration
|
||||
package org.whispersystems.signalservice.api.provisioning
|
||||
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
@@ -3,7 +3,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.registration
|
||||
package org.whispersystems.signalservice.api.provisioning
|
||||
|
||||
/**
|
||||
* Restore method chosen by user on new device after performing a quick-restore.
|
||||
@@ -5,12 +5,10 @@
|
||||
|
||||
package org.whispersystems.signalservice.api.registration
|
||||
|
||||
import org.signal.libsignal.protocol.ecc.ECPublicKey
|
||||
import org.signal.registration.proto.RegistrationProvisionMessage
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
import org.whispersystems.signalservice.api.account.AccountAttributes
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollection
|
||||
import org.whispersystems.signalservice.internal.crypto.PrimaryProvisioningCipher
|
||||
import org.whispersystems.signalservice.api.provisioning.RestoreMethod
|
||||
import org.whispersystems.signalservice.internal.push.BackupV2AuthCheckResponse
|
||||
import org.whispersystems.signalservice.internal.push.BackupV3AuthCheckResponse
|
||||
import org.whispersystems.signalservice.internal.push.PushServiceSocket
|
||||
@@ -127,22 +125,6 @@ class RegistrationApi(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts and sends the [RegistrationProvisionMessage] from the current primary (old device) to the new device over
|
||||
* the provisioning web socket identified by [deviceIdentifier].
|
||||
*/
|
||||
fun sendReRegisterDeviceProvisioningMessage(
|
||||
deviceIdentifier: String,
|
||||
deviceKey: ECPublicKey,
|
||||
registrationProvisionMessage: RegistrationProvisionMessage
|
||||
): NetworkResult<Unit> {
|
||||
val cipherText = PrimaryProvisioningCipher(deviceKey).encrypt(registrationProvisionMessage)
|
||||
|
||||
return NetworkResult.fromFetch {
|
||||
pushServiceSocket.sendProvisioningMessage(deviceIdentifier, cipherText)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set [RestoreMethod] enum on the server for use by the old device to update UX.
|
||||
*/
|
||||
@@ -151,13 +133,4 @@ class RegistrationApi(
|
||||
pushServiceSocket.setRestoreMethodChosen(token, RestoreMethodBody(method = method))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the [RestoreMethod] to be set on the server by the new device. This is a long polling operation.
|
||||
*/
|
||||
fun waitForRestoreMethod(token: String, timeout: Int = 30): NetworkResult<RestoreMethod> {
|
||||
return NetworkResult.fromFetch {
|
||||
pushServiceSocket.waitForRestoreMethodChosen(token, timeout).method ?: RestoreMethod.DECLINE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.whispersystems.signalservice.api.registration
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import org.whispersystems.signalservice.api.provisioning.RestoreMethod
|
||||
|
||||
/**
|
||||
* Request and response body used to communicate a quick restore method selection during registration.
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.services
|
||||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import org.whispersystems.signalservice.api.websocket.SignalWebSocket
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
||||
import org.whispersystems.signalservice.internal.push.AttachmentUploadForm
|
||||
import org.whispersystems.signalservice.internal.websocket.DefaultResponseMapper
|
||||
import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage
|
||||
import org.whispersystems.signalservice.internal.websocket.WebsocketResponse
|
||||
import java.security.SecureRandom
|
||||
|
||||
/**
|
||||
* Provide WebSocket based interface to attachment upload endpoints.
|
||||
*
|
||||
* Note: To be expanded to have REST fallback and other attachment related operations.
|
||||
*/
|
||||
class AttachmentService(private val authWebSocket: SignalWebSocket.AuthenticatedWebSocket) {
|
||||
fun getAttachmentV4UploadAttributes(): Single<ServiceResponse<AttachmentUploadForm>> {
|
||||
val requestMessage = WebSocketRequestMessage(
|
||||
id = SecureRandom().nextLong(),
|
||||
verb = "GET",
|
||||
path = "/v4/attachments/form/upload"
|
||||
)
|
||||
|
||||
return authWebSocket.request(requestMessage)
|
||||
.map { response: WebsocketResponse? -> DefaultResponseMapper.getDefault(AttachmentUploadForm::class.java).map(response) }
|
||||
.onErrorReturn { throwable: Throwable? -> ServiceResponse.forUnknownError(throwable) }
|
||||
}
|
||||
|
||||
class AttachmentAttributesResponseProcessor<T>(response: ServiceResponse<T>) : ServiceResponseProcessor<T>(response)
|
||||
}
|
||||
@@ -185,19 +185,13 @@ public class PushServiceSocket {
|
||||
|
||||
private static final String DELETE_ACCOUNT_PATH = "/v1/accounts/me";
|
||||
|
||||
private static final String PROVISIONING_MESSAGE_PATH = "/v1/provisioning/%s";
|
||||
private static final String SET_RESTORE_METHOD_PATH = "/v1/devices/restore_account/%s";
|
||||
private static final String WAIT_RESTORE_METHOD_PATH = "/v1/devices/restore_account/%s?timeout=%s";
|
||||
|
||||
private static final String GROUP_MESSAGE_PATH = "/v1/messages/multi_recipient?ts=%s&online=%s&urgent=%s&story=%s";
|
||||
private static final String ATTACHMENT_V4_PATH = "/v4/attachments/form/upload";
|
||||
|
||||
private static final String PROFILE_PATH = "/v1/profile/%s";
|
||||
private static final String PROFILE_BATCH_CHECK_PATH = "/v1/profile/identity_check/batch";
|
||||
|
||||
private static final String SENDER_CERTIFICATE_PATH = "/v1/certificate/delivery";
|
||||
private static final String SENDER_CERTIFICATE_NO_E164_PATH = "/v1/certificate/delivery?includeE164=false";
|
||||
|
||||
private static final String ATTACHMENT_KEY_DOWNLOAD_PATH = "attachments/%s";
|
||||
private static final String ATTACHMENT_ID_DOWNLOAD_PATH = "attachments/%d";
|
||||
private static final String AVATAR_UPLOAD_PATH = "";
|
||||
@@ -242,11 +236,8 @@ public class PushServiceSocket {
|
||||
|
||||
private static final String ARCHIVE_MEDIA_DOWNLOAD_PATH = "backups/%s/%s";
|
||||
|
||||
private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp";
|
||||
|
||||
private static final Map<String, String> NO_HEADERS = Collections.emptyMap();
|
||||
private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler();
|
||||
private static final ResponseCodeHandler LONG_POLL_HANDLER = new LongPollingResponseCodeHandler();
|
||||
private static final ResponseCodeHandler UNOPINIONATED_HANDLER = new UnopinionatedResponseCodeHandler();
|
||||
private static final ResponseCodeHandler UNOPINIONATED_BINARY_ERROR_HANDLER = new UnopinionatedBinaryErrorResponseCodeHandler();
|
||||
|
||||
@@ -404,33 +395,10 @@ public class PushServiceSocket {
|
||||
makeServiceRequest(String.format(Locale.US, SET_RESTORE_METHOD_PATH, urlEncode(token)), "PUT", body, NO_HEADERS, UNOPINIONATED_HANDLER, SealedSenderAccess.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a long-polling endpoint that relies on the fact that our normal connection timeout is already 30s.
|
||||
*/
|
||||
public @Nonnull RestoreMethodBody waitForRestoreMethodChosen(@Nonnull String token, int timeoutSeconds) throws IOException {
|
||||
String response = makeServiceRequest(String.format(Locale.US, WAIT_RESTORE_METHOD_PATH, urlEncode(token), timeoutSeconds), "GET", null, NO_HEADERS, LONG_POLL_HANDLER, SealedSenderAccess.NONE);
|
||||
return JsonUtil.fromJsonResponse(response, RestoreMethodBody.class);
|
||||
}
|
||||
|
||||
public void sendProvisioningMessage(String destination, byte[] body) throws IOException {
|
||||
makeServiceRequest(String.format(PROVISIONING_MESSAGE_PATH, urlEncode(destination)), "PUT",
|
||||
JsonUtil.toJson(new ProvisioningMessage(Base64.encodeWithPadding(body))));
|
||||
}
|
||||
|
||||
public void requestPushChallenge(String sessionId, String gcmRegistrationId) throws IOException {
|
||||
patchVerificationSession(sessionId, gcmRegistrationId, null, null, null, null);
|
||||
}
|
||||
|
||||
public byte[] getSenderCertificate() throws IOException {
|
||||
String responseText = makeServiceRequest(SENDER_CERTIFICATE_PATH, "GET", null);
|
||||
return JsonUtil.fromJson(responseText, SenderCertificate.class).getCertificate();
|
||||
}
|
||||
|
||||
public byte[] getUuidOnlySenderCertificate() throws IOException {
|
||||
String responseText = makeServiceRequest(SENDER_CERTIFICATE_NO_E164_PATH, "GET", null);
|
||||
return JsonUtil.fromJson(responseText, SenderCertificate.class).getCertificate();
|
||||
}
|
||||
|
||||
public SendGroupMessageResponse sendGroupMessage(byte[] body, @Nonnull SealedSenderAccess sealedSenderAccess, long timestamp, boolean online, boolean urgent, boolean story)
|
||||
throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException
|
||||
{
|
||||
@@ -888,18 +856,6 @@ public class PushServiceSocket {
|
||||
}
|
||||
}
|
||||
|
||||
public AttachmentUploadForm getAttachmentV4UploadAttributes()
|
||||
throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException
|
||||
{
|
||||
String response = makeServiceRequest(ATTACHMENT_V4_PATH, "GET", null);
|
||||
try {
|
||||
return JsonUtil.fromJson(response, AttachmentUploadForm.class);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
throw new MalformedResponseException("Unable to parse entity", e);
|
||||
}
|
||||
}
|
||||
|
||||
public AttachmentDigest uploadGroupV2Avatar(byte[] avatarCipherText, AvatarUploadAttributes uploadAttributes)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user