2016-11-09 09:37:40 -08:00
|
|
|
package org.thoughtcrime.securesms.service;
|
|
|
|
|
|
|
|
|
2017-11-24 22:00:30 -08:00
|
|
|
import android.Manifest;
|
2016-11-09 09:37:40 -08:00
|
|
|
import android.app.Service;
|
2017-03-04 15:48:10 -08:00
|
|
|
import android.content.BroadcastReceiver;
|
2017-02-05 12:38:08 -08:00
|
|
|
import android.content.Context;
|
2016-11-09 09:37:40 -08:00
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
import android.media.AudioManager;
|
2018-02-16 11:10:35 -08:00
|
|
|
import android.net.Uri;
|
2017-03-04 15:48:10 -08:00
|
|
|
import android.os.Build;
|
2017-02-05 12:38:08 -08:00
|
|
|
import android.os.Bundle;
|
2016-11-09 09:37:40 -08:00
|
|
|
import android.os.Handler;
|
2017-02-05 12:38:08 -08:00
|
|
|
import android.os.HandlerThread;
|
2016-11-09 09:37:40 -08:00
|
|
|
import android.os.IBinder;
|
2017-02-05 12:38:08 -08:00
|
|
|
import android.os.ResultReceiver;
|
2019-06-05 15:47:14 -04:00
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.annotation.WorkerThread;
|
2019-05-23 16:56:05 -03:00
|
|
|
import android.telephony.PhoneStateListener;
|
2016-11-09 09:37:40 -08:00
|
|
|
import android.telephony.TelephonyManager;
|
|
|
|
import android.util.Pair;
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
import org.signal.ringrtc.CallConnection;
|
|
|
|
import org.signal.ringrtc.CallConnection.CallError;
|
|
|
|
import org.signal.ringrtc.CallConnectionFactory;
|
|
|
|
import org.signal.ringrtc.CallException;
|
|
|
|
import org.signal.ringrtc.SignalMessageRecipient;
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2017-02-17 20:27:11 -08:00
|
|
|
import org.greenrobot.eventbus.EventBus;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.thoughtcrime.securesms.ApplicationContext;
|
|
|
|
import org.thoughtcrime.securesms.WebRtcCallActivity;
|
2017-02-17 13:12:48 -08:00
|
|
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
2017-07-26 09:59:15 -07:00
|
|
|
import org.thoughtcrime.securesms.database.Address;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
2018-02-16 11:10:35 -08:00
|
|
|
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
2019-07-15 11:12:26 -04:00
|
|
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
2017-02-02 18:34:51 -08:00
|
|
|
import org.thoughtcrime.securesms.events.WebRtcViewModel;
|
2019-05-23 16:56:05 -03:00
|
|
|
import org.thoughtcrime.securesms.logging.Log;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
2017-11-24 22:00:30 -08:00
|
|
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
2019-05-17 09:19:09 -07:00
|
|
|
import org.thoughtcrime.securesms.ringrtc.CameraState;
|
|
|
|
import org.thoughtcrime.securesms.ringrtc.MessageRecipient;
|
|
|
|
import org.thoughtcrime.securesms.ringrtc.CallConnectionWrapper;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
|
|
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
2019-05-23 16:56:05 -03:00
|
|
|
import org.thoughtcrime.securesms.util.TelephonyUtil;
|
2017-02-17 13:12:48 -08:00
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.thoughtcrime.securesms.util.Util;
|
2017-02-22 15:49:05 -08:00
|
|
|
import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder;
|
2017-03-14 13:24:24 -07:00
|
|
|
import org.thoughtcrime.securesms.webrtc.IncomingPstnCallReceiver;
|
|
|
|
import org.thoughtcrime.securesms.webrtc.UncaughtExceptionHandlerManager;
|
2017-03-04 15:48:10 -08:00
|
|
|
import org.thoughtcrime.securesms.webrtc.audio.BluetoothStateManager;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger;
|
2017-03-04 15:48:10 -08:00
|
|
|
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager;
|
2017-03-14 13:24:24 -07:00
|
|
|
import org.thoughtcrime.securesms.webrtc.locks.LockManager;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.webrtc.AudioTrack;
|
2019-01-29 14:25:49 -08:00
|
|
|
import org.webrtc.DefaultVideoDecoderFactory;
|
|
|
|
import org.webrtc.DefaultVideoEncoderFactory;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.webrtc.EglBase;
|
|
|
|
import org.webrtc.IceCandidate;
|
|
|
|
import org.webrtc.MediaStream;
|
|
|
|
import org.webrtc.SurfaceViewRenderer;
|
2019-01-29 14:25:49 -08:00
|
|
|
import org.webrtc.VideoDecoderFactory;
|
|
|
|
import org.webrtc.VideoEncoderFactory;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.webrtc.VideoTrack;
|
2017-02-02 18:34:51 -08:00
|
|
|
import org.whispersystems.libsignal.IdentityKey;
|
2019-05-17 09:19:09 -07:00
|
|
|
import org.whispersystems.libsignal.InvalidKeyException;
|
2016-11-09 09:37:40 -08:00
|
|
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
|
|
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
|
|
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
2017-02-02 18:34:51 -08:00
|
|
|
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
2017-02-05 12:38:08 -08:00
|
|
|
import org.whispersystems.signalservice.internal.util.concurrent.SettableFuture;
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
import java.lang.Thread;
|
2016-11-09 09:37:40 -08:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.security.SecureRandom;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.concurrent.ExecutionException;
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
2017-02-22 15:49:05 -08:00
|
|
|
import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_ESTABLISHED;
|
2017-11-27 12:18:14 -08:00
|
|
|
import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_INCOMING_CONNECTING;
|
2017-02-22 15:49:05 -08:00
|
|
|
import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_INCOMING_RINGING;
|
|
|
|
import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_OUTGOING_RINGING;
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
public class WebRtcCallService extends Service implements CallConnection.Observer,
|
2018-04-25 11:00:03 -07:00
|
|
|
BluetoothStateManager.BluetoothStateListener,
|
2019-05-17 09:19:09 -07:00
|
|
|
CallConnectionWrapper.CameraEventListener
|
2018-04-25 11:00:03 -07:00
|
|
|
{
|
2016-11-09 09:37:40 -08:00
|
|
|
|
|
|
|
private static final String TAG = WebRtcCallService.class.getSimpleName();
|
|
|
|
|
|
|
|
private enum CallState {
|
|
|
|
STATE_IDLE, STATE_DIALING, STATE_ANSWERING, STATE_REMOTE_RINGING, STATE_LOCAL_RINGING, STATE_CONNECTED
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final String DATA_CHANNEL_NAME = "signaling";
|
|
|
|
|
2017-07-26 09:59:15 -07:00
|
|
|
public static final String EXTRA_REMOTE_ADDRESS = "remote_address";
|
2016-11-09 09:37:40 -08:00
|
|
|
public static final String EXTRA_MUTE = "mute_value";
|
2017-03-04 15:48:10 -08:00
|
|
|
public static final String EXTRA_AVAILABLE = "enabled_value";
|
2016-11-09 09:37:40 -08:00
|
|
|
public static final String EXTRA_REMOTE_DESCRIPTION = "remote_description";
|
|
|
|
public static final String EXTRA_TIMESTAMP = "timestamp";
|
|
|
|
public static final String EXTRA_CALL_ID = "call_id";
|
|
|
|
public static final String EXTRA_ICE_SDP = "ice_sdp";
|
|
|
|
public static final String EXTRA_ICE_SDP_MID = "ice_sdp_mid";
|
|
|
|
public static final String EXTRA_ICE_SDP_LINE_INDEX = "ice_sdp_line_index";
|
2017-02-05 12:38:08 -08:00
|
|
|
public static final String EXTRA_RESULT_RECEIVER = "result_receiver";
|
2019-05-17 09:19:09 -07:00
|
|
|
public static final String EXTRA_CALL_ERROR = "call_error";
|
|
|
|
public static final String EXTRA_IDENTITY_KEY_BYTES = "identity_key_bytes";
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
public static final String ACTION_INCOMING_CALL = "CALL_INCOMING";
|
|
|
|
public static final String ACTION_OUTGOING_CALL = "CALL_OUTGOING";
|
|
|
|
public static final String ACTION_ANSWER_CALL = "ANSWER_CALL";
|
|
|
|
public static final String ACTION_DENY_CALL = "DENY_CALL";
|
|
|
|
public static final String ACTION_LOCAL_HANGUP = "LOCAL_HANGUP";
|
|
|
|
public static final String ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO";
|
|
|
|
public static final String ACTION_SET_MUTE_VIDEO = "SET_MUTE_VIDEO";
|
2018-04-25 11:00:03 -07:00
|
|
|
public static final String ACTION_FLIP_CAMERA = "FLIP_CAMERA";
|
2017-03-04 15:48:10 -08:00
|
|
|
public static final String ACTION_BLUETOOTH_CHANGE = "BLUETOOTH_CHANGE";
|
|
|
|
public static final String ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE";
|
2017-03-05 10:02:23 -08:00
|
|
|
public static final String ACTION_SCREEN_OFF = "SCREEN_OFF";
|
2017-03-04 15:48:10 -08:00
|
|
|
public static final String ACTION_CHECK_TIMEOUT = "CHECK_TIMEOUT";
|
|
|
|
public static final String ACTION_IS_IN_CALL_QUERY = "IS_IN_CALL";
|
2016-11-09 09:37:40 -08:00
|
|
|
|
|
|
|
public static final String ACTION_RESPONSE_MESSAGE = "RESPONSE_MESSAGE";
|
|
|
|
public static final String ACTION_ICE_MESSAGE = "ICE_MESSAGE";
|
|
|
|
public static final String ACTION_CALL_CONNECTED = "CALL_CONNECTED";
|
|
|
|
public static final String ACTION_REMOTE_HANGUP = "REMOTE_HANGUP";
|
|
|
|
public static final String ACTION_REMOTE_BUSY = "REMOTE_BUSY";
|
|
|
|
public static final String ACTION_REMOTE_VIDEO_MUTE = "REMOTE_VIDEO_MUTE";
|
2019-05-17 09:19:09 -07:00
|
|
|
public static final String ACTION_CALL_RINGING = "CALL_RINGING";
|
|
|
|
public static final String ACTION_CALL_ERROR = "CALL_ERROR";
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2018-04-25 11:00:03 -07:00
|
|
|
private CallState callState = CallState.STATE_IDLE;
|
|
|
|
private CameraState localCameraState = CameraState.UNKNOWN;
|
|
|
|
private boolean microphoneEnabled = true;
|
|
|
|
private boolean remoteVideoEnabled = false;
|
|
|
|
private boolean bluetoothAvailable = false;
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-07-15 11:12:26 -04:00
|
|
|
private SignalServiceMessageSender messageSender;
|
|
|
|
private SignalServiceAccountManager accountManager;
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
private SignalAudioManager audioManager;
|
|
|
|
private BluetoothStateManager bluetoothStateManager;
|
|
|
|
private WiredHeadsetStateReceiver wiredHeadsetStateReceiver;
|
2017-03-05 10:02:23 -08:00
|
|
|
private PowerButtonReceiver powerButtonReceiver;
|
2016-11-09 09:37:40 -08:00
|
|
|
private LockManager lockManager;
|
|
|
|
|
2017-02-05 12:38:08 -08:00
|
|
|
private IncomingPstnCallReceiver callReceiver;
|
|
|
|
private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager;
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
@Nullable private Long callId;
|
|
|
|
@Nullable private Recipient recipient;
|
2019-05-17 09:19:09 -07:00
|
|
|
@Nullable private CallConnectionWrapper callConnection;
|
|
|
|
@Nullable private CallConnectionFactory callConnectionFactory;
|
|
|
|
@Nullable private MessageRecipient messageRecipient;
|
|
|
|
@Nullable private List<IceCandidate> pendingRemoteIceUpdates;
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-01-29 14:25:49 -08:00
|
|
|
@Nullable private SurfaceViewRenderer localRenderer;
|
|
|
|
@Nullable private SurfaceViewRenderer remoteRenderer;
|
|
|
|
@Nullable private EglBase eglBase;
|
2016-11-09 09:37:40 -08:00
|
|
|
|
|
|
|
private ExecutorService serviceExecutor = Executors.newSingleThreadExecutor();
|
|
|
|
|
2019-05-23 16:56:05 -03:00
|
|
|
private final PhoneStateListener hangUpRtcOnDeviceCallAnswered = new HangUpRtcOnPstnCallAnsweredListener();
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
@Override
|
|
|
|
public void onCreate() {
|
|
|
|
super.onCreate();
|
2019-05-23 16:56:05 -03:00
|
|
|
Log.d(TAG, "onCreate");
|
2016-11-09 09:37:40 -08:00
|
|
|
|
|
|
|
initializeResources();
|
2017-02-05 12:38:08 -08:00
|
|
|
|
|
|
|
registerIncomingPstnCallReceiver();
|
2016-11-09 09:37:40 -08:00
|
|
|
registerUncaughtExceptionHandler();
|
2017-03-04 15:48:10 -08:00
|
|
|
registerWiredHeadsetStateReceiver();
|
2019-05-23 16:56:05 -03:00
|
|
|
|
|
|
|
TelephonyUtil.getManager(this)
|
|
|
|
.listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_CALL_STATE);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int onStartCommand(final Intent intent, int flags, int startId) {
|
2018-08-02 09:25:33 -04:00
|
|
|
Log.i(TAG, "onStartCommand...");
|
2016-11-09 09:37:40 -08:00
|
|
|
if (intent == null || intent.getAction() == null) return START_NOT_STICKY;
|
|
|
|
|
2017-12-15 09:45:00 -08:00
|
|
|
serviceExecutor.execute(() -> {
|
|
|
|
if (intent.getAction().equals(ACTION_INCOMING_CALL) && isBusy()) handleBusyCall(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_REMOTE_BUSY)) handleBusyMessage(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_INCOMING_CALL)) handleIncomingCall(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_OUTGOING_CALL) && isIdle()) handleOutgoingCall(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_ANSWER_CALL)) handleAnswerCall(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_DENY_CALL)) handleDenyCall(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_LOCAL_HANGUP)) handleLocalHangup(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_REMOTE_HANGUP)) handleRemoteHangup(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_SET_MUTE_AUDIO)) handleSetMuteAudio(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_SET_MUTE_VIDEO)) handleSetMuteVideo(intent);
|
2019-05-17 09:19:09 -07:00
|
|
|
else if (intent.getAction().equals(ACTION_FLIP_CAMERA)) handleSetCameraFlip(intent);
|
2017-12-15 09:45:00 -08:00
|
|
|
else if (intent.getAction().equals(ACTION_BLUETOOTH_CHANGE)) handleBluetoothChange(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_WIRED_HEADSET_CHANGE)) handleWiredHeadsetChange(intent);
|
2019-05-17 09:19:09 -07:00
|
|
|
else if (intent.getAction().equals(ACTION_SCREEN_OFF)) handleScreenOffChange(intent);
|
2017-12-15 09:45:00 -08:00
|
|
|
else if (intent.getAction().equals(ACTION_REMOTE_VIDEO_MUTE)) handleRemoteVideoMute(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_RESPONSE_MESSAGE)) handleResponseMessage(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_ICE_MESSAGE)) handleRemoteIceCandidate(intent);
|
2019-05-17 09:19:09 -07:00
|
|
|
else if (intent.getAction().equals(ACTION_CALL_RINGING)) handleCallRinging(intent);
|
2017-12-15 09:45:00 -08:00
|
|
|
else if (intent.getAction().equals(ACTION_CALL_CONNECTED)) handleCallConnected(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_CHECK_TIMEOUT)) handleCheckTimeout(intent);
|
|
|
|
else if (intent.getAction().equals(ACTION_IS_IN_CALL_QUERY)) handleIsInCallQuery(intent);
|
2019-05-17 09:19:09 -07:00
|
|
|
else if (intent.getAction().equals(ACTION_CALL_ERROR)) handleCallError(intent);
|
2016-11-09 09:37:40 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
return START_NOT_STICKY;
|
|
|
|
}
|
|
|
|
|
2017-02-05 12:38:08 -08:00
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
super.onDestroy();
|
2019-05-23 16:56:05 -03:00
|
|
|
Log.d(TAG, "onDestroy");
|
2017-02-05 12:38:08 -08:00
|
|
|
|
|
|
|
if (callReceiver != null) {
|
|
|
|
unregisterReceiver(callReceiver);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uncaughtExceptionHandlerManager != null) {
|
|
|
|
uncaughtExceptionHandlerManager.unregister();
|
|
|
|
}
|
2017-03-04 15:48:10 -08:00
|
|
|
|
|
|
|
if (bluetoothStateManager != null) {
|
|
|
|
bluetoothStateManager.onDestroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wiredHeadsetStateReceiver != null) {
|
|
|
|
unregisterReceiver(wiredHeadsetStateReceiver);
|
2017-03-05 10:02:23 -08:00
|
|
|
wiredHeadsetStateReceiver = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (powerButtonReceiver != null) {
|
|
|
|
unregisterReceiver(powerButtonReceiver);
|
|
|
|
powerButtonReceiver = null;
|
2017-03-04 15:48:10 -08:00
|
|
|
}
|
2019-05-23 16:56:05 -03:00
|
|
|
|
|
|
|
TelephonyUtil.getManager(this)
|
|
|
|
.listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_NONE);
|
2017-02-05 12:38:08 -08:00
|
|
|
}
|
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
@Override
|
|
|
|
public void onBluetoothStateChanged(boolean isAvailable) {
|
2018-08-02 09:25:33 -04:00
|
|
|
Log.i(TAG, "onBluetoothStateChanged: " + isAvailable);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
Intent intent = new Intent(this, WebRtcCallService.class);
|
|
|
|
intent.setAction(ACTION_BLUETOOTH_CHANGE);
|
|
|
|
intent.putExtra(EXTRA_AVAILABLE, isAvailable);
|
|
|
|
|
|
|
|
startService(intent);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
2018-04-25 11:00:03 -07:00
|
|
|
@Override
|
|
|
|
public void onCameraSwitchCompleted(@NonNull CameraState newCameraState) {
|
|
|
|
this.localCameraState = newCameraState;
|
|
|
|
if (recipient != null) {
|
|
|
|
sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
// Initializers
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
private void initializeResources() {
|
2019-07-15 11:12:26 -04:00
|
|
|
this.messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
|
|
|
this.accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
2016-11-09 09:37:40 -08:00
|
|
|
this.callState = CallState.STATE_IDLE;
|
|
|
|
this.lockManager = new LockManager(this);
|
2017-03-04 15:48:10 -08:00
|
|
|
this.audioManager = new SignalAudioManager(this);
|
|
|
|
this.bluetoothStateManager = new BluetoothStateManager(this, this);
|
2019-01-29 14:25:49 -08:00
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
this.messageSender.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10));
|
|
|
|
this.accountManager.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10));
|
|
|
|
}
|
|
|
|
|
2017-02-05 12:38:08 -08:00
|
|
|
private void registerIncomingPstnCallReceiver() {
|
|
|
|
callReceiver = new IncomingPstnCallReceiver();
|
|
|
|
registerReceiver(callReceiver, new IntentFilter("android.intent.action.PHONE_STATE"));
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
private void registerUncaughtExceptionHandler() {
|
2017-02-05 12:38:08 -08:00
|
|
|
uncaughtExceptionHandlerManager = new UncaughtExceptionHandlerManager();
|
2016-11-09 09:37:40 -08:00
|
|
|
uncaughtExceptionHandlerManager.registerHandler(new ProximityLockRelease(lockManager));
|
|
|
|
}
|
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
private void registerWiredHeadsetStateReceiver() {
|
|
|
|
wiredHeadsetStateReceiver = new WiredHeadsetStateReceiver();
|
|
|
|
|
|
|
|
String action;
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= 21) {
|
|
|
|
action = AudioManager.ACTION_HEADSET_PLUG;
|
|
|
|
} else {
|
|
|
|
action = Intent.ACTION_HEADSET_PLUG;
|
|
|
|
}
|
|
|
|
|
|
|
|
registerReceiver(wiredHeadsetStateReceiver, new IntentFilter(action));
|
|
|
|
}
|
|
|
|
|
2017-03-05 10:02:23 -08:00
|
|
|
private void registerPowerButtonReceiver() {
|
|
|
|
if (powerButtonReceiver == null) {
|
|
|
|
powerButtonReceiver = new PowerButtonReceiver();
|
|
|
|
|
|
|
|
registerReceiver(powerButtonReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void unregisterPowerButtonReceiver() {
|
|
|
|
if (powerButtonReceiver != null) {
|
|
|
|
unregisterReceiver(powerButtonReceiver);
|
|
|
|
|
|
|
|
powerButtonReceiver = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
// Handlers
|
|
|
|
|
|
|
|
private void handleIncomingCall(final Intent intent) {
|
|
|
|
if (callState != CallState.STATE_IDLE) throw new IllegalStateException("Incoming on non-idle");
|
|
|
|
|
|
|
|
final String offer = intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION);
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
this.callState = CallState.STATE_ANSWERING;
|
|
|
|
this.callId = intent.getLongExtra(EXTRA_CALL_ID, -1);
|
|
|
|
this.pendingRemoteIceUpdates = new LinkedList<>();
|
|
|
|
this.recipient = getRemoteRecipient(intent);
|
|
|
|
this.messageRecipient = new MessageRecipient(messageSender, recipient);
|
|
|
|
|
|
|
|
Log.i(TAG, "handleIncomingCall(): callId: " + callId);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
|
|
|
if (isIncomingMessageExpired(intent)) {
|
|
|
|
insertMissedCall(this.recipient, true);
|
|
|
|
terminate();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-27 12:18:14 -08:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
setCallInProgressNotification(TYPE_INCOMING_CONNECTING, this.recipient);
|
|
|
|
}
|
|
|
|
|
2019-08-28 09:16:51 -04:00
|
|
|
initializeVideo();
|
Add ringrtc support
Initial commit of the RingRTC Java interface implementation.
The implementation lives in an external .aar with the package
org.signal.ringrtc.
The package provides two high level objects of interest
=======================================================
org.signal.ringrtc.CallConnection -- represents the session of a call,
very similar to WebRTC's PeerConnection.
org.signal.ringrtc.CallConnectionFactory -- creates CallConnection
objects, very similar to WebRTC's PeerConnectionFactory.
The implementation interfaces with the Android application in a few
places:
==================================================================
src/org/thoughtcrime/securesms/ApplicationContext.java -- RingRTC
library initialization at application startup.
src/org/thoughtcrime/securesms/service/WebRtcCallService.java -- Call
creation and state machine.
src/org/thoughtcrime/securesms/ringrtc -- this package implements
interface classes needed by ringrtc and a CallConnectionWrapper helper
class.
The two interfaces needed so far are:
ringrtc/Logger.java
ringrtc/SignalMessageRecipient.java
The logger is self-explanatory, but SignalMessageRecipient is a little
more involved. SignalMessageRecipient encapsulates the Signal-Android
notion of "Recipient" and the mechanism for sending Signal Messages
related to audio/video calling.
The CallConnectionWrapper class is clone of the original
org.thoughtcrime.securesms.webrtc.PeerConnectionWrapper, suitably
modified to match the CallConnection interface.
This class continues to handle the Camera switching APIs, with that
portion of the code remaining unmodified from the original.
CallConnectionFactory Details
=============================
The primary public methods:
initialize() -- initialize the WebRTC library and RingRTC library.
The WebRTC initialization is lifted from the original Signal-Android
code.
createCallConnectionFactory() -- creates a CallConnectionFactory
object. Internally it creates a WebRTC PeerConnectionFactory object
and a RingRTC CallConnectionFactory object.
dispose() -- tears down the CallConnectionFactory object, including
the internal PeerConnectionFactory and RingRTC CallConnectionFactory.
createCallConnection() -- creates a CallConnection object, connecting
that with an application controlled CallConnection.Observer object.
This function takes a CallConnection.Configuration object to link the
CallConnection object with some application provided services, like
sending Signal protocol messages.
CallConnection Details
======================
This object is a subclass of WebRTC's PeerConnection class.
The primary public methods and objects:
CallConnection.Configuration
----------------------------
Configuration object used to parameterize a call. Notable members:
- SignalServiceMessageSender messageSender
- long callId
- org.signal.SignalMessageRecipient recipient
The 'accountManager' is used to fetch public information from the Signal
service, specifically used here to obtain the public Signal TURN
server details.
The 'callId' is a 64-bit pseudo-random number generated when the call
is initiated, used to identify the call through out its lifetime.
The "recipient' is an implementation of the
org.signal.SignalMessageRecipient interface, which encapsulates the
sending of Signal service messages to a recipient (remote peer) using
existing Signal protocol data structures.
The native library needs to be able to send Signal messages via the
service, but it does not have a native implementation to do so.
Instead the native code calls out to the client for sending Signal
messages. To accomplish this, the client implements the
org.signal.SignalMessageRecipient interface and passes an instance of
that in a CallConnection.Configuration object.
CallConnection
--------------
dispose() -- tears down the CallConnection object, including the
internal PeerConnection and RingRTC CallConnection.
sendOffer() -- initiates a call to a remote recipient. This is the
beginning of an outbound call.
validateResponse() -- checks an offer response recipient against the
originating call details.
handleOfferAnswer() -- handles the receipt of answer, which was a
response from an originating offer.
acceptOffer() -- accept an offer from a remote participant. This is
the begin of an incoming call.
answerCall() -- invoked when the call is completely established and
online.
hangUp() -- hang up the connection and shut things done. This is the
end of the call.
sendBusy() -- send the remote side an indication that the local side
is already in a call and the line is busy.
sendVideoStatus() -- send the current state of the local camera video
stream to the remote side.
CallConnection.Observer
-----------------------
Observer object, used by the RingRTC library to notify the client
application of important events and status changes. Similar in spirit
to WebRTC's PeerConnection.Observer.
Observer callbacks come in three flavors:
- state change notifications,
- on stream notifications
- errors conditions
For state notifications, the callback contains the callId, the
recipient and a CallConnection.CallEvent type.
For streams, the callback contains the callId, the
recipient and a org.webrtc.MediaStream.
For errors, the callback contains the callId, the recipient and an
exception type. The currently thrown exceptions include:
- UntrustedIdentityException
- UnregisteredUserException
- IOException
Signed-off-by: Curt Brune <curt@signal.org>
Updates to support ringrtc-android version 0.1.0.
* simplify logging interface
It is no longer necessary for the application to specify a Log object
as the library can log via the NDK directly.
* improve error handling and notification
In a number of places where ringrtc errors could occur, no
notification was ever sent to the user, nor was the UI cleaned up. It
would look like the app was in hung state.
This patch updates these situations to send the WebRtcViewModel a
NETWORK_FAILURE message.
* update handleIncomingCall() for lockManager and notification
During the conversion to RingRTC, the implementation of
handleIncomingCall() missed a couple of things:
-- updating the Phone state with the lockManager
-- sending a message to the viewModel
* log the callId in various handler methods
For debugging purposes it is very handy to have the callId present in
the log during the various call handler methods.
Signed-off-by: Curt Brune <curt@signal.org>
2019-05-17 09:19:09 -07:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
try {
|
|
|
|
boolean isSystemContact = false;
|
|
|
|
|
|
|
|
if (Permissions.hasAny(WebRtcCallService.this, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) {
|
|
|
|
isSystemContact = ContactAccessor.getInstance().isSystemContact(WebRtcCallService.this, recipient.getAddress().serialize());
|
Add ringrtc support
Initial commit of the RingRTC Java interface implementation.
The implementation lives in an external .aar with the package
org.signal.ringrtc.
The package provides two high level objects of interest
=======================================================
org.signal.ringrtc.CallConnection -- represents the session of a call,
very similar to WebRTC's PeerConnection.
org.signal.ringrtc.CallConnectionFactory -- creates CallConnection
objects, very similar to WebRTC's PeerConnectionFactory.
The implementation interfaces with the Android application in a few
places:
==================================================================
src/org/thoughtcrime/securesms/ApplicationContext.java -- RingRTC
library initialization at application startup.
src/org/thoughtcrime/securesms/service/WebRtcCallService.java -- Call
creation and state machine.
src/org/thoughtcrime/securesms/ringrtc -- this package implements
interface classes needed by ringrtc and a CallConnectionWrapper helper
class.
The two interfaces needed so far are:
ringrtc/Logger.java
ringrtc/SignalMessageRecipient.java
The logger is self-explanatory, but SignalMessageRecipient is a little
more involved. SignalMessageRecipient encapsulates the Signal-Android
notion of "Recipient" and the mechanism for sending Signal Messages
related to audio/video calling.
The CallConnectionWrapper class is clone of the original
org.thoughtcrime.securesms.webrtc.PeerConnectionWrapper, suitably
modified to match the CallConnection interface.
This class continues to handle the Camera switching APIs, with that
portion of the code remaining unmodified from the original.
CallConnectionFactory Details
=============================
The primary public methods:
initialize() -- initialize the WebRTC library and RingRTC library.
The WebRTC initialization is lifted from the original Signal-Android
code.
createCallConnectionFactory() -- creates a CallConnectionFactory
object. Internally it creates a WebRTC PeerConnectionFactory object
and a RingRTC CallConnectionFactory object.
dispose() -- tears down the CallConnectionFactory object, including
the internal PeerConnectionFactory and RingRTC CallConnectionFactory.
createCallConnection() -- creates a CallConnection object, connecting
that with an application controlled CallConnection.Observer object.
This function takes a CallConnection.Configuration object to link the
CallConnection object with some application provided services, like
sending Signal protocol messages.
CallConnection Details
======================
This object is a subclass of WebRTC's PeerConnection class.
The primary public methods and objects:
CallConnection.Configuration
----------------------------
Configuration object used to parameterize a call. Notable members:
- SignalServiceMessageSender messageSender
- long callId
- org.signal.SignalMessageRecipient recipient
The 'accountManager' is used to fetch public information from the Signal
service, specifically used here to obtain the public Signal TURN
server details.
The 'callId' is a 64-bit pseudo-random number generated when the call
is initiated, used to identify the call through out its lifetime.
The "recipient' is an implementation of the
org.signal.SignalMessageRecipient interface, which encapsulates the
sending of Signal service messages to a recipient (remote peer) using
existing Signal protocol data structures.
The native library needs to be able to send Signal messages via the
service, but it does not have a native implementation to do so.
Instead the native code calls out to the client for sending Signal
messages. To accomplish this, the client implements the
org.signal.SignalMessageRecipient interface and passes an instance of
that in a CallConnection.Configuration object.
CallConnection
--------------
dispose() -- tears down the CallConnection object, including the
internal PeerConnection and RingRTC CallConnection.
sendOffer() -- initiates a call to a remote recipient. This is the
beginning of an outbound call.
validateResponse() -- checks an offer response recipient against the
originating call details.
handleOfferAnswer() -- handles the receipt of answer, which was a
response from an originating offer.
acceptOffer() -- accept an offer from a remote participant. This is
the begin of an incoming call.
answerCall() -- invoked when the call is completely established and
online.
hangUp() -- hang up the connection and shut things done. This is the
end of the call.
sendBusy() -- send the remote side an indication that the local side
is already in a call and the line is busy.
sendVideoStatus() -- send the current state of the local camera video
stream to the remote side.
CallConnection.Observer
-----------------------
Observer object, used by the RingRTC library to notify the client
application of important events and status changes. Similar in spirit
to WebRTC's PeerConnection.Observer.
Observer callbacks come in three flavors:
- state change notifications,
- on stream notifications
- errors conditions
For state notifications, the callback contains the callId, the
recipient and a CallConnection.CallEvent type.
For streams, the callback contains the callId, the
recipient and a org.webrtc.MediaStream.
For errors, the callback contains the callId, the recipient and an
exception type. The currently thrown exceptions include:
- UntrustedIdentityException
- UnregisteredUserException
- IOException
Signed-off-by: Curt Brune <curt@signal.org>
Updates to support ringrtc-android version 0.1.0.
* simplify logging interface
It is no longer necessary for the application to specify a Log object
as the library can log via the NDK directly.
* improve error handling and notification
In a number of places where ringrtc errors could occur, no
notification was ever sent to the user, nor was the UI cleaned up. It
would look like the app was in hung state.
This patch updates these situations to send the WebRtcViewModel a
NETWORK_FAILURE message.
* update handleIncomingCall() for lockManager and notification
During the conversion to RingRTC, the implementation of
handleIncomingCall() missed a couple of things:
-- updating the Phone state with the lockManager
-- sending a message to the viewModel
* log the callId in various handler methods
For debugging purposes it is very handy to have the callId present in
the log during the various call handler methods.
Signed-off-by: Curt Brune <curt@signal.org>
2019-05-17 09:19:09 -07:00
|
|
|
}
|
2019-05-17 09:19:09 -07:00
|
|
|
|
|
|
|
boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this);
|
|
|
|
boolean hideIp = !isSystemContact || isAlwaysTurn;
|
|
|
|
|
|
|
|
this.callConnection = new CallConnectionWrapper(WebRtcCallService.this,
|
|
|
|
callConnectionFactory,
|
|
|
|
WebRtcCallService.this,
|
|
|
|
localRenderer,
|
|
|
|
WebRtcCallService.this,
|
|
|
|
eglBase,
|
|
|
|
hideIp,
|
|
|
|
callId,
|
|
|
|
false,
|
|
|
|
messageRecipient,
|
|
|
|
accountManager);
|
|
|
|
|
|
|
|
for (IceCandidate candidate : this.pendingRemoteIceUpdates) {
|
|
|
|
this.callConnection.addIceCandidate(candidate);
|
|
|
|
}
|
|
|
|
this.pendingRemoteIceUpdates = null;
|
|
|
|
|
|
|
|
this.localCameraState = callConnection.getCameraState();
|
|
|
|
this.lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING);
|
|
|
|
this.callConnection.acceptOffer(offer);
|
|
|
|
|
|
|
|
sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
} catch (UnregisteredUserException e) {
|
|
|
|
sendMessage(WebRtcViewModel.State.NO_SUCH_USER, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
terminate();
|
|
|
|
} catch (IOException e) {
|
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
terminate();
|
|
|
|
} catch (CallException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
terminate();
|
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void handleOutgoingCall(Intent intent) {
|
2019-08-28 09:16:51 -04:00
|
|
|
if (callState != CallState.STATE_IDLE) throw new IllegalStateException("Dialing from non-idle?");
|
Add ringrtc support
Initial commit of the RingRTC Java interface implementation.
The implementation lives in an external .aar with the package
org.signal.ringrtc.
The package provides two high level objects of interest
=======================================================
org.signal.ringrtc.CallConnection -- represents the session of a call,
very similar to WebRTC's PeerConnection.
org.signal.ringrtc.CallConnectionFactory -- creates CallConnection
objects, very similar to WebRTC's PeerConnectionFactory.
The implementation interfaces with the Android application in a few
places:
==================================================================
src/org/thoughtcrime/securesms/ApplicationContext.java -- RingRTC
library initialization at application startup.
src/org/thoughtcrime/securesms/service/WebRtcCallService.java -- Call
creation and state machine.
src/org/thoughtcrime/securesms/ringrtc -- this package implements
interface classes needed by ringrtc and a CallConnectionWrapper helper
class.
The two interfaces needed so far are:
ringrtc/Logger.java
ringrtc/SignalMessageRecipient.java
The logger is self-explanatory, but SignalMessageRecipient is a little
more involved. SignalMessageRecipient encapsulates the Signal-Android
notion of "Recipient" and the mechanism for sending Signal Messages
related to audio/video calling.
The CallConnectionWrapper class is clone of the original
org.thoughtcrime.securesms.webrtc.PeerConnectionWrapper, suitably
modified to match the CallConnection interface.
This class continues to handle the Camera switching APIs, with that
portion of the code remaining unmodified from the original.
CallConnectionFactory Details
=============================
The primary public methods:
initialize() -- initialize the WebRTC library and RingRTC library.
The WebRTC initialization is lifted from the original Signal-Android
code.
createCallConnectionFactory() -- creates a CallConnectionFactory
object. Internally it creates a WebRTC PeerConnectionFactory object
and a RingRTC CallConnectionFactory object.
dispose() -- tears down the CallConnectionFactory object, including
the internal PeerConnectionFactory and RingRTC CallConnectionFactory.
createCallConnection() -- creates a CallConnection object, connecting
that with an application controlled CallConnection.Observer object.
This function takes a CallConnection.Configuration object to link the
CallConnection object with some application provided services, like
sending Signal protocol messages.
CallConnection Details
======================
This object is a subclass of WebRTC's PeerConnection class.
The primary public methods and objects:
CallConnection.Configuration
----------------------------
Configuration object used to parameterize a call. Notable members:
- SignalServiceMessageSender messageSender
- long callId
- org.signal.SignalMessageRecipient recipient
The 'accountManager' is used to fetch public information from the Signal
service, specifically used here to obtain the public Signal TURN
server details.
The 'callId' is a 64-bit pseudo-random number generated when the call
is initiated, used to identify the call through out its lifetime.
The "recipient' is an implementation of the
org.signal.SignalMessageRecipient interface, which encapsulates the
sending of Signal service messages to a recipient (remote peer) using
existing Signal protocol data structures.
The native library needs to be able to send Signal messages via the
service, but it does not have a native implementation to do so.
Instead the native code calls out to the client for sending Signal
messages. To accomplish this, the client implements the
org.signal.SignalMessageRecipient interface and passes an instance of
that in a CallConnection.Configuration object.
CallConnection
--------------
dispose() -- tears down the CallConnection object, including the
internal PeerConnection and RingRTC CallConnection.
sendOffer() -- initiates a call to a remote recipient. This is the
beginning of an outbound call.
validateResponse() -- checks an offer response recipient against the
originating call details.
handleOfferAnswer() -- handles the receipt of answer, which was a
response from an originating offer.
acceptOffer() -- accept an offer from a remote participant. This is
the begin of an incoming call.
answerCall() -- invoked when the call is completely established and
online.
hangUp() -- hang up the connection and shut things done. This is the
end of the call.
sendBusy() -- send the remote side an indication that the local side
is already in a call and the line is busy.
sendVideoStatus() -- send the current state of the local camera video
stream to the remote side.
CallConnection.Observer
-----------------------
Observer object, used by the RingRTC library to notify the client
application of important events and status changes. Similar in spirit
to WebRTC's PeerConnection.Observer.
Observer callbacks come in three flavors:
- state change notifications,
- on stream notifications
- errors conditions
For state notifications, the callback contains the callId, the
recipient and a CallConnection.CallEvent type.
For streams, the callback contains the callId, the
recipient and a org.webrtc.MediaStream.
For errors, the callback contains the callId, the recipient and an
exception type. The currently thrown exceptions include:
- UntrustedIdentityException
- UnregisteredUserException
- IOException
Signed-off-by: Curt Brune <curt@signal.org>
Updates to support ringrtc-android version 0.1.0.
* simplify logging interface
It is no longer necessary for the application to specify a Log object
as the library can log via the NDK directly.
* improve error handling and notification
In a number of places where ringrtc errors could occur, no
notification was ever sent to the user, nor was the UI cleaned up. It
would look like the app was in hung state.
This patch updates these situations to send the WebRtcViewModel a
NETWORK_FAILURE message.
* update handleIncomingCall() for lockManager and notification
During the conversion to RingRTC, the implementation of
handleIncomingCall() missed a couple of things:
-- updating the Phone state with the lockManager
-- sending a message to the viewModel
* log the callId in various handler methods
For debugging purposes it is very handy to have the callId present in
the log during the various call handler methods.
Signed-off-by: Curt Brune <curt@signal.org>
2019-05-17 09:19:09 -07:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
this.callState = CallState.STATE_DIALING;
|
|
|
|
this.recipient = getRemoteRecipient(intent);
|
|
|
|
this.messageRecipient = new MessageRecipient(messageSender, recipient);
|
|
|
|
this.callId = new SecureRandom().nextLong();
|
|
|
|
|
|
|
|
Log.i(TAG, "handleOutgoingCall() callId: " + callId);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-03-15 15:31:52 -07:00
|
|
|
initializeVideo();
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-03-15 15:31:52 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.CALL_OUTGOING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL);
|
|
|
|
audioManager.initializeAudioForCall();
|
|
|
|
audioManager.startOutgoingRinger(OutgoingRinger.Type.RINGING);
|
|
|
|
bluetoothStateManager.setWantsConnection(true);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-03-15 15:31:52 -07:00
|
|
|
setCallInProgressNotification(TYPE_OUTGOING_RINGING, recipient);
|
|
|
|
DatabaseFactory.getSmsDatabase(this).insertOutgoingCall(recipient.getAddress());
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
try {
|
|
|
|
this.callConnection = new CallConnectionWrapper(WebRtcCallService.this,
|
|
|
|
callConnectionFactory,
|
|
|
|
WebRtcCallService.this,
|
|
|
|
localRenderer,
|
|
|
|
WebRtcCallService.this,
|
|
|
|
eglBase,
|
|
|
|
TextSecurePreferences.isTurnOnly(WebRtcCallService.this),
|
|
|
|
callId,
|
|
|
|
true,
|
|
|
|
messageRecipient,
|
|
|
|
accountManager);
|
|
|
|
|
|
|
|
this.localCameraState = callConnection.getCameraState();
|
|
|
|
this.callConnection.sendOffer();
|
|
|
|
} catch (UnregisteredUserException e) {
|
|
|
|
sendMessage(WebRtcViewModel.State.NO_SUCH_USER, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
terminate();
|
|
|
|
} catch (IOException e) {
|
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
terminate();
|
|
|
|
} catch (CallException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
terminate();
|
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void handleResponseMessage(Intent intent) {
|
2019-05-17 09:19:09 -07:00
|
|
|
long callId = getCallId(intent);
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
try {
|
2019-05-17 09:19:09 -07:00
|
|
|
Log.i(TAG, "handleResponseMessage(): callId: " + callId +
|
|
|
|
" Got response: " + intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION));
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
if (this.callConnection == null) {
|
|
|
|
Log.i(TAG, "Received stale answer while no call in progress");
|
2016-11-09 09:37:40 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
MessageRecipient messageRecipient = new MessageRecipient(messageSender, getRemoteRecipient(intent));
|
|
|
|
if (!this.callConnection.validateResponse(messageRecipient, callId)) {
|
|
|
|
Log.w(TAG, "Received answer for recipient and call id for inactive call: callId: " + callId);
|
|
|
|
return;
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
this.callConnection.handleOfferAnswer(intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION));
|
2017-07-12 10:59:10 -07:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
} catch (CallException e) {
|
2016-11-09 09:37:40 -08:00
|
|
|
Log.w(TAG, e);
|
2019-05-17 09:19:09 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2016-11-09 09:37:40 -08:00
|
|
|
terminate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleRemoteIceCandidate(Intent intent) {
|
2019-05-17 09:19:09 -07:00
|
|
|
long callId = getCallId(intent);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
Log.i(TAG, "handleRemoteIceCandidate(): callId: " + callId);
|
|
|
|
|
|
|
|
if (Util.isEquals(this.callId, callId)) {
|
2017-07-12 11:15:28 -07:00
|
|
|
IceCandidate candidate = new IceCandidate(intent.getStringExtra(EXTRA_ICE_SDP_MID),
|
|
|
|
intent.getIntExtra(EXTRA_ICE_SDP_LINE_INDEX, 0),
|
|
|
|
intent.getStringExtra(EXTRA_ICE_SDP));
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
if (this.callConnection != null) {
|
|
|
|
this.callConnection.addIceCandidate(candidate);
|
|
|
|
} else if (this.pendingRemoteIceUpdates != null) {
|
|
|
|
this.pendingRemoteIceUpdates.add(candidate);
|
|
|
|
}
|
2019-08-28 09:16:51 -04:00
|
|
|
|
2017-07-12 10:59:10 -07:00
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
private void handleCallRinging(Intent intent) {
|
|
|
|
Log.i(TAG, "handleCallRinging(): state: " + callState.toString());
|
2016-11-09 09:37:40 -08:00
|
|
|
if (callState == CallState.STATE_ANSWERING) {
|
2019-05-17 09:19:09 -07:00
|
|
|
Log.i(TAG, "handleCallRinging(): STATE_ANSWERING");
|
2016-11-09 09:37:40 -08:00
|
|
|
if (this.recipient == null) throw new AssertionError("assert");
|
|
|
|
|
|
|
|
this.callState = CallState.STATE_LOCAL_RINGING;
|
|
|
|
this.lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE);
|
|
|
|
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.CALL_INCOMING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2019-06-27 15:17:14 -04:00
|
|
|
startCallCardActivityIfPossible();
|
2017-03-04 15:48:10 -08:00
|
|
|
audioManager.initializeAudioForCall();
|
2018-02-16 11:10:35 -08:00
|
|
|
|
|
|
|
if (TextSecurePreferences.isCallNotificationsEnabled(this)) {
|
|
|
|
Uri ringtone = recipient.resolve().getCallRingtone();
|
|
|
|
VibrateState vibrateState = recipient.resolve().getCallVibrate();
|
|
|
|
|
|
|
|
if (ringtone == null) ringtone = TextSecurePreferences.getCallNotificationRingtone(this);
|
|
|
|
|
|
|
|
audioManager.startIncomingRinger(ringtone, vibrateState == VibrateState.ENABLED || (vibrateState == VibrateState.DEFAULT && TextSecurePreferences.isCallNotificationVibrateEnabled(this)));
|
|
|
|
}
|
2017-03-04 15:48:10 -08:00
|
|
|
|
2017-03-05 10:02:23 -08:00
|
|
|
registerPowerButtonReceiver();
|
|
|
|
|
2017-02-22 15:49:05 -08:00
|
|
|
setCallInProgressNotification(TYPE_INCOMING_RINGING, recipient);
|
2016-11-09 09:37:40 -08:00
|
|
|
} else if (callState == CallState.STATE_DIALING) {
|
2019-05-17 09:19:09 -07:00
|
|
|
Log.i(TAG, "handleCallRinging(): STATE_DIALING");
|
2016-11-09 09:37:40 -08:00
|
|
|
if (this.recipient == null) throw new AssertionError("assert");
|
|
|
|
|
|
|
|
this.callState = CallState.STATE_REMOTE_RINGING;
|
|
|
|
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.CALL_RINGING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleCallConnected(Intent intent) {
|
2019-05-17 09:19:09 -07:00
|
|
|
Log.i(TAG, "handleCallConnected()");
|
2016-11-09 09:37:40 -08:00
|
|
|
if (callState != CallState.STATE_REMOTE_RINGING && callState != CallState.STATE_LOCAL_RINGING) {
|
|
|
|
Log.w(TAG, "Ignoring call connected for unknown state: " + callState);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Util.isEquals(this.callId, getCallId(intent))) {
|
|
|
|
Log.w(TAG, "Ignoring connected for unknown call id: " + getCallId(intent));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
if (recipient == null || this.callConnection == null) {
|
2016-11-09 09:37:40 -08:00
|
|
|
throw new AssertionError("assert");
|
|
|
|
}
|
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
audioManager.startCommunication(callState == CallState.STATE_REMOTE_RINGING);
|
|
|
|
bluetoothStateManager.setWantsConnection(true);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
callState = CallState.STATE_CONNECTED;
|
2017-02-18 16:59:45 -08:00
|
|
|
|
2018-04-25 11:00:03 -07:00
|
|
|
if (localCameraState.isEnabled()) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO);
|
|
|
|
else lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.CALL_CONNECTED, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2017-03-05 10:02:23 -08:00
|
|
|
unregisterPowerButtonReceiver();
|
|
|
|
|
2017-02-22 15:49:05 -08:00
|
|
|
setCallInProgressNotification(TYPE_ESTABLISHED, recipient);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
try {
|
|
|
|
this.callConnection.setCommunicationMode();
|
|
|
|
this.callConnection.setAudioEnabled(microphoneEnabled);
|
|
|
|
this.callConnection.setVideoEnabled(localCameraState.isEnabled());
|
|
|
|
} catch (CallException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
terminate();
|
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleBusyCall(Intent intent) {
|
|
|
|
Recipient recipient = getRemoteRecipient(intent);
|
|
|
|
long callId = getCallId(intent);
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
Log.i(TAG, "handleBusyCall() callId: " + callId);
|
|
|
|
|
2017-12-15 09:45:00 -08:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
switch (callState) {
|
|
|
|
case STATE_DIALING:
|
|
|
|
case STATE_REMOTE_RINGING: setCallInProgressNotification(TYPE_OUTGOING_RINGING, this.recipient); break;
|
2018-06-21 15:33:54 -07:00
|
|
|
case STATE_IDLE: setCallInProgressNotification(TYPE_INCOMING_CONNECTING, recipient); break;
|
2017-12-15 09:45:00 -08:00
|
|
|
case STATE_ANSWERING: setCallInProgressNotification(TYPE_INCOMING_CONNECTING, this.recipient); break;
|
|
|
|
case STATE_LOCAL_RINGING: setCallInProgressNotification(TYPE_INCOMING_RINGING, this.recipient); break;
|
|
|
|
case STATE_CONNECTED: setCallInProgressNotification(TYPE_ESTABLISHED, this.recipient); break;
|
|
|
|
default: throw new AssertionError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-19 10:57:34 -08:00
|
|
|
if (callState == CallState.STATE_IDLE) {
|
|
|
|
stopForeground(true);
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
try {
|
|
|
|
MessageRecipient messageRecipient = new MessageRecipient(messageSender, recipient);
|
|
|
|
this.callConnection.sendBusy(messageRecipient, callId);
|
|
|
|
} catch (CallException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
insertMissedCall(getRemoteRecipient(intent), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleBusyMessage(Intent intent) {
|
2017-06-01 13:11:48 -07:00
|
|
|
final Recipient recipient = getRemoteRecipient(intent);
|
|
|
|
final long callId = getCallId(intent);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
Log.i(TAG, "handleBusyMessage(): callId: " + callId);
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
if (callState != CallState.STATE_DIALING || !Util.isEquals(this.callId, callId) || !recipient.equals(this.recipient)) {
|
|
|
|
Log.w(TAG, "Got busy message for inactive session...");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.CALL_BUSY, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
audioManager.startOutgoingRinger(OutgoingRinger.Type.BUSY);
|
2017-09-09 23:46:48 -07:00
|
|
|
Util.runOnMainDelayed(new Runnable() {
|
2016-11-09 09:37:40 -08:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2017-06-01 13:11:48 -07:00
|
|
|
Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class);
|
|
|
|
intent.setAction(ACTION_LOCAL_HANGUP);
|
|
|
|
intent.putExtra(EXTRA_CALL_ID, intent.getLongExtra(EXTRA_CALL_ID, -1));
|
2017-07-26 09:59:15 -07:00
|
|
|
intent.putExtra(EXTRA_REMOTE_ADDRESS, intent.getStringExtra(EXTRA_REMOTE_ADDRESS));
|
2017-06-01 13:11:48 -07:00
|
|
|
|
|
|
|
startService(intent);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
}, WebRtcCallActivity.BUSY_SIGNAL_DELAY_FINISH);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleCheckTimeout(Intent intent) {
|
|
|
|
if (this.callId != null &&
|
|
|
|
this.callId == intent.getLongExtra(EXTRA_CALL_ID, -1) &&
|
|
|
|
this.callState != CallState.STATE_CONNECTED)
|
|
|
|
{
|
|
|
|
Log.w(TAG, "Timing out call: " + this.callId);
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2017-03-08 20:42:00 +01:00
|
|
|
|
|
|
|
if (this.callState == CallState.STATE_ANSWERING || this.callState == CallState.STATE_LOCAL_RINGING) {
|
|
|
|
insertMissedCall(this.recipient, true);
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
terminate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-05 12:38:08 -08:00
|
|
|
private void handleIsInCallQuery(Intent intent) {
|
|
|
|
ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER);
|
|
|
|
|
|
|
|
if (resultReceiver != null) {
|
|
|
|
resultReceiver.send(callState != CallState.STATE_IDLE ? 1 : 0, null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
private void insertMissedCall(@NonNull Recipient recipient, boolean signal) {
|
2017-07-26 09:59:15 -07:00
|
|
|
Pair<Long, Long> messageAndThreadId = DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getAddress());
|
2018-01-24 19:17:44 -08:00
|
|
|
MessageNotifier.updateNotification(this, messageAndThreadId.second, signal);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void handleAnswerCall(Intent intent) {
|
|
|
|
if (callState != CallState.STATE_LOCAL_RINGING) {
|
|
|
|
Log.w(TAG, "Can only answer from ringing!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
if (this.callConnection == null || recipient == null || callId == null) {
|
2016-11-09 09:37:40 -08:00
|
|
|
throw new AssertionError("assert");
|
|
|
|
}
|
|
|
|
|
2017-07-26 09:59:15 -07:00
|
|
|
DatabaseFactory.getSmsDatabase(this).insertReceivedCall(recipient.getAddress());
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
try {
|
|
|
|
Log.i(TAG, "handleAnswerCall()");
|
|
|
|
this.callConnection.answerCall();
|
|
|
|
intent.putExtra(EXTRA_CALL_ID, callId);
|
|
|
|
intent.putExtra(EXTRA_REMOTE_ADDRESS, recipient.getAddress());
|
|
|
|
handleCallConnected(intent);
|
|
|
|
} catch (CallException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
terminate();
|
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleDenyCall(Intent intent) {
|
|
|
|
if (callState != CallState.STATE_LOCAL_RINGING) {
|
|
|
|
Log.w(TAG, "Can only deny from ringing!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
if (recipient == null || callId == null) {
|
2016-11-09 09:37:40 -08:00
|
|
|
throw new AssertionError("assert");
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
try {
|
|
|
|
Log.i(TAG, "handleDenyCall()");
|
|
|
|
this.callConnection.hangUp();
|
|
|
|
DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getAddress());
|
|
|
|
} catch (CallException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
2019-08-28 09:16:51 -04:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
terminate();
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void handleLocalHangup(Intent intent) {
|
2019-05-17 09:19:09 -07:00
|
|
|
if (this.recipient != null && this.callId != null) {
|
2016-11-09 09:37:40 -08:00
|
|
|
this.accountManager.cancelInFlightRequests();
|
|
|
|
this.messageSender.cancelInFlightRequests();
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
try {
|
|
|
|
Log.i(TAG, "handleLocalHangup()");
|
|
|
|
this.callConnection.hangUp();
|
|
|
|
sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
} catch (CallException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
terminate();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleRemoteHangup(Intent intent) {
|
2019-05-17 09:19:09 -07:00
|
|
|
|
|
|
|
long callId = getCallId(intent);
|
|
|
|
Log.i(TAG, "handleRemoteHangup(): callId: " + callId);
|
|
|
|
|
|
|
|
if (!Util.isEquals(this.callId, callId)) {
|
2016-11-09 09:37:40 -08:00
|
|
|
Log.w(TAG, "hangup for non-active call...");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.recipient == null) {
|
|
|
|
throw new AssertionError("assert");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.callState == CallState.STATE_DIALING || this.callState == CallState.STATE_REMOTE_RINGING) {
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.RECIPIENT_UNAVAILABLE, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2016-11-09 09:37:40 -08:00
|
|
|
} else {
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this.callState == CallState.STATE_ANSWERING || this.callState == CallState.STATE_LOCAL_RINGING) {
|
|
|
|
insertMissedCall(this.recipient, true);
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
terminate();
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void handleSetMuteAudio(Intent intent) {
|
|
|
|
boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false);
|
2017-03-04 15:48:10 -08:00
|
|
|
this.microphoneEnabled = !muted;
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
if (this.callConnection != null) {
|
|
|
|
this.callConnection.setAudioEnabled(this.microphoneEnabled);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
2019-05-06 18:10:27 -03:00
|
|
|
|
|
|
|
if (recipient != null) {
|
|
|
|
sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void handleSetMuteVideo(Intent intent) {
|
2017-03-04 15:48:10 -08:00
|
|
|
AudioManager audioManager = ServiceUtil.getAudioManager(this);
|
|
|
|
boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false);
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
if (this.callConnection != null) {
|
|
|
|
try {
|
|
|
|
this.callConnection.setVideoEnabled(!muted);
|
|
|
|
this.localCameraState = this.callConnection.getCameraState();
|
|
|
|
} catch (CallException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
terminate();
|
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
2017-02-02 18:53:45 -08:00
|
|
|
|
2017-02-18 16:59:45 -08:00
|
|
|
if (callState == CallState.STATE_CONNECTED) {
|
2018-04-25 11:00:03 -07:00
|
|
|
if (localCameraState.isEnabled()) this.lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO);
|
|
|
|
else this.lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL);
|
2017-02-18 16:59:45 -08:00
|
|
|
}
|
|
|
|
|
2018-04-25 11:00:03 -07:00
|
|
|
if (localCameraState.isEnabled() &&
|
2017-03-24 11:11:48 -07:00
|
|
|
!audioManager.isSpeakerphoneOn() &&
|
|
|
|
!audioManager.isBluetoothScoOn() &&
|
|
|
|
!audioManager.isWiredHeadsetOn())
|
|
|
|
{
|
2017-03-04 15:48:10 -08:00
|
|
|
audioManager.setSpeakerphoneOn(true);
|
|
|
|
}
|
|
|
|
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(viewModelStateFor(callState), this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2017-03-04 15:48:10 -08:00
|
|
|
}
|
|
|
|
|
2018-03-23 20:31:03 +01:00
|
|
|
private void handleSetCameraFlip(Intent intent) {
|
2018-08-02 09:25:33 -04:00
|
|
|
Log.i(TAG, "handleSetCameraFlip()...");
|
2018-03-23 20:31:03 +01:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
if (localCameraState.isEnabled() && this.callConnection != null) {
|
|
|
|
this.callConnection.flipCamera();
|
|
|
|
localCameraState = this.callConnection.getCameraState();
|
2018-04-25 11:00:03 -07:00
|
|
|
if (recipient != null) {
|
|
|
|
sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2018-03-23 20:31:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
private void handleBluetoothChange(Intent intent) {
|
|
|
|
this.bluetoothAvailable = intent.getBooleanExtra(EXTRA_AVAILABLE, false);
|
2017-03-05 10:02:23 -08:00
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
if (recipient != null) {
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2017-03-04 15:48:10 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleWiredHeadsetChange(Intent intent) {
|
2018-08-02 09:25:33 -04:00
|
|
|
Log.i(TAG, "handleWiredHeadsetChange...");
|
2017-03-04 15:48:10 -08:00
|
|
|
|
|
|
|
if (callState == CallState.STATE_CONNECTED ||
|
|
|
|
callState == CallState.STATE_DIALING ||
|
|
|
|
callState == CallState.STATE_REMOTE_RINGING)
|
|
|
|
{
|
|
|
|
AudioManager audioManager = ServiceUtil.getAudioManager(this);
|
|
|
|
boolean present = intent.getBooleanExtra(EXTRA_AVAILABLE, false);
|
|
|
|
|
|
|
|
if (present && audioManager.isSpeakerphoneOn()) {
|
|
|
|
audioManager.setSpeakerphoneOn(false);
|
|
|
|
audioManager.setBluetoothScoOn(false);
|
2018-04-25 11:00:03 -07:00
|
|
|
} else if (!present && !audioManager.isSpeakerphoneOn() && !audioManager.isBluetoothScoOn() && localCameraState.isEnabled()) {
|
2017-03-04 15:48:10 -08:00
|
|
|
audioManager.setSpeakerphoneOn(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (recipient != null) {
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2017-03-04 15:48:10 -08:00
|
|
|
}
|
|
|
|
}
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
2017-03-05 10:02:23 -08:00
|
|
|
private void handleScreenOffChange(Intent intent) {
|
|
|
|
if (callState == CallState.STATE_ANSWERING ||
|
|
|
|
callState == CallState.STATE_LOCAL_RINGING)
|
|
|
|
{
|
2018-08-02 09:25:33 -04:00
|
|
|
Log.i(TAG, "Silencing incoming ringer...");
|
2017-03-05 10:02:23 -08:00
|
|
|
audioManager.silenceIncomingRinger();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
private void handleRemoteVideoMute(Intent intent) {
|
2019-05-17 09:19:09 -07:00
|
|
|
Log.i(TAG, "handleRemoteVideoMute()");
|
2016-11-09 09:37:40 -08:00
|
|
|
boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false);
|
|
|
|
long callId = intent.getLongExtra(EXTRA_CALL_ID, -1);
|
|
|
|
|
|
|
|
if (this.recipient == null || this.callState != CallState.STATE_CONNECTED || !Util.isEquals(this.callId, callId)) {
|
|
|
|
Log.w(TAG, "Got video toggle for inactive call, ignoring...");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-02 18:34:51 -08:00
|
|
|
this.remoteVideoEnabled = !muted;
|
2018-04-25 11:00:03 -07:00
|
|
|
sendMessage(WebRtcViewModel.State.CALL_CONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
private void handleCallError(Intent intent) {
|
|
|
|
Recipient recipient = getRemoteRecipient(intent);
|
|
|
|
long callId = getCallId(intent);
|
|
|
|
int error_num = intent.getIntExtra(EXTRA_CALL_ERROR, -1);
|
|
|
|
CallError error = CallError.fromNativeIndex(error_num);
|
|
|
|
|
|
|
|
if (this.recipient == null || !Util.isEquals(this.callId, callId)) {
|
|
|
|
Log.w(TAG, "Got call error for inactive call, ignoring...");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.callState == CallState.STATE_ANSWERING || this.callState == CallState.STATE_LOCAL_RINGING) {
|
|
|
|
insertMissedCall(this.recipient, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (error) {
|
|
|
|
case UNREGISTERED_USER:
|
|
|
|
sendMessage(WebRtcViewModel.State.NO_SUCH_USER, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
break;
|
|
|
|
case UNTRUSTED_IDENTITY:
|
|
|
|
try {
|
|
|
|
byte[] identityKeyBytes = intent.getByteArrayExtra(EXTRA_IDENTITY_KEY_BYTES);
|
|
|
|
IdentityKey key = new IdentityKey(identityKeyBytes, 0);
|
|
|
|
sendMessage(WebRtcViewModel.State.UNTRUSTED_IDENTITY, recipient, key, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
} catch (InvalidKeyException e) {
|
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NETWORK_FAILURE:
|
|
|
|
case INTERNAL_FAILURE:
|
|
|
|
case FAILURE:
|
|
|
|
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
terminate();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
/// Helper Methods
|
|
|
|
|
|
|
|
private boolean isBusy() {
|
2019-05-23 16:56:05 -03:00
|
|
|
return callState != CallState.STATE_IDLE || TelephonyUtil.isAnyPstnLineBusy(this);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isIdle() {
|
|
|
|
return callState == CallState.STATE_IDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isIncomingMessageExpired(Intent intent) {
|
|
|
|
return System.currentTimeMillis() - intent.getLongExtra(WebRtcCallService.EXTRA_TIMESTAMP, -1) > TimeUnit.MINUTES.toMillis(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void initializeVideo() {
|
2019-01-29 14:25:49 -08:00
|
|
|
Util.runOnMainSync(() -> {
|
2019-05-17 09:19:09 -07:00
|
|
|
|
2019-01-29 14:25:49 -08:00
|
|
|
eglBase = EglBase.create();
|
|
|
|
localRenderer = new SurfaceViewRenderer(WebRtcCallService.this);
|
|
|
|
remoteRenderer = new SurfaceViewRenderer(WebRtcCallService.this);
|
|
|
|
|
|
|
|
localRenderer.init(eglBase.getEglBaseContext(), null);
|
|
|
|
remoteRenderer.init(eglBase.getEglBaseContext(), null);
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
VideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(),
|
|
|
|
true, true);
|
2019-01-29 14:25:49 -08:00
|
|
|
VideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(eglBase.getEglBaseContext());
|
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
callConnectionFactory = CallConnectionFactory.createCallConnectionFactory(WebRtcCallService.this,
|
|
|
|
encoderFactory,
|
|
|
|
decoderFactory);
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-02-22 15:49:05 -08:00
|
|
|
private void setCallInProgressNotification(int type, Recipient recipient) {
|
2019-06-27 15:17:14 -04:00
|
|
|
startForeground(CallNotificationBuilder.getNotificationId(getApplicationContext(), type),
|
2017-02-22 15:49:05 -08:00
|
|
|
CallNotificationBuilder.getCallInProgressNotification(this, type, recipient));
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
private synchronized void terminate() {
|
2019-05-17 09:19:09 -07:00
|
|
|
Log.i(TAG, "terminate()");
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING);
|
2017-02-22 15:49:05 -08:00
|
|
|
stopForeground(true);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
audioManager.stop(callState == CallState.STATE_DIALING || callState == CallState.STATE_REMOTE_RINGING || callState == CallState.STATE_CONNECTED);
|
|
|
|
bluetoothStateManager.setWantsConnection(false);
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2019-05-17 09:19:09 -07:00
|
|
|
if (callConnection != null) {
|
|
|
|
callConnection.dispose();
|
|
|
|
callConnection = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (callConnectionFactory != null) {
|
|
|
|
callConnectionFactory.dispose();
|
|
|
|
callConnectionFactory = null;
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (eglBase != null && localRenderer != null && remoteRenderer != null) {
|
|
|
|
localRenderer.release();
|
|
|
|
remoteRenderer.release();
|
|
|
|
eglBase.release();
|
2017-02-01 22:55:19 -08:00
|
|
|
|
|
|
|
localRenderer = null;
|
|
|
|
remoteRenderer = null;
|
|
|
|
eglBase = null;
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
2017-07-12 11:15:28 -07:00
|
|
|
this.callState = CallState.STATE_IDLE;
|
2018-04-25 11:00:03 -07:00
|
|
|
this.localCameraState = CameraState.UNKNOWN;
|
2017-07-12 11:15:28 -07:00
|
|
|
this.recipient = null;
|
|
|
|
this.callId = null;
|
|
|
|
this.microphoneEnabled = true;
|
|
|
|
this.remoteVideoEnabled = false;
|
2019-05-17 09:19:09 -07:00
|
|
|
this.pendingRemoteIceUpdates = null;
|
2016-11-09 09:37:40 -08:00
|
|
|
lockManager.updatePhoneState(LockManager.PhoneState.IDLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-02 18:34:51 -08:00
|
|
|
private void sendMessage(@NonNull WebRtcViewModel.State state,
|
2018-04-25 11:00:03 -07:00
|
|
|
@NonNull Recipient recipient,
|
|
|
|
@NonNull CameraState localCameraState,
|
|
|
|
boolean remoteVideoEnabled,
|
|
|
|
boolean bluetoothAvailable,
|
|
|
|
boolean microphoneEnabled)
|
2016-11-09 09:37:40 -08:00
|
|
|
{
|
2019-05-17 09:19:09 -07:00
|
|
|
EventBus.getDefault().postSticky(new WebRtcViewModel(state,
|
|
|
|
recipient,
|
|
|
|
localCameraState,
|
|
|
|
localRenderer,
|
|
|
|
remoteRenderer,
|
|
|
|
remoteVideoEnabled,
|
|
|
|
bluetoothAvailable,
|
|
|
|
microphoneEnabled));
|
2017-02-02 18:34:51 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void sendMessage(@NonNull WebRtcViewModel.State state,
|
2018-04-25 11:00:03 -07:00
|
|
|
@NonNull Recipient recipient,
|
|
|
|
@NonNull IdentityKey identityKey,
|
|
|
|
@NonNull CameraState localCameraState,
|
|
|
|
boolean remoteVideoEnabled,
|
|
|
|
boolean bluetoothAvailable,
|
|
|
|
boolean microphoneEnabled)
|
2017-02-02 18:34:51 -08:00
|
|
|
{
|
2019-05-17 09:19:09 -07:00
|
|
|
EventBus.getDefault().postSticky(new WebRtcViewModel(state,
|
|
|
|
recipient,
|
|
|
|
identityKey,
|
|
|
|
localCameraState,
|
|
|
|
localRenderer,
|
|
|
|
remoteRenderer,
|
|
|
|
remoteVideoEnabled,
|
|
|
|
bluetoothAvailable,
|
|
|
|
microphoneEnabled));
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
2019-06-27 15:17:14 -04:00
|
|
|
private void startCallCardActivityIfPossible() {
|
|
|
|
if (Build.VERSION.SDK_INT >= 29 && !ApplicationContext.getInstance(getApplicationContext()).isAppVisible()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
Intent activityIntent = new Intent();
|
|
|
|
activityIntent.setClass(this, WebRtcCallActivity.class);
|
|
|
|
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
this.startActivity(activityIntent);
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
private @NonNull Recipient getRemoteRecipient(Intent intent) {
|
2017-07-26 09:59:15 -07:00
|
|
|
Address remoteAddress = intent.getParcelableExtra(EXTRA_REMOTE_ADDRESS);
|
|
|
|
if (remoteAddress == null) throw new AssertionError("No recipient in intent!");
|
2016-11-09 09:37:40 -08:00
|
|
|
|
2017-08-21 18:32:38 -07:00
|
|
|
return Recipient.from(this, remoteAddress, true);
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private long getCallId(Intent intent) {
|
|
|
|
return intent.getLongExtra(EXTRA_CALL_ID, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
@Override
|
|
|
|
public IBinder onBind(Intent intent) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-02-02 18:34:51 -08:00
|
|
|
private WebRtcViewModel.State viewModelStateFor(CallState state) {
|
|
|
|
switch (state) {
|
|
|
|
case STATE_CONNECTED: return WebRtcViewModel.State.CALL_CONNECTED;
|
|
|
|
case STATE_DIALING: return WebRtcViewModel.State.CALL_OUTGOING;
|
|
|
|
case STATE_REMOTE_RINGING: return WebRtcViewModel.State.CALL_RINGING;
|
|
|
|
case STATE_LOCAL_RINGING: return WebRtcViewModel.State.CALL_INCOMING;
|
|
|
|
case STATE_ANSWERING: return WebRtcViewModel.State.CALL_INCOMING;
|
|
|
|
case STATE_IDLE: return WebRtcViewModel.State.CALL_DISCONNECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return WebRtcViewModel.State.CALL_DISCONNECTED;
|
|
|
|
}
|
|
|
|
|
2017-03-04 15:48:10 -08:00
|
|
|
///
|
|
|
|
|
|
|
|
private static class WiredHeadsetStateReceiver extends BroadcastReceiver {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
int state = intent.getIntExtra("state", -1);
|
|
|
|
|
|
|
|
Intent serviceIntent = new Intent(context, WebRtcCallService.class);
|
|
|
|
serviceIntent.setAction(WebRtcCallService.ACTION_WIRED_HEADSET_CHANGE);
|
|
|
|
serviceIntent.putExtra(WebRtcCallService.EXTRA_AVAILABLE, state != 0);
|
|
|
|
context.startService(serviceIntent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-05 10:02:23 -08:00
|
|
|
private static class PowerButtonReceiver extends BroadcastReceiver {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
|
|
|
|
Intent serviceIntent = new Intent(context, WebRtcCallService.class);
|
|
|
|
serviceIntent.setAction(WebRtcCallService.ACTION_SCREEN_OFF);
|
|
|
|
context.startService(serviceIntent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
private static class ProximityLockRelease implements Thread.UncaughtExceptionHandler {
|
|
|
|
private final LockManager lockManager;
|
|
|
|
|
|
|
|
private ProximityLockRelease(LockManager lockManager) {
|
|
|
|
this.lockManager = lockManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void uncaughtException(Thread thread, Throwable throwable) {
|
|
|
|
Log.d(TAG, "Uncaught exception - releasing proximity lock", throwable);
|
|
|
|
lockManager.updatePhoneState(LockManager.PhoneState.IDLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private abstract class StateAwareListener<V> implements FutureTaskListener<V> {
|
|
|
|
|
|
|
|
private final CallState expectedState;
|
|
|
|
private final long expectedCallId;
|
|
|
|
|
|
|
|
StateAwareListener(CallState expectedState, long expectedCallId) {
|
|
|
|
this.expectedState = expectedState;
|
|
|
|
this.expectedCallId = expectedCallId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSuccess(V result) {
|
|
|
|
if (!isConsistentState()) {
|
|
|
|
Log.w(TAG, "State has changed since request, aborting success callback...");
|
|
|
|
} else {
|
|
|
|
onSuccessContinue(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFailure(ExecutionException throwable) {
|
|
|
|
if (!isConsistentState()) {
|
|
|
|
Log.w(TAG, throwable);
|
|
|
|
Log.w(TAG, "State has changed since request, aborting failure callback...");
|
|
|
|
} else {
|
|
|
|
onFailureContinue(throwable.getCause());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isConsistentState() {
|
|
|
|
return this.expectedState == callState && Util.isEquals(callId, this.expectedCallId);
|
|
|
|
}
|
|
|
|
|
|
|
|
public abstract void onSuccessContinue(V result);
|
|
|
|
public abstract void onFailureContinue(Throwable throwable);
|
|
|
|
}
|
|
|
|
|
|
|
|
private abstract class FailureListener<V> extends StateAwareListener<V> {
|
|
|
|
FailureListener(CallState expectedState, long expectedCallId) {
|
|
|
|
super(expectedState, expectedCallId);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSuccessContinue(V result) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
private abstract class SuccessOnlyListener<V> extends StateAwareListener<V> {
|
|
|
|
SuccessOnlyListener(CallState expectedState, long expectedCallId) {
|
|
|
|
super(expectedState, expectedCallId);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFailureContinue(Throwable throwable) {
|
|
|
|
Log.w(TAG, throwable);
|
|
|
|
throw new AssertionError(throwable);
|
|
|
|
}
|
|
|
|
}
|
2017-02-05 12:38:08 -08:00
|
|
|
|
|
|
|
@WorkerThread
|
|
|
|
public static boolean isCallActive(Context context) {
|
2018-08-02 09:25:33 -04:00
|
|
|
Log.i(TAG, "isCallActive()");
|
2017-02-05 12:38:08 -08:00
|
|
|
|
|
|
|
HandlerThread handlerThread = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
handlerThread = new HandlerThread("webrtc-callback");
|
|
|
|
handlerThread.start();
|
|
|
|
|
|
|
|
final SettableFuture<Boolean> future = new SettableFuture<>();
|
|
|
|
|
|
|
|
ResultReceiver resultReceiver = new ResultReceiver(new Handler(handlerThread.getLooper())) {
|
|
|
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
2018-08-02 09:25:33 -04:00
|
|
|
Log.i(TAG, "Got result...");
|
2017-02-05 12:38:08 -08:00
|
|
|
future.set(resultCode == 1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Intent intent = new Intent(context, WebRtcCallService.class);
|
|
|
|
intent.setAction(ACTION_IS_IN_CALL_QUERY);
|
|
|
|
intent.putExtra(EXTRA_RESULT_RECEIVER, resultReceiver);
|
|
|
|
|
|
|
|
context.startService(intent);
|
|
|
|
|
2018-08-02 09:25:33 -04:00
|
|
|
Log.i(TAG, "Blocking on result...");
|
2017-02-05 12:38:08 -08:00
|
|
|
return future.get();
|
|
|
|
} catch (InterruptedException | ExecutionException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
return false;
|
|
|
|
} finally {
|
|
|
|
if (handlerThread != null) handlerThread.quit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void isCallActive(Context context, ResultReceiver resultReceiver) {
|
|
|
|
Intent intent = new Intent(context, WebRtcCallService.class);
|
|
|
|
intent.setAction(ACTION_IS_IN_CALL_QUERY);
|
|
|
|
intent.putExtra(EXTRA_RESULT_RECEIVER, resultReceiver);
|
|
|
|
|
|
|
|
context.startService(intent);
|
|
|
|
}
|
2019-05-23 16:56:05 -03:00
|
|
|
|
|
|
|
private class HangUpRtcOnPstnCallAnsweredListener extends PhoneStateListener {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCallStateChanged(int state, String phoneNumber) {
|
|
|
|
super.onCallStateChanged(state, phoneNumber);
|
|
|
|
if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
|
|
|
|
hangup();
|
|
|
|
Log.i(TAG, "Device phone call ended Signal call.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void hangup() {
|
|
|
|
Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class);
|
|
|
|
intent.setAction(ACTION_LOCAL_HANGUP);
|
|
|
|
|
|
|
|
startService(intent);
|
|
|
|
}
|
|
|
|
}
|
2019-05-17 09:19:09 -07:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCallEvent(SignalMessageRecipient eventRecipient,
|
|
|
|
long callId,
|
|
|
|
CallConnection.CallEvent event) {
|
|
|
|
Log.i(TAG, "handling onCallEvent(): " + callId + ", event: " + event.toString());
|
|
|
|
if (eventRecipient instanceof MessageRecipient) {
|
|
|
|
MessageRecipient recipient = (MessageRecipient)eventRecipient;
|
|
|
|
|
|
|
|
Intent intent = new Intent(this, WebRtcCallService.class);
|
|
|
|
intent.putExtra(EXTRA_CALL_ID, callId);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case RINGING:
|
|
|
|
intent.setAction(ACTION_CALL_RINGING);
|
|
|
|
break;
|
|
|
|
case REMOTE_CONNECTED:
|
|
|
|
intent.setAction(ACTION_CALL_CONNECTED);
|
|
|
|
break;
|
|
|
|
case REMOTE_VIDEO_ENABLE:
|
|
|
|
intent.setAction(ACTION_REMOTE_VIDEO_MUTE);
|
|
|
|
intent.putExtra(EXTRA_MUTE, false);
|
|
|
|
break;
|
|
|
|
case REMOTE_VIDEO_DISABLE:
|
|
|
|
intent.setAction(ACTION_REMOTE_VIDEO_MUTE);
|
|
|
|
intent.putExtra(EXTRA_MUTE, true);
|
|
|
|
break;
|
|
|
|
case REMOTE_HANGUP:
|
|
|
|
case CALL_DISCONNECTED:
|
|
|
|
intent.setAction(ACTION_REMOTE_HANGUP);
|
|
|
|
break;
|
|
|
|
case CALL_TIMEOUT:
|
|
|
|
intent.setAction(ACTION_CHECK_TIMEOUT);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Log.e(TAG, "handling onCallEvent(): Unexpected event type" + event.toString());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
startService(intent);
|
|
|
|
} else {
|
|
|
|
Log.e(TAG, "handling onCallEvent(): Unexpected recipient type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCallError(SignalMessageRecipient eventRecipient,
|
|
|
|
long callId,
|
|
|
|
Exception error) {
|
|
|
|
Log.i(TAG, "handling onCallError(): " + callId + ", error: " + error.toString());
|
|
|
|
if (eventRecipient instanceof SignalMessageRecipient) {
|
|
|
|
MessageRecipient recipient = (MessageRecipient)eventRecipient;
|
|
|
|
|
|
|
|
Intent intent = new Intent(this, WebRtcCallService.class);
|
|
|
|
intent.setAction(ACTION_CALL_ERROR);
|
|
|
|
intent.putExtra(EXTRA_CALL_ID, callId);
|
|
|
|
intent.putExtra(EXTRA_REMOTE_ADDRESS, recipient.getAddress());
|
|
|
|
|
|
|
|
if (error instanceof UntrustedIdentityException) {
|
|
|
|
intent.putExtra(EXTRA_CALL_ERROR, CallError.UNTRUSTED_IDENTITY.ordinal());
|
|
|
|
byte[] identityKeyBytes = ((UntrustedIdentityException)error).getIdentityKey().serialize();
|
|
|
|
intent.putExtra(EXTRA_IDENTITY_KEY_BYTES, identityKeyBytes);
|
|
|
|
} else if (error instanceof UnregisteredUserException) {
|
|
|
|
intent.putExtra(EXTRA_CALL_ERROR, CallError.UNREGISTERED_USER.ordinal());
|
|
|
|
} else if (error instanceof IOException) {
|
|
|
|
intent.putExtra(EXTRA_CALL_ERROR, CallError.NETWORK_FAILURE.ordinal());
|
|
|
|
} else if (error instanceof CallException) {
|
|
|
|
intent.putExtra(EXTRA_CALL_ERROR, CallError.INTERNAL_FAILURE.ordinal());
|
|
|
|
} else {
|
|
|
|
intent.putExtra(EXTRA_CALL_ERROR, CallError.FAILURE.ordinal());
|
|
|
|
}
|
|
|
|
startService(intent);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
Log.e(TAG, "handling onCallError(): Unexpected recipient type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAddStream(SignalMessageRecipient eventRecipient,
|
|
|
|
long callId,
|
|
|
|
MediaStream stream) {
|
|
|
|
Log.i(TAG, "onAddStream: callId: " + callId + ", stream: " + stream);
|
|
|
|
|
|
|
|
for (AudioTrack audioTrack : stream.audioTracks) {
|
|
|
|
Log.i(TAG, "onAddStream: enabling audioTrack");
|
|
|
|
audioTrack.setEnabled(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stream.videoTracks != null && stream.videoTracks.size() == 1) {
|
|
|
|
Log.i(TAG, "onAddStream: enabling videoTrack");
|
|
|
|
VideoTrack videoTrack = stream.videoTracks.get(0);
|
|
|
|
videoTrack.setEnabled(true);
|
|
|
|
videoTrack.addSink(remoteRenderer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-09 09:37:40 -08:00
|
|
|
}
|