NekoX/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java

1363 lines
48 KiB
Java
Raw Normal View History

2017-03-31 01:58:05 +02:00
/*
2019-01-23 18:03:33 +01:00
* This is the source code of Telegram for Android v. 5.x.x.
2017-03-31 01:58:05 +02:00
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Grishka, 2013-2016.
*/
package org.telegram.messenger.voip;
2018-07-30 04:07:02 +02:00
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
2017-03-31 01:58:05 +02:00
import android.app.Activity;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
2020-03-30 14:00:09 +02:00
import android.media.audiofx.AcousticEchoCanceler;
import android.media.audiofx.NoiseSuppressor;
2017-03-31 01:58:05 +02:00
import android.net.Uri;
import android.os.Build;
2018-07-30 04:07:02 +02:00
import android.os.Bundle;
2017-03-31 01:58:05 +02:00
import android.os.IBinder;
2019-05-14 14:08:05 +02:00
import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat;
2020-03-30 14:00:09 +02:00
import android.os.SystemClock;
2018-07-30 04:07:02 +02:00
import android.telecom.TelecomManager;
import android.text.TextUtils;
2017-03-31 01:58:05 +02:00
import android.view.KeyEvent;
2018-07-30 04:07:02 +02:00
import android.widget.Toast;
2017-03-31 01:58:05 +02:00
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.ContactsController;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.MessagesStorage;
import org.telegram.messenger.NotificationCenter;
2018-07-30 04:07:02 +02:00
import org.telegram.messenger.NotificationsController;
2017-03-31 01:58:05 +02:00
import org.telegram.messenger.R;
import org.telegram.messenger.Utilities;
2019-01-23 18:03:33 +01:00
import org.telegram.messenger.XiaomiUtilities;
2017-03-31 01:58:05 +02:00
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
2017-12-08 18:35:59 +01:00
import org.telegram.ui.Components.voip.VoIPHelper;
2017-03-31 01:58:05 +02:00
import org.telegram.ui.VoIPActivity;
import org.telegram.ui.VoIPFeedbackActivity;
2018-07-30 04:07:02 +02:00
import java.io.ByteArrayOutputStream;
2020-03-30 14:00:09 +02:00
import java.io.File;
2018-07-30 04:07:02 +02:00
import java.io.IOException;
2017-03-31 01:58:05 +02:00
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
2017-07-08 18:32:04 +02:00
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
2018-07-30 04:07:02 +02:00
import java.util.List;
2017-03-31 01:58:05 +02:00
2018-07-30 04:07:02 +02:00
public class VoIPService extends VoIPBaseService{
2017-03-31 01:58:05 +02:00
2018-07-30 04:07:02 +02:00
public static final int CALL_MIN_LAYER = 65;
2017-03-31 01:58:05 +02:00
2017-07-08 18:32:04 +02:00
public static final int STATE_HANGING_UP = 10;
public static final int STATE_EXCHANGING_KEYS = 12;
public static final int STATE_WAITING = 13;
public static final int STATE_REQUESTING = 14;
public static final int STATE_WAITING_INCOMING = 15;
public static final int STATE_RINGING = 16;
public static final int STATE_BUSY = 17;
2017-03-31 01:58:05 +02:00
private TLRPC.User user;
private TLRPC.PhoneCall call;
private int callReqId;
private byte[] g_a;
private byte[] a_or_b;
private byte[] g_a_hash;
private byte[] authKey;
private long keyFingerprint;
2017-12-08 18:35:59 +01:00
private boolean forceRating;
2017-03-31 01:58:05 +02:00
public static TLRPC.PhoneCall callIShouldHavePutIntoIntent;
2020-03-30 14:00:09 +02:00
private boolean needSendDebugLog = false;
private boolean needRateCall = false;
2017-03-31 01:58:05 +02:00
private boolean endCallAfterRequest=false;
private ArrayList<TLRPC.PhoneCall> pendingUpdates=new ArrayList<>();
2017-12-08 18:35:59 +01:00
private Runnable delayedStartOutgoingCall;
2018-07-30 04:07:02 +02:00
private int peerCapabilities;
private byte[] groupCallEncryptionKey;
private long groupCallKeyFingerprint;
private List<Integer> groupUsersToAdd=new ArrayList<>();
private boolean upgrading;
private boolean joiningGroupCall;
2019-03-03 21:40:48 +01:00
private boolean startedRinging=false;
2017-03-31 01:58:05 +02:00
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
2018-07-30 04:07:02 +02:00
@SuppressLint("MissingPermission")
2017-03-31 01:58:05 +02:00
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(sharedInstance!=null){
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("Tried to start the VoIP service when it's already started");
}
2017-03-31 01:58:05 +02:00
return START_NOT_STICKY;
}
2017-12-08 18:35:59 +01:00
2018-07-30 04:07:02 +02:00
currentAccount=intent.getIntExtra("account", -1);
if(currentAccount==-1)
throw new IllegalStateException("No account specified when starting VoIP service");
2017-12-08 18:35:59 +01:00
int userID=intent.getIntExtra("user_id", 0);
2017-03-31 01:58:05 +02:00
isOutgoing = intent.getBooleanExtra("is_outgoing", false);
2018-07-30 04:07:02 +02:00
user = MessagesController.getInstance(currentAccount).getUser(userID);
2017-03-31 01:58:05 +02:00
if(user==null){
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.w("VoIPService: user==null");
}
2017-03-31 01:58:05 +02:00
stopSelf();
return START_NOT_STICKY;
}
2018-07-30 04:07:02 +02:00
sharedInstance = this;
2017-03-31 01:58:05 +02:00
if (isOutgoing) {
2017-12-08 18:35:59 +01:00
dispatchStateChanged(STATE_REQUESTING);
2018-07-30 04:07:02 +02:00
if(USE_CONNECTION_SERVICE){
TelecomManager tm=(TelecomManager) getSystemService(TELECOM_SERVICE);
Bundle extras=new Bundle();
Bundle myExtras=new Bundle();
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, addAccountToTelecomManager());
myExtras.putInt("call_type", 1);
extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, myExtras);
2019-01-23 18:03:33 +01:00
ContactsController.getInstance(currentAccount).createOrUpdateConnectionServiceContact(user.id, user.first_name, user.last_name);
tm.placeCall(Uri.fromParts("tel", "+99084"+user.id, null), extras);
2018-07-30 04:07:02 +02:00
}else{
delayedStartOutgoingCall=new Runnable(){
@Override
public void run(){
delayedStartOutgoingCall=null;
startOutgoingCall();
}
};
AndroidUtilities.runOnUIThread(delayedStartOutgoingCall, 2000);
}
2017-03-31 01:58:05 +02:00
if (intent.getBooleanExtra("start_incall_activity", false)) {
startActivity(new Intent(this, VoIPActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
} else {
2018-07-30 04:07:02 +02:00
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.closeInCallActivity);
2017-03-31 01:58:05 +02:00
call = callIShouldHavePutIntoIntent;
callIShouldHavePutIntoIntent = null;
2018-07-30 04:07:02 +02:00
if(USE_CONNECTION_SERVICE){
acknowledgeCall(false);
showNotification();
}else{
acknowledgeCall(true);
}
2017-03-31 01:58:05 +02:00
}
2018-07-30 04:07:02 +02:00
initializeAccountRelatedThings();
2017-03-31 01:58:05 +02:00
return START_NOT_STICKY;
}
2018-07-30 04:07:02 +02:00
@Override
public void onCreate(){
super.onCreate();
if(callIShouldHavePutIntoIntent!=null && Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
2018-08-27 10:33:11 +02:00
NotificationsController.checkOtherNotificationsChannel();
2018-07-30 04:07:02 +02:00
Notification.Builder bldr=new Notification.Builder(this, NotificationsController.OTHER_NOTIFICATIONS_CHANNEL)
.setSmallIcon(R.drawable.notification)
.setContentTitle(LocaleController.getString("VoipOutgoingCall", R.string.VoipOutgoingCall))
.setShowWhen(false);
startForeground(ID_ONGOING_CALL_NOTIFICATION, bldr.build());
}
}
2017-03-31 01:58:05 +02:00
@Override
2017-12-08 18:35:59 +01:00
protected void updateServerConfig(){
2018-07-30 04:07:02 +02:00
final SharedPreferences preferences = MessagesController.getMainSettings(currentAccount);
2020-03-30 14:00:09 +02:00
TgVoip.setGlobalServerConfig(preferences.getString("voip_server_config", "{}"));
ConnectionsManager.getInstance(currentAccount).sendRequest(new TLRPC.TL_phone_getCallConfig(), (response, error) -> {
if (error == null) {
String data = ((TLRPC.TL_dataJSON) response).data;
TgVoip.setGlobalServerConfig(data);
preferences.edit().putString("voip_server_config", data).commit();
2018-07-30 04:07:02 +02:00
}
});
2017-03-31 01:58:05 +02:00
}
@Override
2020-03-30 14:00:09 +02:00
protected void onTgVoipPreStop(){
2019-03-03 21:40:48 +01:00
/*if(BuildConfig.DEBUG){
2017-12-08 18:35:59 +01:00
String debugLog=controller.getDebugLog();
TLRPC.TL_phone_saveCallDebug req=new TLRPC.TL_phone_saveCallDebug();
req.debug=new TLRPC.TL_dataJSON();
req.debug.data=debugLog;
req.peer=new TLRPC.TL_inputPhoneCall();
req.peer.access_hash=call.access_hash;
req.peer.id=call.id;
2018-07-30 04:07:02 +02:00
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate(){
2017-12-08 18:35:59 +01:00
@Override
public void run(TLObject response, TLRPC.TL_error error){
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.d("Sent debug logs, response=" + response);
}
2017-12-08 18:35:59 +01:00
}
});
2019-03-03 21:40:48 +01:00
}*/
2020-03-30 14:00:09 +02:00
}
@Override
protected void onTgVoipStop(TgVoip.FinalState finalState) {
if (needRateCall || forceRating || finalState.isRatingSuggested) {
startRatingActivity();
needRateCall = false;
}
if (needSendDebugLog && finalState.debugLog != null) {
TLRPC.TL_phone_saveCallDebug req = new TLRPC.TL_phone_saveCallDebug();
req.debug = new TLRPC.TL_dataJSON();
req.debug.data = finalState.debugLog;
req.peer = new TLRPC.TL_inputPhoneCall();
req.peer.access_hash = call.access_hash;
req.peer.id = call.id;
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> {
if (BuildVars.LOGS_ENABLED) {
FileLog.d("Sent debug logs, response = " + response);
}
});
needSendDebugLog = false;
}
2017-03-31 01:58:05 +02:00
}
public static VoIPService getSharedInstance() {
2017-12-08 18:35:59 +01:00
return sharedInstance instanceof VoIPService ? ((VoIPService)sharedInstance) : null;
2017-03-31 01:58:05 +02:00
}
public TLRPC.User getUser() {
return user;
}
public void hangUp() {
declineIncomingCall(currentState == STATE_RINGING || (currentState==STATE_WAITING && isOutgoing) ? DISCARD_REASON_MISSED : DISCARD_REASON_HANGUP, null);
}
public void hangUp(Runnable onDone) {
declineIncomingCall(currentState == STATE_RINGING || (currentState==STATE_WAITING && isOutgoing) ? DISCARD_REASON_MISSED : DISCARD_REASON_HANGUP, onDone);
}
private void startOutgoingCall() {
2018-07-30 04:07:02 +02:00
if(USE_CONNECTION_SERVICE && systemCallConnection!=null)
systemCallConnection.setDialing();
2017-03-31 01:58:05 +02:00
configureDeviceForCall();
showNotification();
startConnectingSound();
dispatchStateChanged(STATE_REQUESTING);
AndroidUtilities.runOnUIThread(new Runnable(){
@Override
public void run(){
2018-07-30 04:07:02 +02:00
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.didStartedCall);
2017-03-31 01:58:05 +02:00
}
});
final byte[] salt = new byte[256];
Utilities.random.nextBytes(salt);
TLRPC.TL_messages_getDhConfig req = new TLRPC.TL_messages_getDhConfig();
req.random_length = 256;
2018-07-30 04:07:02 +02:00
final MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount);
req.version = messagesStorage.getLastSecretVersion();
callReqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate() {
2017-03-31 01:58:05 +02:00
@Override
public void run(TLObject response, TLRPC.TL_error error) {
callReqId = 0;
2019-01-23 18:03:33 +01:00
if(endCallAfterRequest){
callEnded();
return;
}
2017-03-31 01:58:05 +02:00
if (error == null) {
TLRPC.messages_DhConfig res = (TLRPC.messages_DhConfig) response;
if (response instanceof TLRPC.TL_messages_dhConfig) {
if (!Utilities.isGoodPrime(res.p, res.g)) {
callFailed();
return;
}
2018-07-30 04:07:02 +02:00
messagesStorage.setSecretPBytes(res.p);
messagesStorage.setSecretG(res.g);
messagesStorage.setLastSecretVersion(res.version);
messagesStorage.saveSecretParams(messagesStorage.getLastSecretVersion(), messagesStorage.getSecretG(), messagesStorage.getSecretPBytes());
2017-03-31 01:58:05 +02:00
}
final byte[] salt = new byte[256];
for (int a = 0; a < 256; a++) {
salt[a] = (byte) ((byte) (Utilities.random.nextDouble() * 256) ^ res.random[a]);
}
2018-07-30 04:07:02 +02:00
BigInteger i_g_a = BigInteger.valueOf(messagesStorage.getSecretG());
i_g_a = i_g_a.modPow(new BigInteger(1, salt), new BigInteger(1, messagesStorage.getSecretPBytes()));
2017-03-31 01:58:05 +02:00
byte[] g_a = i_g_a.toByteArray();
if (g_a.length > 256) {
byte[] correctedAuth = new byte[256];
System.arraycopy(g_a, 1, correctedAuth, 0, 256);
g_a = correctedAuth;
}
TLRPC.TL_phone_requestCall reqCall = new TLRPC.TL_phone_requestCall();
2018-07-30 04:07:02 +02:00
reqCall.user_id = MessagesController.getInstance(currentAccount).getInputUser(user);
2017-03-31 01:58:05 +02:00
reqCall.protocol = new TLRPC.TL_phoneCallProtocol();
2017-07-08 18:32:04 +02:00
reqCall.protocol.udp_p2p = true;
reqCall.protocol.udp_reflector = true;
2017-03-31 01:58:05 +02:00
reqCall.protocol.min_layer = CALL_MIN_LAYER;
2020-03-30 14:00:09 +02:00
reqCall.protocol.max_layer = TgVoip.getConnectionMaxLayer();
reqCall.protocol.library_versions.addAll(TgVoip.getAvailableVersions());
2017-03-31 01:58:05 +02:00
VoIPService.this.g_a=g_a;
reqCall.g_a_hash = Utilities.computeSHA256(g_a, 0, g_a.length);
reqCall.random_id = Utilities.random.nextInt();
2018-07-30 04:07:02 +02:00
ConnectionsManager.getInstance(currentAccount).sendRequest(reqCall, new RequestDelegate() {
2017-03-31 01:58:05 +02:00
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable(){
@Override
public void run(){
if (error == null) {
call = ((TLRPC.TL_phone_phoneCall) response).phone_call;
a_or_b = salt;
dispatchStateChanged(STATE_WAITING);
if(endCallAfterRequest){
hangUp();
return;
}
if(pendingUpdates.size()>0 && call!=null){
for(TLRPC.PhoneCall call:pendingUpdates){
onCallUpdated(call);
}
pendingUpdates.clear();
}
timeoutRunnable = new Runnable() {
@Override
public void run() {
timeoutRunnable=null;
TLRPC.TL_phone_discardCall req = new TLRPC.TL_phone_discardCall();
req.peer = new TLRPC.TL_inputPhoneCall();
req.peer.access_hash = call.access_hash;
req.peer.id = call.id;
req.reason=new TLRPC.TL_phoneCallDiscardReasonMissed();
2018-07-30 04:07:02 +02:00
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate() {
2017-03-31 01:58:05 +02:00
@Override
public void run(TLObject response, TLRPC.TL_error error) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
if (error != null) {
FileLog.e("error on phone.discardCall: " + error);
} else {
FileLog.d("phone.discardCall " + response);
}
}
2017-03-31 01:58:05 +02:00
AndroidUtilities.runOnUIThread(new Runnable(){
@Override
public void run(){
callFailed();
}
});
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
}
};
2018-07-30 04:07:02 +02:00
AndroidUtilities.runOnUIThread(timeoutRunnable, MessagesController.getInstance(currentAccount).callReceiveTimeout);
2017-03-31 01:58:05 +02:00
} else {
if (error.code == 400 && "PARTICIPANT_VERSION_OUTDATED".equals(error.text)) {
2020-03-30 14:00:09 +02:00
callFailed(TgVoip.ERROR_PEER_OUTDATED);
2019-01-23 18:03:33 +01:00
} else if(error.code==403){
2020-03-30 14:00:09 +02:00
callFailed(TgVoip.ERROR_PRIVACY);
2017-03-31 01:58:05 +02:00
}else if(error.code==406){
2020-03-30 14:00:09 +02:00
callFailed(TgVoip.ERROR_LOCALIZED);
2017-03-31 01:58:05 +02:00
}else {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("Error on phone.requestCall: " + error);
}
2017-03-31 01:58:05 +02:00
callFailed();
}
}
}
});
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
} else {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("Error on getDhConfig " + error);
}
2017-03-31 01:58:05 +02:00
callFailed();
}
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
}
2018-07-30 04:07:02 +02:00
private void acknowledgeCall(final boolean startRinging){
2017-03-31 01:58:05 +02:00
if(call instanceof TLRPC.TL_phoneCallDiscarded){
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.w("Call " + call.id + " was discarded before the service started, stopping");
}
2017-03-31 01:58:05 +02:00
stopSelf();
return;
}
2019-01-23 18:03:33 +01:00
if(Build.VERSION.SDK_INT>=19 && XiaomiUtilities.isMIUI() && !XiaomiUtilities.isCustomPermissionGranted(XiaomiUtilities.OP_SHOW_WHEN_LOCKED)){
if(((KeyguardManager)getSystemService(KEYGUARD_SERVICE)).inKeyguardRestrictedInputMode()){
if(BuildVars.LOGS_ENABLED)
FileLog.e("MIUI: no permission to show when locked but the screen is locked. ¯\\_(ツ)_/¯");
stopSelf();
return;
}
}
2017-03-31 01:58:05 +02:00
TLRPC.TL_phone_receivedCall req = new TLRPC.TL_phone_receivedCall();
req.peer = new TLRPC.TL_inputPhoneCall();
req.peer.id = call.id;
req.peer.access_hash = call.access_hash;
2018-07-30 04:07:02 +02:00
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate() {
2017-03-31 01:58:05 +02:00
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable(){
@Override
public void run(){
if(sharedInstance==null)
return;
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.w("receivedCall response = " + response);
}
2017-03-31 01:58:05 +02:00
if (error != null){
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("error on receivedCall: " + error);
}
2017-03-31 01:58:05 +02:00
stopSelf();
}else{
2018-07-30 04:07:02 +02:00
if(USE_CONNECTION_SERVICE){
2019-01-23 18:03:33 +01:00
ContactsController.getInstance(currentAccount).createOrUpdateConnectionServiceContact(user.id, user.first_name, user.last_name);
2018-07-30 04:07:02 +02:00
TelecomManager tm=(TelecomManager) getSystemService(TELECOM_SERVICE);
Bundle extras=new Bundle();
extras.putInt("call_type", 1);
tm.addNewIncomingCall(addAccountToTelecomManager(), extras);
}
if(startRinging)
startRinging();
2017-03-31 01:58:05 +02:00
}
}
});
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
}
2018-07-30 04:07:02 +02:00
protected void startRinging() {
if(currentState==STATE_WAITING_INCOMING){
return;
2017-03-31 01:58:05 +02:00
}
2018-07-30 04:07:02 +02:00
if(USE_CONNECTION_SERVICE && systemCallConnection!=null)
systemCallConnection.setRinging();
if (BuildVars.LOGS_ENABLED) {
FileLog.d("starting ringing for call " + call.id);
}
dispatchStateChanged(STATE_WAITING_INCOMING);
2019-03-03 21:40:48 +01:00
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
2018-07-30 04:07:02 +02:00
showIncomingNotification(ContactsController.formatName(user.first_name, user.last_name), null, user, null, 0, VoIPActivity.class);
if (BuildVars.LOGS_ENABLED) {
FileLog.d("Showing incoming call notification");
}
2017-03-31 01:58:05 +02:00
} else {
2019-03-03 21:40:48 +01:00
startRingtoneAndVibration(user.id);
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.d("Starting incall activity for incoming call");
}
2017-03-31 01:58:05 +02:00
try {
PendingIntent.getActivity(VoIPService.this, 12345, new Intent(VoIPService.this, VoIPActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0).send();
} catch (Exception x) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("Error starting incall activity", x);
}
}
2019-03-03 21:40:48 +01:00
}
}
@Override
public void startRingtoneAndVibration(){
if(!startedRinging){
startRingtoneAndVibration(user.id);
startedRinging=true;
2017-03-31 01:58:05 +02:00
}
}
2018-08-27 10:33:11 +02:00
@Override
protected boolean isRinging(){
return currentState==STATE_WAITING_INCOMING;
}
2017-03-31 01:58:05 +02:00
public void acceptIncomingCall() {
stopRinging();
showNotification();
configureDeviceForCall();
startConnectingSound();
dispatchStateChanged(STATE_EXCHANGING_KEYS);
AndroidUtilities.runOnUIThread(new Runnable(){
@Override
public void run(){
2018-07-30 04:07:02 +02:00
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.didStartedCall);
2017-03-31 01:58:05 +02:00
}
});
2018-07-30 04:07:02 +02:00
final MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount);
2017-03-31 01:58:05 +02:00
TLRPC.TL_messages_getDhConfig req = new TLRPC.TL_messages_getDhConfig();
req.random_length = 256;
2018-07-30 04:07:02 +02:00
req.version = messagesStorage.getLastSecretVersion();
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate() {
2017-03-31 01:58:05 +02:00
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error == null) {
TLRPC.messages_DhConfig res = (TLRPC.messages_DhConfig) response;
if (response instanceof TLRPC.TL_messages_dhConfig) {
if (!Utilities.isGoodPrime(res.p, res.g)) {
/*acceptingChats.remove(encryptedChat.id);
declineSecretChat(encryptedChat.id);*/
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("stopping VoIP service, bad prime");
}
2017-03-31 01:58:05 +02:00
callFailed();
return;
}
2018-07-30 04:07:02 +02:00
messagesStorage.setSecretPBytes(res.p);
messagesStorage.setSecretG(res.g);
messagesStorage.setLastSecretVersion(res.version);
MessagesStorage.getInstance(currentAccount).saveSecretParams(messagesStorage.getLastSecretVersion(), messagesStorage.getSecretG(), messagesStorage.getSecretPBytes());
2017-03-31 01:58:05 +02:00
}
byte[] salt = new byte[256];
for (int a = 0; a < 256; a++) {
salt[a] = (byte) ((byte) (Utilities.random.nextDouble() * 256) ^ res.random[a]);
}
2018-07-30 04:07:02 +02:00
if(call==null){
if (BuildVars.LOGS_ENABLED) {
FileLog.e("call is null");
}
callFailed();
return;
}
2017-03-31 01:58:05 +02:00
a_or_b = salt;
2018-07-30 04:07:02 +02:00
BigInteger g_b = BigInteger.valueOf(messagesStorage.getSecretG());
BigInteger p = new BigInteger(1, messagesStorage.getSecretPBytes());
2017-03-31 01:58:05 +02:00
g_b = g_b.modPow(new BigInteger(1, salt), p);
g_a_hash=call.g_a_hash;
byte[] g_b_bytes = g_b.toByteArray();
if (g_b_bytes.length > 256) {
byte[] correctedAuth = new byte[256];
System.arraycopy(g_b_bytes, 1, correctedAuth, 0, 256);
g_b_bytes = correctedAuth;
}
TLRPC.TL_phone_acceptCall req = new TLRPC.TL_phone_acceptCall();
req.g_b = g_b_bytes;
//req.key_fingerprint = Utilities.bytesToLong(authKeyId);
req.peer = new TLRPC.TL_inputPhoneCall();
req.peer.id = call.id;
req.peer.access_hash = call.access_hash;
req.protocol = new TLRPC.TL_phoneCallProtocol();
req.protocol.udp_p2p = req.protocol.udp_reflector = true;
req.protocol.min_layer = CALL_MIN_LAYER;
2020-03-30 14:00:09 +02:00
req.protocol.max_layer = TgVoip.getConnectionMaxLayer();
req.protocol.library_versions.addAll(TgVoip.getAvailableVersions());
2018-07-30 04:07:02 +02:00
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate() {
2017-03-31 01:58:05 +02:00
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable(){
@Override
public void run(){
if (error == null) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.w("accept call ok! " + response);
}
2017-03-31 01:58:05 +02:00
call = ((TLRPC.TL_phone_phoneCall) response).phone_call;
if(call instanceof TLRPC.TL_phoneCallDiscarded){
onCallUpdated(call);
}/*else{
initiateActualEncryptedCall();
}*/
} else {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("Error on phone.acceptCall: " + error);
}
2017-03-31 01:58:05 +02:00
callFailed();
}
}
});
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
} else {
//acceptingChats.remove(encryptedChat.id);
callFailed();
}
}
});
}
public void declineIncomingCall() {
declineIncomingCall(DISCARD_REASON_HANGUP, null);
}
2018-07-30 04:07:02 +02:00
@Override
protected Class<? extends Activity> getUIActivityClass(){
return VoIPActivity.class;
}
2017-03-31 01:58:05 +02:00
public void declineIncomingCall(int reason, final Runnable onDone) {
2018-07-30 04:07:02 +02:00
stopRinging();
callDiscardReason=reason;
2017-03-31 01:58:05 +02:00
if(currentState==STATE_REQUESTING){
2017-12-08 18:35:59 +01:00
if(delayedStartOutgoingCall!=null){
AndroidUtilities.cancelRunOnUIThread(delayedStartOutgoingCall);
callEnded();
}else{
dispatchStateChanged(STATE_HANGING_UP);
endCallAfterRequest=true;
2019-01-23 18:03:33 +01:00
AndroidUtilities.runOnUIThread(new Runnable(){
@Override
public void run(){
if(currentState==STATE_HANGING_UP){
callEnded();
}
}
}, 5000);
2017-12-08 18:35:59 +01:00
}
2017-03-31 01:58:05 +02:00
return;
}
if (currentState == STATE_HANGING_UP || currentState == STATE_ENDED)
return;
dispatchStateChanged(STATE_HANGING_UP);
if (call == null) {
if (onDone != null)
onDone.run();
callEnded();
if (callReqId != 0) {
2018-07-30 04:07:02 +02:00
ConnectionsManager.getInstance(currentAccount).cancelRequest(callReqId, false);
2017-03-31 01:58:05 +02:00
callReqId = 0;
}
return;
}
TLRPC.TL_phone_discardCall req = new TLRPC.TL_phone_discardCall();
req.peer = new TLRPC.TL_inputPhoneCall();
req.peer.access_hash = call.access_hash;
req.peer.id = call.id;
2020-03-30 14:00:09 +02:00
req.duration = (int) (getCallDuration() / 1000);
req.connection_id = tgVoip != null ? tgVoip.getPreferredRelayId() : 0;
2017-03-31 01:58:05 +02:00
switch (reason) {
case DISCARD_REASON_DISCONNECT:
req.reason = new TLRPC.TL_phoneCallDiscardReasonDisconnect();
break;
case DISCARD_REASON_MISSED:
req.reason = new TLRPC.TL_phoneCallDiscardReasonMissed();
break;
case DISCARD_REASON_LINE_BUSY:
req.reason = new TLRPC.TL_phoneCallDiscardReasonBusy();
break;
case DISCARD_REASON_HANGUP:
default:
req.reason = new TLRPC.TL_phoneCallDiscardReasonHangup();
break;
}
2018-07-30 04:07:02 +02:00
final boolean wasNotConnected=ConnectionsManager.getInstance(currentAccount).getConnectionState()!=ConnectionsManager.ConnectionStateConnected;
2017-07-08 18:32:04 +02:00
final Runnable stopper;
2017-03-31 01:58:05 +02:00
if(wasNotConnected){
if (onDone != null)
onDone.run();
callEnded();
2017-07-08 18:32:04 +02:00
stopper=null;
}else{
stopper=new Runnable(){
private boolean done=false;
@Override
public void run(){
if(done)
return;
done=true;
if(onDone!=null)
onDone.run();
callEnded();
}
};
2020-03-30 14:00:09 +02:00
AndroidUtilities.runOnUIThread(stopper, (int) (TgVoip.getGlobalServerConfig().hangupUiTimeout * 1000));
2017-03-31 01:58:05 +02:00
}
2018-07-30 04:07:02 +02:00
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate() {
2017-03-31 01:58:05 +02:00
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error != null) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("error on phone.discardCall: " + error);
}
2017-03-31 01:58:05 +02:00
} else {
if (response instanceof TLRPC.TL_updates) {
TLRPC.TL_updates updates = (TLRPC.TL_updates) response;
2018-07-30 04:07:02 +02:00
MessagesController.getInstance(currentAccount).processUpdates(updates, false);
2017-03-31 01:58:05 +02:00
}
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.d("phone.discardCall " + response);
}
2017-03-31 01:58:05 +02:00
}
if (!wasNotConnected){
AndroidUtilities.cancelRunOnUIThread(stopper);
if(onDone!=null)
onDone.run();
}
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
}
2018-07-30 04:07:02 +02:00
private void dumpCallObject() {
try {
if (BuildVars.LOGS_ENABLED) {
Field[] flds = TLRPC.PhoneCall.class.getFields();
for (Field f : flds) {
FileLog.d(f.getName() + " = " + f.get(call));
}
}
} catch (Exception x) {
if (BuildVars.LOGS_ENABLED) {
FileLog.e(x);
}
}
}
2017-03-31 01:58:05 +02:00
public void onCallUpdated(TLRPC.PhoneCall call) {
if(this.call==null){
pendingUpdates.add(call);
return;
}
if(call==null)
return;
if(call.id!=this.call.id){
2018-07-30 04:07:02 +02:00
if(BuildVars.LOGS_ENABLED) {
FileLog.w("onCallUpdated called with wrong call id (got " + call.id + ", expected " + this.call.id + ")");
}
2017-03-31 01:58:05 +02:00
return;
}
if(call.access_hash==0)
call.access_hash=this.call.access_hash;
2018-07-30 04:07:02 +02:00
if(BuildVars.LOGS_ENABLED) {
FileLog.d("Call updated: " + call);
dumpCallObject();
}
2017-03-31 01:58:05 +02:00
this.call = call;
if (call instanceof TLRPC.TL_phoneCallDiscarded) {
2020-03-30 14:00:09 +02:00
needSendDebugLog = call.need_debug;
needRateCall = call.need_rating;
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.d("call discarded, stopping service");
}
2017-03-31 01:58:05 +02:00
if (call.reason instanceof TLRPC.TL_phoneCallDiscardReasonBusy) {
dispatchStateChanged(STATE_BUSY);
playingSound = true;
soundPool.play(spBusyId, 1, 1, 0, -1, 1);
2017-07-08 18:32:04 +02:00
AndroidUtilities.runOnUIThread(afterSoundRunnable, 1500);
2019-01-23 18:03:33 +01:00
endConnectionServiceCall(1500);
2017-03-31 01:58:05 +02:00
stopSelf();
} else {
callEnded();
}
} else if (call instanceof TLRPC.TL_phoneCall && authKey == null){
if(call.g_a_or_b==null){
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.w("stopping VoIP service, Ga == null");
}
2017-03-31 01:58:05 +02:00
callFailed();
return;
}
if(!Arrays.equals(g_a_hash, Utilities.computeSHA256(call.g_a_or_b, 0, call.g_a_or_b.length))){
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.w("stopping VoIP service, Ga hash doesn't match");
}
2017-03-31 01:58:05 +02:00
callFailed();
return;
}
g_a=call.g_a_or_b;
BigInteger g_a = new BigInteger(1, call.g_a_or_b);
2018-07-30 04:07:02 +02:00
BigInteger p = new BigInteger(1, MessagesStorage.getInstance(currentAccount).getSecretPBytes());
2017-03-31 01:58:05 +02:00
if (!Utilities.isGoodGaAndGb(g_a, p)) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.w("stopping VoIP service, bad Ga and Gb (accepting)");
}
2017-03-31 01:58:05 +02:00
callFailed();
return;
}
g_a = g_a.modPow(new BigInteger(1, a_or_b), p);
byte[] authKey = g_a.toByteArray();
if (authKey.length > 256) {
byte[] correctedAuth = new byte[256];
System.arraycopy(authKey, authKey.length - 256, correctedAuth, 0, 256);
authKey = correctedAuth;
} else if (authKey.length < 256) {
byte[] correctedAuth = new byte[256];
System.arraycopy(authKey, 0, correctedAuth, 256 - authKey.length, authKey.length);
for (int a = 0; a < 256 - authKey.length; a++) {
2018-08-27 10:33:11 +02:00
correctedAuth[a] = 0;
2017-03-31 01:58:05 +02:00
}
authKey = correctedAuth;
}
byte[] authKeyHash = Utilities.computeSHA1(authKey);
byte[] authKeyId = new byte[8];
System.arraycopy(authKeyHash, authKeyHash.length - 8, authKeyId, 0, 8);
VoIPService.this.authKey = authKey;
keyFingerprint = Utilities.bytesToLong(authKeyId);
if(keyFingerprint!=call.key_fingerprint){
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.w("key fingerprints don't match");
}
2017-03-31 01:58:05 +02:00
callFailed();
return;
}
initiateActualEncryptedCall();
} else if(call instanceof TLRPC.TL_phoneCallAccepted && authKey==null){
processAcceptedCall();
} else {
if (currentState == STATE_WAITING && call.receive_date != 0) {
dispatchStateChanged(STATE_RINGING);
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.d("!!!!!! CALL RECEIVED");
}
2019-01-23 18:03:33 +01:00
if(connectingSoundRunnable!=null){
AndroidUtilities.cancelRunOnUIThread(connectingSoundRunnable);
connectingSoundRunnable=null;
}
2017-03-31 01:58:05 +02:00
if (spPlayID != 0)
soundPool.stop(spPlayID);
spPlayID = soundPool.play(spRingbackID, 1, 1, 0, -1, 1);
if (timeoutRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(timeoutRunnable);
timeoutRunnable=null;
}
timeoutRunnable = new Runnable() {
@Override
public void run() {
timeoutRunnable=null;
declineIncomingCall(DISCARD_REASON_MISSED, null);
}
};
2018-07-30 04:07:02 +02:00
AndroidUtilities.runOnUIThread(timeoutRunnable, MessagesController.getInstance(currentAccount).callRingTimeout);
2017-03-31 01:58:05 +02:00
}
}
}
private void startRatingActivity() {
try {
PendingIntent.getActivity(VoIPService.this, 0, new Intent(VoIPService.this, VoIPFeedbackActivity.class)
.putExtra("call_id", call.id)
.putExtra("call_access_hash", call.access_hash)
2018-07-30 04:07:02 +02:00
.putExtra("account", currentAccount)
2017-03-31 01:58:05 +02:00
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP), 0).send();
} catch (Exception x) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("Error starting incall activity", x);
}
2017-03-31 01:58:05 +02:00
}
}
public byte[] getEncryptionKey() {
return authKey;
}
private void processAcceptedCall() {
2017-07-08 18:32:04 +02:00
2017-03-31 01:58:05 +02:00
dispatchStateChanged(STATE_EXCHANGING_KEYS);
2018-07-30 04:07:02 +02:00
BigInteger p = new BigInteger(1, MessagesStorage.getInstance(currentAccount).getSecretPBytes());
2017-03-31 01:58:05 +02:00
BigInteger i_authKey = new BigInteger(1, call.g_b);
if (!Utilities.isGoodGaAndGb(i_authKey, p)) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.w("stopping VoIP service, bad Ga and Gb");
}
2017-03-31 01:58:05 +02:00
callFailed();
return;
}
i_authKey = i_authKey.modPow(new BigInteger(1, a_or_b), p);
byte[] authKey = i_authKey.toByteArray();
if (authKey.length > 256) {
byte[] correctedAuth = new byte[256];
System.arraycopy(authKey, authKey.length - 256, correctedAuth, 0, 256);
authKey = correctedAuth;
} else if (authKey.length < 256) {
byte[] correctedAuth = new byte[256];
System.arraycopy(authKey, 0, correctedAuth, 256 - authKey.length, authKey.length);
for (int a = 0; a < 256 - authKey.length; a++) {
2018-08-27 10:33:11 +02:00
correctedAuth[a] = 0;
2017-03-31 01:58:05 +02:00
}
authKey = correctedAuth;
}
byte[] authKeyHash = Utilities.computeSHA1(authKey);
byte[] authKeyId = new byte[8];
System.arraycopy(authKeyHash, authKeyHash.length - 8, authKeyId, 0, 8);
long fingerprint = Utilities.bytesToLong(authKeyId);
this.authKey=authKey;
keyFingerprint=fingerprint;
TLRPC.TL_phone_confirmCall req=new TLRPC.TL_phone_confirmCall();
req.g_a=g_a;
req.key_fingerprint=fingerprint;
req.peer=new TLRPC.TL_inputPhoneCall();
req.peer.id=call.id;
req.peer.access_hash=call.access_hash;
req.protocol=new TLRPC.TL_phoneCallProtocol();
2020-03-30 14:00:09 +02:00
req.protocol.max_layer=TgVoip.getConnectionMaxLayer();
2017-03-31 01:58:05 +02:00
req.protocol.min_layer=CALL_MIN_LAYER;
req.protocol.udp_p2p=req.protocol.udp_reflector=true;
2020-03-30 14:00:09 +02:00
req.protocol.library_versions.addAll(TgVoip.getAvailableVersions());
2018-07-30 04:07:02 +02:00
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate(){
2017-03-31 01:58:05 +02:00
@Override
public void run(final TLObject response, final TLRPC.TL_error error){
AndroidUtilities.runOnUIThread(new Runnable(){
@Override
public void run(){
if(error!=null){
callFailed();
}else{
call=((TLRPC.TL_phone_phoneCall)response).phone_call;
initiateActualEncryptedCall();
}
}
});
}
});
}
2020-03-30 14:00:09 +02:00
private int convertDataSavingMode(int mode) {
if (mode != TgVoip.DATA_SAVING_ROAMING) {
2019-03-03 21:40:48 +01:00
return mode;
2020-03-30 14:00:09 +02:00
}
return ApplicationLoader.isRoaming() ? TgVoip.DATA_SAVING_MOBILE : TgVoip.DATA_SAVING_NEVER;
2019-03-03 21:40:48 +01:00
}
2017-03-31 01:58:05 +02:00
private void initiateActualEncryptedCall() {
if (timeoutRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(timeoutRunnable);
timeoutRunnable = null;
}
try {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.d("InitCall: keyID=" + keyFingerprint);
}
SharedPreferences nprefs=MessagesController.getNotificationsSettings(currentAccount);
2017-07-08 18:32:04 +02:00
HashSet<String> hashes=new HashSet<>(nprefs.getStringSet("calls_access_hashes", Collections.EMPTY_SET));
hashes.add(call.id+" "+call.access_hash+" "+System.currentTimeMillis());
while(hashes.size()>20){
String oldest=null;
long oldestTime=Long.MAX_VALUE;
Iterator<String> itr=hashes.iterator();
while(itr.hasNext()){
String item=itr.next();
String[] s=item.split(" ");
if(s.length<2){
itr.remove();
}else{
try{
long t=Long.parseLong(s[2]);
if(t<oldestTime){
oldestTime=t;
oldest=item;
}
}catch(Exception x){
itr.remove();
}
}
}
if(oldest!=null)
hashes.remove(oldest);
}
2018-07-30 04:07:02 +02:00
nprefs.edit().putStringSet("calls_access_hashes", hashes).commit();
2017-03-31 01:58:05 +02:00
2020-03-30 14:00:09 +02:00
boolean sysAecAvailable = false, sysNsAvailable = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
try {
sysAecAvailable = AcousticEchoCanceler.isAvailable();
} catch (Exception ignored) {
}
try {
sysNsAvailable = NoiseSuppressor.isAvailable();
} catch (Exception ignored) {
}
}
2018-07-30 04:07:02 +02:00
2020-03-30 14:00:09 +02:00
final SharedPreferences preferences = MessagesController.getGlobalMainSettings();
TgVoip.setNativeVersion(this, call.protocol.library_versions.get(0));
// config
final MessagesController messagesController = MessagesController.getInstance(currentAccount);
final double initializationTimeout = messagesController.callConnectTimeout / 1000.0;
final double receiveTimeout = messagesController.callPacketTimeout / 1000.0;
final int voipDataSaving = convertDataSavingMode(preferences.getInt("VoipDataSaving", VoIPHelper.getDataSavingDefault()));
final TgVoip.ServerConfig serverConfig = TgVoip.getGlobalServerConfig();
final boolean enableAec = !(sysAecAvailable && serverConfig.useSystemAec);
final boolean enableNs = !(sysNsAvailable && serverConfig.useSystemNs);
final String logFilePath = BuildVars.DEBUG_VERSION ? VoIPHelper.getLogFilePath("voip" + call.id) : VoIPHelper.getLogFilePath(call.id);
final TgVoip.Config config = new TgVoip.Config(initializationTimeout, receiveTimeout, voipDataSaving, call.p2p_allowed, enableAec, enableNs,
/* enableAgc */ true, /* enableCallUpgrade */ false, logFilePath, call.protocol.max_layer);
// persistent state
final String persistentStateFilePath = new File(ApplicationLoader.applicationContext.getFilesDir(), "voip_persistent_state.json").getAbsolutePath();
// endpoints
final boolean forceTcp = preferences.getBoolean("dbg_force_tcp_in_calls", false);
final int endpointType = forceTcp ? TgVoip.ENDPOINT_TYPE_TCP_RELAY : TgVoip.ENDPOINT_TYPE_UDP_RELAY;
final TgVoip.Endpoint[] endpoints = new TgVoip.Endpoint[call.connections.size()];
for (int i = 0; i < endpoints.length; i++) {
final TLRPC.TL_phoneConnection connection = call.connections.get(i);
endpoints[i] = new TgVoip.Endpoint(connection.id, connection.ip, connection.ipv6, connection.port, endpointType, connection.peer_tag);
2018-07-30 04:07:02 +02:00
}
2020-03-30 14:00:09 +02:00
if (forceTcp) {
AndroidUtilities.runOnUIThread(() -> Toast.makeText(VoIPService.this, "This call uses TCP which will degrade its quality.", Toast.LENGTH_SHORT).show());
}
// proxy
TgVoip.Proxy proxy = null;
if (preferences.getBoolean("proxy_enabled", false) && preferences.getBoolean("proxy_enabled_calls", false)) {
final String server = preferences.getString("proxy_ip", null);
final String secret = preferences.getString("proxy_secret", null);
2018-07-30 04:07:02 +02:00
if (!TextUtils.isEmpty(server) && TextUtils.isEmpty(secret)) {
2020-03-30 14:00:09 +02:00
proxy = new TgVoip.Proxy(server, preferences.getInt("proxy_port", 0), preferences.getString("proxy_user", null), preferences.getString("proxy_pass", null));
2017-07-08 18:32:04 +02:00
}
}
2020-03-30 14:00:09 +02:00
// encryption key
final TgVoip.EncryptionKey encryptionKey = new TgVoip.EncryptionKey(authKey, isOutgoing);
// init
tgVoip = TgVoip.makeInstance(config, persistentStateFilePath, endpoints, proxy, getNetworkType(), encryptionKey);
tgVoip.setOnStateUpdatedListener(this::onConnectionStateChanged);
tgVoip.setOnSignalBarsUpdatedListener(this::onSignalBarCountChanged);
2017-03-31 01:58:05 +02:00
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
2020-03-30 14:00:09 +02:00
if (tgVoip != null) {
updateTrafficStats();
AndroidUtilities.runOnUIThread(this, 5000);
}
2017-03-31 01:58:05 +02:00
}
}, 5000);
} catch (Exception x) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("error starting call", x);
}
2017-03-31 01:58:05 +02:00
callFailed();
}
}
2017-12-08 18:35:59 +01:00
protected void showNotification(){
showNotification(ContactsController.formatName(user.first_name, user.last_name), user.photo!=null ? user.photo.photo_small : null, VoIPActivity.class);
2017-03-31 01:58:05 +02:00
}
private void startConnectingSound() {
if (spPlayID != 0)
soundPool.stop(spPlayID);
spPlayID = soundPool.play(spConnectingId, 1, 1, 0, -1, 1);
if (spPlayID == 0) {
2019-01-23 18:03:33 +01:00
AndroidUtilities.runOnUIThread(connectingSoundRunnable=new Runnable() {
2017-03-31 01:58:05 +02:00
@Override
public void run() {
if (sharedInstance == null)
return;
if (spPlayID == 0)
spPlayID = soundPool.play(spConnectingId, 1, 1, 0, -1, 1);
if (spPlayID == 0)
AndroidUtilities.runOnUIThread(this, 100);
2019-01-23 18:03:33 +01:00
else
connectingSoundRunnable=null;
2017-03-31 01:58:05 +02:00
}
}, 100);
}
}
2020-03-30 14:00:09 +02:00
protected void callFailed(String error) {
2017-03-31 01:58:05 +02:00
if (call != null) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.d("Discarding failed call");
}
2017-03-31 01:58:05 +02:00
TLRPC.TL_phone_discardCall req = new TLRPC.TL_phone_discardCall();
req.peer = new TLRPC.TL_inputPhoneCall();
req.peer.access_hash = call.access_hash;
req.peer.id = call.id;
2020-03-30 14:00:09 +02:00
req.duration = (int) (getCallDuration() / 1000);
req.connection_id = tgVoip != null ? tgVoip.getPreferredRelayId() : 0;
2017-03-31 01:58:05 +02:00
req.reason = new TLRPC.TL_phoneCallDiscardReasonDisconnect();
2018-07-30 04:07:02 +02:00
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate() {
2017-03-31 01:58:05 +02:00
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error != null) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("error on phone.discardCall: " + error);
}
2017-03-31 01:58:05 +02:00
} else {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.d("phone.discardCall " + response);
}
2017-03-31 01:58:05 +02:00
}
}
});
}
2020-03-30 14:00:09 +02:00
super.callFailed(error);
2017-03-31 01:58:05 +02:00
}
@Override
2018-07-30 04:07:02 +02:00
public long getCallID(){
2017-12-08 18:35:59 +01:00
return call!=null ? call.id : 0;
2017-03-31 01:58:05 +02:00
}
public void onUIForegroundStateChanged(boolean isForeground) {
2019-03-03 21:40:48 +01:00
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
return;
2017-03-31 01:58:05 +02:00
if (currentState == STATE_WAITING_INCOMING) {
if (isForeground) {
stopForeground(true);
} else {
if (!((KeyguardManager) getSystemService(KEYGUARD_SERVICE)).inKeyguardRestrictedInputMode()) {
2018-07-30 04:07:02 +02:00
if(NotificationManagerCompat.from(this).areNotificationsEnabled())
showIncomingNotification(ContactsController.formatName(user.first_name, user.last_name), null, user, null, 0, VoIPActivity.class);
else
declineIncomingCall(DISCARD_REASON_LINE_BUSY, null);
2017-03-31 01:58:05 +02:00
} else {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(VoIPService.this, VoIPActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
try {
PendingIntent.getActivity(VoIPService.this, 0, intent, 0).send();
} catch (PendingIntent.CanceledException e) {
2018-07-30 04:07:02 +02:00
if (BuildVars.LOGS_ENABLED) {
FileLog.e("error restarting activity", e);
}
declineIncomingCall(DISCARD_REASON_LINE_BUSY, null);
}
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
showNotification();
2017-03-31 01:58:05 +02:00
}
}
}, 500);
}
}
}
}
/*package*/ void onMediaButtonEvent(KeyEvent ev) {
2018-08-27 10:33:11 +02:00
if (ev.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK || ev.getKeyCode()==KeyEvent.KEYCODE_MEDIA_PAUSE || ev.getKeyCode()==KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
2017-03-31 01:58:05 +02:00
if (ev.getAction() == KeyEvent.ACTION_UP) {
if (currentState == STATE_WAITING_INCOMING) {
acceptIncomingCall();
} else {
setMicMute(!isMicMute());
for (StateListener l : stateListeners)
l.onAudioSettingsChanged();
}
}
}
}
public byte[] getGA(){
return g_a;
}
@Override
2018-07-30 04:07:02 +02:00
public void didReceivedNotification(int id, int account, Object... args){
2017-03-31 01:58:05 +02:00
if(id==NotificationCenter.appDidLogout){
callEnded();
}
}
2017-12-08 18:35:59 +01:00
public void forceRating(){
forceRating=true;
2017-03-31 01:58:05 +02:00
}
2017-12-08 18:35:59 +01:00
2018-07-30 04:07:02 +02:00
private String[] getEmoji(){
ByteArrayOutputStream os=new ByteArrayOutputStream();
try{
os.write(authKey);
os.write(g_a);
}catch(IOException ignore){}
return EncryptionKeyEmojifier.emojifyForCall(Utilities.computeSHA256(os.toByteArray(), 0, os.size()));
}
2020-03-30 14:00:09 +02:00
public boolean canUpgrate() {
return (peerCapabilities & TgVoip.PEER_CAP_GROUP_CALLS) == TgVoip.PEER_CAP_GROUP_CALLS;
2018-07-30 04:07:02 +02:00
}
public void upgradeToGroupCall(List<Integer> usersToAdd){
if(upgrading)
return;
groupUsersToAdd=usersToAdd;
2020-03-30 14:00:09 +02:00
if (!isOutgoing) {
tgVoip.requestCallUpgrade();
2018-07-30 04:07:02 +02:00
return;
}
upgrading=true;
groupCallEncryptionKey=new byte[256];
Utilities.random.nextBytes(groupCallEncryptionKey);
groupCallEncryptionKey[0]&=0x7F;
byte[] authKeyHash = Utilities.computeSHA1(groupCallEncryptionKey);
byte[] authKeyId = new byte[8];
System.arraycopy(authKeyHash, authKeyHash.length - 8, authKeyId, 0, 8);
groupCallKeyFingerprint=Utilities.bytesToLong(authKeyId);
2020-03-30 14:00:09 +02:00
tgVoip.sendGroupCallKey(groupCallEncryptionKey);
2018-07-30 04:07:02 +02:00
}
/*public void upgradedToGroupCall(TLRPC.TL_updateGroupCall update){
if(upgrading){
FileLog.w("Received an update about call upgrade but we're upgrading it ourselves; ignoring update");
return;
}
VoIPGroupService.waitingToStart=true;
TLRPC.TL_phone_getGroupCall req=new TLRPC.TL_phone_getGroupCall();
req.call=new TLRPC.TL_inputGroupCall();
req.call.id=update.call.id;
req.call.access_hash=update.call.access_hash;
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate(){
@Override
public void run(TLObject response, TLRPC.TL_error error){
if(response!=null){
TLRPC.TL_phone_groupCall call=(TLRPC.TL_phone_groupCall) response;
if((call.call.flags & 2)==1 || call.call.key_fingerprint!=groupCallKeyFingerprint){
callFailed(VoIPController.ERROR_INSECURE_UPGRADE);
return;
}
stopSelf();
VoIPGroupService.callToStartFor=call;
VoIPGroupService.secretCallEncryptionKey=groupCallEncryptionKey;
Intent intent=new Intent(ApplicationLoader.applicationContext, VoIPGroupService.class);
intent.putExtra("account", currentAccount);
intent.putExtra("use_existing_call", true);
intent.putExtra("start_incall_activity", true);
intent.putExtra("need_update_self_streams", true);
intent.putExtra("private_key_emoji", getEmoji());
//intent.putExtra("forced_admin_id", user.id);
int[] uids=new int[groupUsersToAdd.size()];
for(int i=0;i<uids.length;i++)
uids[i]=groupUsersToAdd.get(i);
intent.putExtra("invite_users", uids);
ApplicationLoader.applicationContext.startService(intent);
}else{
VoIPGroupService.waitingToStart=false;
callFailed();
}
}
});
}*/
@Override
public void onConnectionStateChanged(int newState){
2020-03-30 14:00:09 +02:00
if (newState == STATE_ESTABLISHED) {
if (callStartTime == 0) {
callStartTime = SystemClock.elapsedRealtime();
}
peerCapabilities = tgVoip.getPeerCapabilities();
2018-07-30 04:07:02 +02:00
}
super.onConnectionStateChanged(newState);
}
@Override
public void onGroupCallKeyReceived(byte[] key){
joiningGroupCall=true;
groupCallEncryptionKey=key;
byte[] authKeyHash = Utilities.computeSHA1(groupCallEncryptionKey);
byte[] authKeyId = new byte[8];
System.arraycopy(authKeyHash, authKeyHash.length - 8, authKeyId, 0, 8);
groupCallKeyFingerprint=Utilities.bytesToLong(authKeyId);
}
@Override
public void onGroupCallKeySent(){
if(isOutgoing){
//actuallyUpgradeToGroupCall();
}
}
@Override
public void onCallUpgradeRequestReceived(){
upgradeToGroupCall(new ArrayList<Integer>());
}
@TargetApi(Build.VERSION_CODES.O)
@Override
public CallConnection getConnectionAndStartCall(){
if(systemCallConnection==null){
if(BuildVars.LOGS_ENABLED)
FileLog.d("creating call connection");
systemCallConnection=new CallConnection();
systemCallConnection.setInitializing();
if(isOutgoing){
delayedStartOutgoingCall=new Runnable(){
@Override
public void run(){
delayedStartOutgoingCall=null;
startOutgoingCall();
}
};
AndroidUtilities.runOnUIThread(delayedStartOutgoingCall, 2000);
}
2019-01-23 18:03:33 +01:00
systemCallConnection.setAddress(Uri.fromParts("tel", "+99084"+user.id, null), TelecomManager.PRESENTATION_ALLOWED);
2018-07-30 04:07:02 +02:00
systemCallConnection.setCallerDisplayName(ContactsController.formatName(user.first_name, user.last_name), TelecomManager.PRESENTATION_ALLOWED);
}
return systemCallConnection;
}
/*private void actuallyUpgradeToGroupCall(){
TLRPC.TL_phone_upgradePhoneCall req=new TLRPC.TL_phone_upgradePhoneCall();
req.peer=new TLRPC.TL_inputPhoneCall();
req.peer.id=call.id;
req.peer.access_hash=call.access_hash;
req.key_fingerprint=groupCallKeyFingerprint;
req.streams=VoIPGroupController.getInitialStreams();
upgrading=true;
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate(){
@Override
public void run(TLObject response, TLRPC.TL_error error){
FileLog.d("upgrade call response = "+response);
if(error!=null){
FileLog.e("Failed to upgrade call, error: "+error.code+" "+error.text);
callFailed();
return;
}
stopSelf();
TLRPC.TL_phone_groupCall call=(TLRPC.TL_phone_groupCall) response;
VoIPGroupService.callToStartFor=call;
VoIPGroupService.secretCallEncryptionKey=groupCallEncryptionKey;
VoIPGroupService.waitingToStart=true;
Intent intent=new Intent(ApplicationLoader.applicationContext, VoIPGroupService.class);
intent.putExtra("account", currentAccount);
intent.putExtra("use_existing_call", true);
intent.putExtra("start_incall_activity", true);
intent.putExtra("forced_admin_id", user.id);
intent.putExtra("private_key_emoji", getEmoji());
int[] uids=new int[groupUsersToAdd.size()];
for(int i=0;i<uids.length;i++)
uids[i]=groupUsersToAdd.get(i);
intent.putExtra("invite_users", uids);
ApplicationLoader.applicationContext.startService(intent);
}
});
}*/
2017-03-31 01:58:05 +02:00
}