mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-26 11:08:02 +00:00
Add initial support for Group Calling.
This commit is contained in:
@@ -34,6 +34,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.AnswerMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.CallingResponse;
|
||||
import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
|
||||
@@ -89,7 +90,6 @@ import org.whispersystems.util.Base64;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.SecureRandom;
|
||||
import java.sql.Time;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
@@ -239,6 +239,20 @@ public class SignalServiceMessageSender {
|
||||
sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), content, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an http request on behalf of the calling infrastructure.
|
||||
*
|
||||
* @param requestId Request identifier
|
||||
* @param url Fully qualified URL to request
|
||||
* @param httpMethod Http method to use (e.g., "GET", "POST")
|
||||
* @param headers Optional list of headers to send with request
|
||||
* @param body Optional body to send with request
|
||||
* @return
|
||||
*/
|
||||
public CallingResponse makeCallingRequest(long requestId, String url, String httpMethod, List<Pair<String, String>> headers, byte[] body) {
|
||||
return socket.makeCallingRequest(requestId, url, httpMethod, headers, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a single recipient.
|
||||
*
|
||||
@@ -773,6 +787,8 @@ public class SignalServiceMessageSender {
|
||||
}
|
||||
} else if (callMessage.getBusyMessage().isPresent()) {
|
||||
builder.setBusy(CallMessage.Busy.newBuilder().setId(callMessage.getBusyMessage().get().getId()));
|
||||
} else if (callMessage.getOpaqueMessage().isPresent()) {
|
||||
builder.setOpaque(CallMessage.Opaque.newBuilder().setData(ByteString.copyFrom(callMessage.getOpaqueMessage().get().getOpaque())));
|
||||
}
|
||||
|
||||
builder.setMultiRing(callMessage.isMultiRing());
|
||||
|
@@ -7,6 +7,7 @@ import org.signal.storageservice.protos.groups.Group;
|
||||
import org.signal.storageservice.protos.groups.GroupAttributeBlob;
|
||||
import org.signal.storageservice.protos.groups.GroupChange;
|
||||
import org.signal.storageservice.protos.groups.GroupChanges;
|
||||
import org.signal.storageservice.protos.groups.GroupExternalCredential;
|
||||
import org.signal.storageservice.protos.groups.GroupJoinInfo;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
@@ -166,6 +167,12 @@ public final class GroupsV2Api {
|
||||
return socket.patchGroupsV2Group(groupChange, authorization.toString(), groupLinkPassword);
|
||||
}
|
||||
|
||||
public GroupExternalCredential getGroupExternalCredential(GroupsV2AuthorizationString authorization)
|
||||
throws IOException
|
||||
{
|
||||
return socket.getGroupExternalCredential(authorization);
|
||||
}
|
||||
|
||||
private static HashMap<Integer, AuthCredentialResponse> parseCredentialResponse(CredentialResponse credentialResponse)
|
||||
throws IOException
|
||||
{
|
||||
|
@@ -23,6 +23,7 @@ import org.whispersystems.signalservice.api.messages.calls.BusyMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.OpaqueMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage;
|
||||
@@ -616,6 +617,9 @@ public final class SignalServiceContent {
|
||||
} else if (content.hasBusy()) {
|
||||
SignalServiceProtos.CallMessage.Busy busy = content.getBusy();
|
||||
return SignalServiceCallMessage.forBusy(new BusyMessage(busy.getId()), isMultiRing, destinationDeviceId);
|
||||
} else if (content.hasOpaque()) {
|
||||
SignalServiceProtos.CallMessage.Opaque opaque = content.getOpaque();
|
||||
return SignalServiceCallMessage.forOpaque(new OpaqueMessage(opaque.getData().toByteArray()), isMultiRing, destinationDeviceId);
|
||||
}
|
||||
|
||||
return SignalServiceCallMessage.empty();
|
||||
|
@@ -0,0 +1,48 @@
|
||||
package org.whispersystems.signalservice.api.messages.calls;
|
||||
|
||||
/**
|
||||
* Encapsulate the response to an http request on behalf of ringrtc.
|
||||
*/
|
||||
public abstract class CallingResponse {
|
||||
private final long requestId;
|
||||
|
||||
CallingResponse(long requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
public long getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public static class Success extends CallingResponse {
|
||||
private final int responseStatus;
|
||||
private final byte[] responseBody;
|
||||
|
||||
public Success(long requestId, int responseStatus, byte[] responseBody) {
|
||||
super(requestId);
|
||||
this.responseStatus = responseStatus;
|
||||
this.responseBody = responseBody;
|
||||
}
|
||||
|
||||
public int getResponseStatus() {
|
||||
return responseStatus;
|
||||
}
|
||||
|
||||
public byte[] getResponseBody() {
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Error extends CallingResponse {
|
||||
private final Throwable throwable;
|
||||
|
||||
public Error(long requestId, Throwable throwable) {
|
||||
super(requestId);
|
||||
this.throwable = throwable;
|
||||
}
|
||||
|
||||
public Throwable getThrowable() {
|
||||
return throwable;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package org.whispersystems.signalservice.api.messages.calls;
|
||||
|
||||
public class OpaqueMessage {
|
||||
|
||||
private final byte[] opaque;
|
||||
|
||||
public OpaqueMessage(byte[] opaque) {
|
||||
this.opaque = opaque;
|
||||
}
|
||||
|
||||
public byte[] getOpaque() {
|
||||
return opaque;
|
||||
}
|
||||
}
|
@@ -12,6 +12,7 @@ public class SignalServiceCallMessage {
|
||||
private final Optional<HangupMessage> hangupMessage;
|
||||
private final Optional<BusyMessage> busyMessage;
|
||||
private final Optional<List<IceUpdateMessage>> iceUpdateMessages;
|
||||
private final Optional<OpaqueMessage> opaqueMessage;
|
||||
private final Optional<Integer> destinationDeviceId;
|
||||
private final boolean isMultiRing;
|
||||
|
||||
@@ -20,6 +21,7 @@ public class SignalServiceCallMessage {
|
||||
Optional<List<IceUpdateMessage>> iceUpdateMessages,
|
||||
Optional<HangupMessage> hangupMessage,
|
||||
Optional<BusyMessage> busyMessage,
|
||||
Optional<OpaqueMessage> opaqueMessage,
|
||||
boolean isMultiRing,
|
||||
Optional<Integer> destinationDeviceId)
|
||||
{
|
||||
@@ -28,6 +30,7 @@ public class SignalServiceCallMessage {
|
||||
this.iceUpdateMessages = iceUpdateMessages;
|
||||
this.hangupMessage = hangupMessage;
|
||||
this.busyMessage = busyMessage;
|
||||
this.opaqueMessage = opaqueMessage;
|
||||
this.isMultiRing = isMultiRing;
|
||||
this.destinationDeviceId = destinationDeviceId;
|
||||
}
|
||||
@@ -38,6 +41,7 @@ public class SignalServiceCallMessage {
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
isMultiRing,
|
||||
Optional.fromNullable(destinationDeviceId));
|
||||
}
|
||||
@@ -48,6 +52,7 @@ public class SignalServiceCallMessage {
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
isMultiRing,
|
||||
Optional.fromNullable(destinationDeviceId));
|
||||
}
|
||||
@@ -58,6 +63,7 @@ public class SignalServiceCallMessage {
|
||||
Optional.of(iceUpdateMessages),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
isMultiRing,
|
||||
Optional.fromNullable(destinationDeviceId));
|
||||
}
|
||||
@@ -71,6 +77,7 @@ public class SignalServiceCallMessage {
|
||||
Optional.of(iceUpdateMessages),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
isMultiRing,
|
||||
Optional.fromNullable(destinationDeviceId));
|
||||
}
|
||||
@@ -81,6 +88,7 @@ public class SignalServiceCallMessage {
|
||||
Optional.absent(),
|
||||
Optional.of(hangupMessage),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
isMultiRing,
|
||||
Optional.fromNullable(destinationDeviceId));
|
||||
}
|
||||
@@ -91,6 +99,18 @@ public class SignalServiceCallMessage {
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.of(busyMessage),
|
||||
Optional.absent(),
|
||||
isMultiRing,
|
||||
Optional.fromNullable(destinationDeviceId));
|
||||
}
|
||||
|
||||
public static SignalServiceCallMessage forOpaque(OpaqueMessage opaqueMessage, boolean isMultiRing, Integer destinationDeviceId) {
|
||||
return new SignalServiceCallMessage(Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.of(opaqueMessage),
|
||||
isMultiRing,
|
||||
Optional.fromNullable(destinationDeviceId));
|
||||
}
|
||||
@@ -102,7 +122,7 @@ public class SignalServiceCallMessage {
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
false,
|
||||
Optional.absent(), false,
|
||||
Optional.absent());
|
||||
}
|
||||
|
||||
@@ -126,6 +146,10 @@ public class SignalServiceCallMessage {
|
||||
return busyMessage;
|
||||
}
|
||||
|
||||
public Optional<OpaqueMessage> getOpaqueMessage() {
|
||||
return opaqueMessage;
|
||||
}
|
||||
|
||||
public boolean isMultiRing() {
|
||||
return isMultiRing;
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import org.signal.storageservice.protos.groups.AvatarUploadAttributes;
|
||||
import org.signal.storageservice.protos.groups.Group;
|
||||
import org.signal.storageservice.protos.groups.GroupChange;
|
||||
import org.signal.storageservice.protos.groups.GroupChanges;
|
||||
import org.signal.storageservice.protos.groups.GroupExternalCredential;
|
||||
import org.signal.storageservice.protos.groups.GroupJoinInfo;
|
||||
import org.signal.zkgroup.VerificationFailedException;
|
||||
import org.signal.zkgroup.profiles.ClientZkProfileOperations;
|
||||
@@ -38,6 +39,7 @@ import org.whispersystems.signalservice.api.groupsv2.CredentialResponse;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
|
||||
import org.whispersystems.signalservice.api.messages.calls.CallingResponse;
|
||||
import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
|
||||
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
|
||||
@@ -204,6 +206,7 @@ public class PushServiceSocket {
|
||||
private static final String GROUPSV2_GROUP_CHANGES = "/v1/groups/logs/%s";
|
||||
private static final String GROUPSV2_AVATAR_REQUEST = "/v1/groups/avatar/form";
|
||||
private static final String GROUPSV2_GROUP_JOIN = "/v1/groups/join/%s";
|
||||
private static final String GROUPSV2_TOKEN = "/v1/groups/token";
|
||||
|
||||
private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp";
|
||||
|
||||
@@ -1665,6 +1668,42 @@ public class PushServiceSocket {
|
||||
throw new NonSuccessfulResponseCodeException("Response: " + response);
|
||||
}
|
||||
|
||||
public CallingResponse makeCallingRequest(long requestId, String url, String httpMethod, List<Pair<String, String>> headers, byte[] body) {
|
||||
ConnectionHolder connectionHolder = getRandom(serviceClients, random);
|
||||
OkHttpClient okHttpClient = connectionHolder.getClient()
|
||||
.newBuilder()
|
||||
.followRedirects(true)
|
||||
.connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.build();
|
||||
|
||||
RequestBody requestBody = body != null ? RequestBody.create(null, body) : null;
|
||||
Request.Builder builder = new Request.Builder()
|
||||
.url(url)
|
||||
.method(httpMethod, requestBody);
|
||||
|
||||
if (headers != null) {
|
||||
for (Pair<String, String> header : headers) {
|
||||
builder.addHeader(header.first(), header.second());
|
||||
}
|
||||
}
|
||||
|
||||
Call call = okHttpClient.newCall(builder.build());
|
||||
|
||||
try {
|
||||
Response response = call.execute();
|
||||
int responseStatus = response.code();
|
||||
byte[] responseBody = response.body() != null ? response.body().bytes() : new byte[0];
|
||||
|
||||
return new CallingResponse.Success(requestId, responseStatus, responseBody);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Exception during ringrtc http call.", e);
|
||||
return new CallingResponse.Error(requestId, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private ServiceConnectionHolder[] createServiceConnectionHolders(SignalUrl[] urls,
|
||||
List<Interceptor> interceptors,
|
||||
Optional<Dns> dns)
|
||||
@@ -2037,6 +2076,18 @@ public class PushServiceSocket {
|
||||
return GroupJoinInfo.parseFrom(readBodyBytes(response));
|
||||
}
|
||||
|
||||
public GroupExternalCredential getGroupExternalCredential(GroupsV2AuthorizationString authorization)
|
||||
throws NonSuccessfulResponseCodeException, PushNetworkException, InvalidProtocolBufferException
|
||||
{
|
||||
ResponseBody response = makeStorageRequest(authorization.toString(),
|
||||
GROUPSV2_TOKEN,
|
||||
"GET",
|
||||
null,
|
||||
NO_HANDLER);
|
||||
|
||||
return GroupExternalCredential.parseFrom(readBodyBytes(response));
|
||||
}
|
||||
|
||||
public static final class GroupHistory {
|
||||
private final GroupChanges groupChanges;
|
||||
private final Optional<ContentRange> contentRange;
|
||||
|
@@ -210,3 +210,7 @@ message GroupJoinInfo {
|
||||
uint32 revision = 6;
|
||||
bool pendingAdminApproval = 7;
|
||||
}
|
||||
|
||||
message GroupExternalCredential {
|
||||
string token = 1;
|
||||
}
|
||||
|
@@ -92,6 +92,9 @@ message CallMessage {
|
||||
optional uint32 deviceId = 3;
|
||||
}
|
||||
|
||||
message Opaque {
|
||||
optional bytes data = 1;
|
||||
}
|
||||
|
||||
optional Offer offer = 1;
|
||||
optional Answer answer = 2;
|
||||
@@ -102,6 +105,7 @@ message CallMessage {
|
||||
optional Hangup hangup = 7;
|
||||
optional bool multiRing = 8;
|
||||
optional uint32 destinationDeviceId = 9;
|
||||
optional Opaque opaque = 10;
|
||||
}
|
||||
|
||||
message DataMessage {
|
||||
|
Reference in New Issue
Block a user