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 ;
2020-08-14 18:58:22 +02:00
import android.Manifest ;
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 ;
2021-06-25 02:43:10 +02:00
import android.app.NotificationChannel ;
import android.app.NotificationManager ;
2017-03-31 01:58:05 +02:00
import android.app.PendingIntent ;
2021-06-25 02:43:10 +02:00
import android.app.Service ;
import android.bluetooth.BluetoothAdapter ;
import android.bluetooth.BluetoothDevice ;
import android.bluetooth.BluetoothHeadset ;
import android.bluetooth.BluetoothProfile ;
import android.content.BroadcastReceiver ;
import android.content.ComponentName ;
import android.content.Context ;
2017-03-31 01:58:05 +02:00
import android.content.Intent ;
2021-06-25 02:43:10 +02:00
import android.content.IntentFilter ;
2017-03-31 01:58:05 +02:00
import android.content.SharedPreferences ;
2020-08-14 18:58:22 +02:00
import android.content.pm.PackageManager ;
2021-06-25 02:43:10 +02:00
import android.graphics.Bitmap ;
import android.graphics.BitmapFactory ;
import android.graphics.Canvas ;
import android.graphics.Paint ;
import android.graphics.Path ;
import android.graphics.PorterDuff ;
import android.graphics.PorterDuffXfermode ;
import android.graphics.drawable.BitmapDrawable ;
import android.graphics.drawable.Icon ;
import android.hardware.Sensor ;
import android.hardware.SensorEvent ;
import android.hardware.SensorEventListener ;
import android.hardware.SensorManager ;
import android.media.AudioAttributes ;
import android.media.AudioFormat ;
2020-08-15 02:01:55 +02:00
import android.media.AudioManager ;
2021-06-25 02:43:10 +02:00
import android.media.AudioTrack ;
import android.media.MediaPlayer ;
import android.media.MediaRouter ;
import android.media.RingtoneManager ;
import android.media.SoundPool ;
2020-03-30 14:00:09 +02:00
import android.media.audiofx.AcousticEchoCanceler ;
import android.media.audiofx.NoiseSuppressor ;
2021-06-25 02:43:10 +02:00
import android.net.ConnectivityManager ;
import android.net.NetworkInfo ;
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 ;
2021-06-25 02:43:10 +02:00
import android.os.PowerManager ;
2020-03-30 14:00:09 +02:00
import android.os.SystemClock ;
2021-06-25 02:43:10 +02:00
import android.os.Vibrator ;
import android.telecom.CallAudioState ;
import android.telecom.Connection ;
import android.telecom.DisconnectCause ;
import android.telecom.PhoneAccount ;
import android.telecom.PhoneAccountHandle ;
2018-07-30 04:07:02 +02:00
import android.telecom.TelecomManager ;
2021-06-25 02:43:10 +02:00
import android.telephony.TelephonyManager ;
import android.text.SpannableString ;
2018-07-30 04:07:02 +02:00
import android.text.TextUtils ;
2021-06-25 02:43:10 +02:00
import android.text.style.ForegroundColorSpan ;
import android.util.LruCache ;
2017-03-31 01:58:05 +02:00
import android.view.KeyEvent ;
2021-06-25 02:43:10 +02:00
import android.view.View ;
import android.view.WindowManager ;
import android.widget.RemoteViews ;
2018-07-30 04:07:02 +02:00
import android.widget.Toast ;
2017-03-31 01:58:05 +02:00
2020-12-23 08:48:30 +01:00
import org.json.JSONObject ;
2020-12-26 06:18:43 +01:00
import org.telegram.messenger.AccountInstance ;
2017-03-31 01:58:05 +02:00
import org.telegram.messenger.AndroidUtilities ;
import org.telegram.messenger.ApplicationLoader ;
2021-06-25 02:43:10 +02:00
import org.telegram.messenger.BuildConfig ;
2017-03-31 01:58:05 +02:00
import org.telegram.messenger.BuildVars ;
2020-12-23 08:48:30 +01:00
import org.telegram.messenger.ChatObject ;
2017-03-31 01:58:05 +02:00
import org.telegram.messenger.ContactsController ;
2021-06-25 02:43:10 +02:00
import org.telegram.messenger.FileLoader ;
2017-03-31 01:58:05 +02:00
import org.telegram.messenger.FileLog ;
2021-06-25 02:43:10 +02:00
import org.telegram.messenger.ImageLoader ;
2017-03-31 01:58:05 +02:00
import org.telegram.messenger.LocaleController ;
2021-03-19 11:25:58 +01:00
import org.telegram.messenger.MessageObject ;
2017-03-31 01:58:05 +02:00
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 ;
2021-06-25 02:43:10 +02:00
import org.telegram.messenger.SharedConfig ;
import org.telegram.messenger.StatsController ;
2020-12-23 08:48:30 +01:00
import org.telegram.messenger.UserConfig ;
2021-06-25 02:43:10 +02:00
import org.telegram.messenger.UserObject ;
2017-03-31 01:58:05 +02:00
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 ;
2021-06-25 02:43:10 +02:00
import org.telegram.tgnet.TLObject ;
2017-03-31 01:58:05 +02:00
import org.telegram.tgnet.TLRPC ;
2021-06-25 02:43:10 +02:00
import org.telegram.ui.ActionBar.BottomSheet ;
import org.telegram.ui.ActionBar.Theme ;
import org.telegram.ui.Components.AvatarDrawable ;
2021-04-09 15:17:32 +02:00
import org.telegram.ui.Components.JoinCallAlert ;
2017-12-08 18:35:59 +01:00
import org.telegram.ui.Components.voip.VoIPHelper ;
2020-08-14 18:58:22 +02:00
import org.telegram.ui.LaunchActivity ;
2017-03-31 01:58:05 +02:00
import org.telegram.ui.VoIPFeedbackActivity ;
2021-06-25 02:43:10 +02:00
import org.telegram.ui.VoIPPermissionActivity ;
2020-08-14 18:58:22 +02:00
import org.webrtc.VideoFrame ;
import org.webrtc.VideoSink ;
2021-06-25 02:43:10 +02:00
import org.webrtc.voiceengine.WebRtcAudioTrack ;
2017-03-31 01:58:05 +02:00
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 ;
2021-06-25 02:43:10 +02:00
import java.lang.reflect.Field ;
import java.lang.reflect.Method ;
2017-03-31 01:58:05 +02:00
import java.math.BigInteger ;
import java.util.ArrayList ;
import java.util.Arrays ;
2021-06-25 02:43:10 +02:00
import java.util.HashMap ;
2017-07-08 18:32:04 +02:00
import java.util.HashSet ;
import java.util.Iterator ;
2020-08-14 18:58:22 +02:00
import java.util.Set ;
2017-03-31 01:58:05 +02:00
2020-08-14 18:58:22 +02:00
@SuppressLint ( " NewApi " )
2021-06-25 02:43:10 +02:00
public class VoIPService extends Service implements SensorEventListener , AudioManager . OnAudioFocusChangeListener , VoIPController . ConnectionStateListener , NotificationCenter . NotificationCenterDelegate {
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
2021-06-25 02:43:10 +02:00
public static final int STATE_WAIT_INIT = Instance . STATE_WAIT_INIT ;
public static final int STATE_WAIT_INIT_ACK = Instance . STATE_WAIT_INIT_ACK ;
public static final int STATE_ESTABLISHED = Instance . STATE_ESTABLISHED ;
public static final int STATE_FAILED = Instance . STATE_FAILED ;
public static final int STATE_RECONNECTING = Instance . STATE_RECONNECTING ;
public static final int STATE_CREATING = 6 ;
public static final int STATE_ENDED = 11 ;
public static final String ACTION_HEADSET_PLUG = " android.intent.action.HEADSET_PLUG " ;
private static final int ID_ONGOING_CALL_NOTIFICATION = 201 ;
private static final int ID_INCOMING_CALL_NOTIFICATION = 202 ;
public static final int QUALITY_SMALL = 0 ;
public static final int QUALITY_MEDIUM = 1 ;
public static final int QUALITY_FULL = 2 ;
public static final int CAPTURE_DEVICE_CAMERA = 0 ;
public static final int CAPTURE_DEVICE_SCREEN = 1 ;
public static final int DISCARD_REASON_HANGUP = 1 ;
public static final int DISCARD_REASON_DISCONNECT = 2 ;
public static final int DISCARD_REASON_MISSED = 3 ;
public static final int DISCARD_REASON_LINE_BUSY = 4 ;
public static final int AUDIO_ROUTE_EARPIECE = 0 ;
public static final int AUDIO_ROUTE_SPEAKER = 1 ;
public static final int AUDIO_ROUTE_BLUETOOTH = 2 ;
private static final boolean USE_CONNECTION_SERVICE = isDeviceCompatibleWithConnectionServiceAPI ( ) ;
private int currentAccount = - 1 ;
private static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32 ;
private static VoIPService sharedInstance ;
private static Runnable setModeRunnable ;
private static final Object sync = new Object ( ) ;
private NetworkInfo lastNetInfo ;
private int currentState = 0 ;
private boolean wasConnected ;
private boolean reconnectScreenCapture ;
private int currentStreamRequestId ;
private TLRPC . Chat chat ;
private boolean isVideoAvailable ;
private boolean notificationsDisabled ;
private boolean switchingCamera ;
private boolean isFrontFaceCamera = true ;
private String lastError ;
private PowerManager . WakeLock proximityWakelock ;
private PowerManager . WakeLock cpuWakelock ;
private boolean isProximityNear ;
private boolean isHeadsetPlugged ;
private int previousAudioOutput = - 1 ;
private ArrayList < StateListener > stateListeners = new ArrayList < > ( ) ;
private MediaPlayer ringtonePlayer ;
private Vibrator vibrator ;
private SoundPool soundPool ;
private int spRingbackID ;
private int spFailedID ;
private int spEndId ;
private int spVoiceChatEndId ;
private int spVoiceChatStartId ;
private int spVoiceChatConnecting ;
private int spBusyId ;
private int spConnectingId ;
private int spPlayId ;
private int spStartRecordId ;
private int spAllowTalkId ;
private boolean needPlayEndSound ;
private boolean hasAudioFocus ;
private boolean micMute ;
private boolean unmutedByHold ;
private BluetoothAdapter btAdapter ;
private Instance . TrafficStats prevTrafficStats ;
private boolean isBtHeadsetConnected ;
private Runnable updateNotificationRunnable ;
private Runnable onDestroyRunnable ;
private Runnable switchingStreamTimeoutRunnable ;
private boolean playedConnectedSound ;
private boolean switchingStream ;
private boolean switchingAccount ;
public TLRPC . PhoneCall privateCall ;
public ChatObject . Call groupCall ;
public boolean currentGroupModeStreaming ;
private boolean createGroupCall ;
private int scheduleDate ;
private TLRPC . InputPeer groupCallPeer ;
public boolean hasFewPeers ;
private String joinHash ;
private int remoteVideoState = Instance . VIDEO_STATE_INACTIVE ;
private TLRPC . TL_dataJSON myParams ;
private int [ ] mySource = new int [ 2 ] ;
private NativeInstance [ ] tgVoip = new NativeInstance [ 2 ] ;
private long [ ] captureDevice = new long [ 2 ] ;
private boolean [ ] destroyCaptureDevice = { true , true } ;
private int [ ] videoState = { Instance . VIDEO_STATE_INACTIVE , Instance . VIDEO_STATE_INACTIVE } ;
private long callStartTime ;
private boolean playingSound ;
private boolean isOutgoing ;
public boolean videoCall ;
private Runnable timeoutRunnable ;
private Boolean mHasEarpiece ;
private boolean wasEstablished ;
private int signalBarCount ;
private int remoteAudioState = Instance . AUDIO_STATE_ACTIVE ;
private boolean audioConfigured ;
private int audioRouteToSet = AUDIO_ROUTE_BLUETOOTH ;
private boolean speakerphoneStateToSet ;
private CallConnection systemCallConnection ;
private int callDiscardReason ;
private boolean bluetoothScoActive ;
private boolean bluetoothScoConnecting ;
private boolean needSwitchToBluetoothAfterScoActivates ;
private boolean didDeleteConnectionServiceContact ;
private Runnable connectingSoundRunnable ;
public String currentBluetoothDeviceName ;
public final SharedUIParams sharedUIParams = new SharedUIParams ( ) ;
2017-03-31 01:58:05 +02:00
private TLRPC . User user ;
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-12-23 08:48:30 +01:00
public static NativeInstance . AudioLevelsCallback audioLevelsCallback ;
2020-03-30 14:00:09 +02:00
2020-12-23 08:48:30 +01:00
private boolean needSendDebugLog ;
private boolean needRateCall ;
private long lastTypingTimeSend ;
private boolean endCallAfterRequest ;
2020-08-14 18:58:22 +02:00
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
2020-12-23 08:48:30 +01:00
private boolean startedRinging ;
2019-03-03 21:40:48 +01:00
2021-03-19 11:25:58 +01:00
private int classGuid ;
private long currentStreamRequestTimestamp ;
2021-06-25 02:43:10 +02:00
public boolean micSwitching ;
private Runnable afterSoundRunnable = new Runnable ( ) {
@Override
public void run ( ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
am . abandonAudioFocus ( VoIPService . this ) ;
am . unregisterMediaButtonEventReceiver ( new ComponentName ( VoIPService . this , VoIPMediaButtonReceiver . class ) ) ;
if ( ! USE_CONNECTION_SERVICE & & sharedInstance = = null ) {
if ( isBtHeadsetConnected ) {
am . stopBluetoothSco ( ) ;
am . setBluetoothScoOn ( false ) ;
bluetoothScoActive = false ;
bluetoothScoConnecting = false ;
}
am . setSpeakerphoneOn ( false ) ;
}
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . release ( ) ) ;
Utilities . globalQueue . postRunnable ( setModeRunnable = ( ) - > {
synchronized ( sync ) {
if ( setModeRunnable = = null ) {
return ;
}
setModeRunnable = null ;
}
try {
am . setMode ( AudioManager . MODE_NORMAL ) ;
} catch ( SecurityException x ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error setting audio more to normal " , x ) ;
}
}
} ) ;
}
} ;
boolean fetchingBluetoothDeviceName ;
private BluetoothProfile . ServiceListener serviceListener = new BluetoothProfile . ServiceListener ( ) {
@Override
public void onServiceDisconnected ( int profile ) {
}
@Override
public void onServiceConnected ( int profile , BluetoothProfile proxy ) {
for ( BluetoothDevice device : proxy . getConnectedDevices ( ) ) {
if ( proxy . getConnectionState ( device ) ! = BluetoothProfile . STATE_CONNECTED ) {
continue ;
}
currentBluetoothDeviceName = device . getName ( ) ;
break ;
}
BluetoothAdapter . getDefaultAdapter ( ) . closeProfileProxy ( profile , proxy ) ;
fetchingBluetoothDeviceName = false ;
}
} ;
private BroadcastReceiver receiver = new BroadcastReceiver ( ) {
@Override
public void onReceive ( Context context , Intent intent ) {
if ( ACTION_HEADSET_PLUG . equals ( intent . getAction ( ) ) ) {
isHeadsetPlugged = intent . getIntExtra ( " state " , 0 ) = = 1 ;
if ( isHeadsetPlugged & & proximityWakelock ! = null & & proximityWakelock . isHeld ( ) ) {
proximityWakelock . release ( ) ;
}
if ( isHeadsetPlugged ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( am . isSpeakerphoneOn ( ) ) {
previousAudioOutput = 0 ;
} else if ( am . isBluetoothScoOn ( ) ) {
previousAudioOutput = 2 ;
} else {
previousAudioOutput = 1 ;
}
setAudioOutput ( 1 ) ;
} else {
if ( previousAudioOutput > = 0 ) {
setAudioOutput ( previousAudioOutput ) ;
previousAudioOutput = - 1 ;
}
}
isProximityNear = false ;
updateOutputGainControlState ( ) ;
} else if ( ConnectivityManager . CONNECTIVITY_ACTION . equals ( intent . getAction ( ) ) ) {
updateNetworkType ( ) ;
} else if ( BluetoothHeadset . ACTION_CONNECTION_STATE_CHANGED . equals ( intent . getAction ( ) ) ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " bt headset state = " + intent . getIntExtra ( BluetoothProfile . EXTRA_STATE , 0 ) ) ;
}
updateBluetoothHeadsetState ( intent . getIntExtra ( BluetoothProfile . EXTRA_STATE , BluetoothProfile . STATE_DISCONNECTED ) = = BluetoothProfile . STATE_CONNECTED ) ;
} else if ( AudioManager . ACTION_SCO_AUDIO_STATE_UPDATED . equals ( intent . getAction ( ) ) ) {
int state = intent . getIntExtra ( AudioManager . EXTRA_SCO_AUDIO_STATE , AudioManager . SCO_AUDIO_STATE_DISCONNECTED ) ;
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Bluetooth SCO state updated: " + state ) ;
}
if ( state = = AudioManager . SCO_AUDIO_STATE_DISCONNECTED & & isBtHeadsetConnected ) {
if ( ! btAdapter . isEnabled ( ) | | btAdapter . getProfileConnectionState ( BluetoothProfile . HEADSET ) ! = BluetoothProfile . STATE_CONNECTED ) {
updateBluetoothHeadsetState ( false ) ;
return ;
}
}
bluetoothScoConnecting = state = = AudioManager . SCO_AUDIO_STATE_CONNECTING ;
bluetoothScoActive = state = = AudioManager . SCO_AUDIO_STATE_CONNECTED ;
if ( bluetoothScoActive ) {
fetchBluetoothDeviceName ( ) ;
if ( needSwitchToBluetoothAfterScoActivates ) {
needSwitchToBluetoothAfterScoActivates = false ;
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
am . setSpeakerphoneOn ( false ) ;
am . setBluetoothScoOn ( true ) ;
}
}
for ( VoIPService . StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
} else if ( TelephonyManager . ACTION_PHONE_STATE_CHANGED . equals ( intent . getAction ( ) ) ) {
String state = intent . getStringExtra ( TelephonyManager . EXTRA_STATE ) ;
if ( TelephonyManager . EXTRA_STATE_OFFHOOK . equals ( state ) ) {
hangUp ( ) ;
}
} else if ( Intent . ACTION_SCREEN_ON . equals ( intent . getAction ( ) ) ) {
for ( int i = 0 ; i < stateListeners . size ( ) ; i + + ) {
stateListeners . get ( i ) . onScreenOnChange ( true ) ;
}
} else if ( Intent . ACTION_SCREEN_OFF . equals ( intent . getAction ( ) ) ) {
for ( int i = 0 ; i < stateListeners . size ( ) ; i + + ) {
stateListeners . get ( i ) . onScreenOnChange ( false ) ;
}
}
}
} ;
2021-03-19 11:25:58 +01:00
2020-08-14 18:58:22 +02:00
public boolean isFrontFaceCamera ( ) {
return isFrontFaceCamera ;
}
2021-03-19 11:25:58 +01:00
public void setMicMute ( boolean mute , boolean hold , boolean send ) {
2021-06-25 02:43:10 +02:00
if ( micMute = = mute | | micSwitching ) {
2021-03-19 11:25:58 +01:00
return ;
}
micMute = mute ;
if ( groupCall ! = null ) {
if ( ! send ) {
TLRPC . TL_groupCallParticipant self = groupCall . participants . get ( getSelfId ( ) ) ;
if ( self ! = null & & self . muted & & ! self . can_self_unmute ) {
send = true ;
}
}
if ( send ) {
2021-06-25 02:43:10 +02:00
editCallMember ( UserConfig . getInstance ( currentAccount ) . getCurrentUser ( ) , mute , null , null , null , null ) ;
2021-03-19 11:25:58 +01:00
Utilities . globalQueue . postRunnable ( updateNotificationRunnable = ( ) - > {
if ( updateNotificationRunnable = = null ) {
return ;
}
updateNotificationRunnable = null ;
showNotification ( chat . title , getRoundAvatarBitmap ( chat ) ) ;
} ) ;
}
}
unmutedByHold = ! micMute & & hold ;
2021-07-15 16:24:57 +02:00
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ) {
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setMuteMicrophone ( mute ) ;
2021-03-19 11:25:58 +01:00
}
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
}
2020-12-23 08:48:30 +01:00
public boolean mutedByAdmin ( ) {
2021-03-19 11:25:58 +01:00
ChatObject . Call call = groupCall ;
2020-12-23 08:48:30 +01:00
if ( call ! = null ) {
2021-03-19 11:25:58 +01:00
int selfId = getSelfId ( ) ;
TLRPC . TL_groupCallParticipant participant = call . participants . get ( selfId ) ;
if ( participant ! = null & & ! participant . can_self_unmute & & participant . muted & & ! ChatObject . canManageCalls ( chat ) ) {
2020-12-23 08:48:30 +01:00
return true ;
}
}
return false ;
}
2021-06-25 02:43:10 +02:00
private final HashMap < String , TLRPC . TL_groupCallParticipant > waitingFrameParticipant = new HashMap < > ( ) ;
private final LruCache < String , ProxyVideoSink > proxyVideoSinkLruCache = new LruCache < String , ProxyVideoSink > ( 6 ) {
@Override
protected void entryRemoved ( boolean evicted , String key , ProxyVideoSink oldValue , ProxyVideoSink newValue ) {
super . entryRemoved ( evicted , key , oldValue , newValue ) ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . removeIncomingVideoOutput ( oldValue . nativeInstance ) ;
}
} ;
public boolean hasVideoCapturer ( ) {
return captureDevice [ CAPTURE_DEVICE_CAMERA ] ! = 0 ;
}
public void checkVideoFrame ( TLRPC . TL_groupCallParticipant participant , boolean screencast ) {
String endpointId = screencast ? participant . presentationEndpoint : participant . videoEndpoint ;
if ( endpointId = = null ) {
return ;
}
if ( ( screencast & & participant . hasPresentationFrame ! = ChatObject . VIDEO_FRAME_NO_FRAME ) | | ( ! screencast & & participant . hasCameraFrame ! = ChatObject . VIDEO_FRAME_NO_FRAME ) ) {
return ;
}
if ( proxyVideoSinkLruCache . get ( endpointId ) ! = null | | ( remoteSinks . get ( endpointId ) ! = null & & waitingFrameParticipant . get ( endpointId ) = = null ) ) {
if ( screencast ) {
participant . hasPresentationFrame = ChatObject . VIDEO_FRAME_HAS_FRAME ;
} else {
participant . hasCameraFrame = ChatObject . VIDEO_FRAME_HAS_FRAME ;
}
return ;
}
if ( waitingFrameParticipant . containsKey ( endpointId ) ) {
waitingFrameParticipant . put ( endpointId , participant ) ;
if ( screencast ) {
participant . hasPresentationFrame = ChatObject . VIDEO_FRAME_REQUESTING ;
} else {
participant . hasCameraFrame = ChatObject . VIDEO_FRAME_REQUESTING ;
}
return ;
}
if ( screencast ) {
participant . hasPresentationFrame = ChatObject . VIDEO_FRAME_REQUESTING ;
} else {
participant . hasCameraFrame = ChatObject . VIDEO_FRAME_REQUESTING ;
}
waitingFrameParticipant . put ( endpointId , participant ) ;
addRemoteSink ( participant , screencast , new VideoSink ( ) {
@Override
public void onFrame ( VideoFrame frame ) {
VideoSink thisSink = this ;
if ( frame ! = null & & frame . getBuffer ( ) . getHeight ( ) ! = 0 & & frame . getBuffer ( ) . getWidth ( ) ! = 0 ) {
AndroidUtilities . runOnUIThread ( ( ) - > {
TLRPC . TL_groupCallParticipant currentParticipant = waitingFrameParticipant . remove ( endpointId ) ;
ProxyVideoSink proxyVideoSink = remoteSinks . get ( endpointId ) ;
if ( proxyVideoSink ! = null & & proxyVideoSink . target = = thisSink ) {
proxyVideoSinkLruCache . put ( endpointId , proxyVideoSink ) ;
remoteSinks . remove ( endpointId ) ;
proxyVideoSink . setTarget ( null ) ;
}
if ( currentParticipant ! = null ) {
if ( screencast ) {
currentParticipant . hasPresentationFrame = ChatObject . VIDEO_FRAME_HAS_FRAME ;
} else {
currentParticipant . hasCameraFrame = ChatObject . VIDEO_FRAME_HAS_FRAME ;
}
}
if ( groupCall ! = null ) {
groupCall . updateVisibleParticipants ( ) ;
}
} ) ;
}
}
} , null ) ;
}
public void clearRemoteSinks ( ) {
proxyVideoSinkLruCache . evictAll ( ) ;
}
public void setAudioRoute ( int route ) {
if ( route = = AUDIO_ROUTE_SPEAKER ) {
setAudioOutput ( 0 ) ;
} else if ( route = = AUDIO_ROUTE_EARPIECE ) {
setAudioOutput ( 1 ) ;
} else if ( route = = AUDIO_ROUTE_BLUETOOTH ) {
setAudioOutput ( 2 ) ;
}
}
public static class ProxyVideoSink implements VideoSink {
2020-08-14 18:58:22 +02:00
private VideoSink target ;
private VideoSink background ;
2021-06-25 02:43:10 +02:00
private long nativeInstance ;
2020-08-14 18:58:22 +02:00
@Override
synchronized public void onFrame ( VideoFrame frame ) {
2021-06-25 02:43:10 +02:00
if ( target ! = null ) {
target . onFrame ( frame ) ;
2020-08-14 18:58:22 +02:00
}
if ( background ! = null ) {
background . onFrame ( frame ) ;
}
}
2021-06-25 02:43:10 +02:00
synchronized public void setTarget ( VideoSink newTarget ) {
if ( target ! = newTarget ) {
if ( target ! = null ) {
target . setParentSink ( null ) ;
}
target = newTarget ;
if ( target ! = null ) {
target . setParentSink ( this ) ;
}
}
}
synchronized public void setBackground ( VideoSink newBackground ) {
if ( background ! = null ) {
background . setParentSink ( null ) ;
}
background = newBackground ;
if ( background ! = null ) {
background . setParentSink ( this ) ;
}
}
synchronized public void removeTarget ( VideoSink target ) {
if ( this . target = = target ) {
this . target = null ;
}
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
synchronized public void removeBackground ( VideoSink background ) {
if ( this . background = = background ) {
this . background = null ;
}
2020-08-14 18:58:22 +02:00
}
synchronized public void swap ( ) {
if ( target ! = null & & background ! = null ) {
target = background ;
background = null ;
}
}
}
2021-06-25 02:43:10 +02:00
private ProxyVideoSink [ ] localSink = new ProxyVideoSink [ 2 ] ;
private ProxyVideoSink [ ] remoteSink = new ProxyVideoSink [ 2 ] ;
private ProxyVideoSink [ ] currentBackgroundSink = new ProxyVideoSink [ 2 ] ;
private String [ ] currentBackgroundEndpointId = new String [ 2 ] ;
private HashMap < String , ProxyVideoSink > remoteSinks = new HashMap < > ( ) ;
2017-03-31 01:58:05 +02:00
@Nullable
@Override
public IBinder onBind ( Intent intent ) {
return null ;
}
2020-08-14 18:58:22 +02:00
@SuppressLint ( { " MissingPermission " , " InlinedApi " } )
2017-03-31 01:58:05 +02:00
@Override
public int onStartCommand ( Intent intent , int flags , int startId ) {
2020-08-14 18:58:22 +02:00
if ( sharedInstance ! = null ) {
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
2020-08-14 18:58:22 +02:00
currentAccount = intent . getIntExtra ( " account " , - 1 ) ;
if ( currentAccount = = - 1 ) {
2018-07-30 04:07:02 +02:00
throw new IllegalStateException ( " No account specified when starting VoIP service " ) ;
2020-08-14 18:58:22 +02:00
}
2021-03-19 11:25:58 +01:00
classGuid = ConnectionsManager . generateClassGuid ( ) ;
2020-08-14 18:58:22 +02:00
int userID = intent . getIntExtra ( " user_id " , 0 ) ;
2020-12-23 08:48:30 +01:00
int chatID = intent . getIntExtra ( " chat_id " , 0 ) ;
createGroupCall = intent . getBooleanExtra ( " createGroupCall " , false ) ;
2021-03-19 11:25:58 +01:00
hasFewPeers = intent . getBooleanExtra ( " hasFewPeers " , false ) ;
joinHash = intent . getStringExtra ( " hash " ) ;
int peerChannelId = intent . getIntExtra ( " peerChannelId " , 0 ) ;
int peerChatId = intent . getIntExtra ( " peerChatId " , 0 ) ;
int peerUserId = intent . getIntExtra ( " peerUserId " , 0 ) ;
if ( peerChatId ! = 0 ) {
groupCallPeer = new TLRPC . TL_inputPeerChat ( ) ;
groupCallPeer . chat_id = peerChatId ;
groupCallPeer . access_hash = intent . getLongExtra ( " peerAccessHash " , 0 ) ;
} else if ( peerChannelId ! = 0 ) {
groupCallPeer = new TLRPC . TL_inputPeerChannel ( ) ;
groupCallPeer . channel_id = peerChannelId ;
groupCallPeer . access_hash = intent . getLongExtra ( " peerAccessHash " , 0 ) ;
} else if ( peerUserId ! = 0 ) {
groupCallPeer = new TLRPC . TL_inputPeerUser ( ) ;
groupCallPeer . user_id = peerUserId ;
groupCallPeer . access_hash = intent . getLongExtra ( " peerAccessHash " , 0 ) ;
}
2021-04-14 03:44:46 +02:00
scheduleDate = intent . getIntExtra ( " scheduleDate " , 0 ) ;
2021-03-19 11:25:58 +01:00
2017-03-31 01:58:05 +02:00
isOutgoing = intent . getBooleanExtra ( " is_outgoing " , false ) ;
2020-08-14 18:58:22 +02:00
videoCall = intent . getBooleanExtra ( " video_call " , false ) ;
isVideoAvailable = intent . getBooleanExtra ( " can_video_call " , false ) ;
2020-08-15 02:01:55 +02:00
notificationsDisabled = intent . getBooleanExtra ( " notifications_disabled " , false ) ;
2020-12-23 08:48:30 +01:00
if ( userID ! = 0 ) {
user = MessagesController . getInstance ( currentAccount ) . getUser ( userID ) ;
}
if ( chatID ! = 0 ) {
chat = MessagesController . getInstance ( currentAccount ) . getChat ( chatID ) ;
2021-03-19 11:25:58 +01:00
if ( ChatObject . isChannel ( chat ) ) {
MessagesController . getInstance ( currentAccount ) . startShortPoll ( chat , classGuid , false ) ;
}
2020-12-23 08:48:30 +01:00
}
2021-04-09 15:17:32 +02:00
loadResources ( ) ;
2021-06-25 02:43:10 +02:00
for ( int a = 0 ; a < localSink . length ; a + + ) {
localSink [ a ] = new ProxyVideoSink ( ) ;
remoteSink [ a ] = new ProxyVideoSink ( ) ;
}
2020-08-15 02:01:55 +02:00
try {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
isHeadsetPlugged = am . isWiredHeadsetOn ( ) ;
} catch ( Exception e ) {
FileLog . e ( e ) ;
}
2020-12-23 08:48:30 +01:00
if ( chat ! = null & & ! createGroupCall ) {
ChatObject . Call call = MessagesController . getInstance ( currentAccount ) . getGroupCall ( chat . id , false ) ;
if ( call = = null ) {
FileLog . w ( " VoIPService: trying to open group call without call " + chat . id ) ;
stopSelf ( ) ;
return START_NOT_STICKY ;
}
}
2020-08-14 18:58:22 +02:00
if ( videoCall ) {
2021-06-25 02:43:10 +02:00
if ( Build . VERSION . SDK_INT < 23 | | checkSelfPermission ( Manifest . permission . CAMERA ) = = PackageManager . PERMISSION_GRANTED ) {
captureDevice [ CAPTURE_DEVICE_CAMERA ] = NativeInstance . createVideoCapturer ( localSink [ CAPTURE_DEVICE_CAMERA ] , isFrontFaceCamera ? 1 : 0 ) ;
if ( chatID ! = 0 ) {
videoState [ CAPTURE_DEVICE_CAMERA ] = Instance . VIDEO_STATE_PAUSED ;
} else {
videoState [ CAPTURE_DEVICE_CAMERA ] = Instance . VIDEO_STATE_ACTIVE ;
}
} else {
videoState [ CAPTURE_DEVICE_CAMERA ] = Instance . VIDEO_STATE_PAUSED ;
}
2020-08-15 02:01:55 +02:00
if ( ! isBtHeadsetConnected & & ! isHeadsetPlugged ) {
2020-08-14 18:58:22 +02:00
setAudioOutput ( 0 ) ;
}
}
2017-03-31 01:58:05 +02:00
2020-12-23 08:48:30 +01:00
if ( user = = null & & chat = = null ) {
2020-08-14 18:58:22 +02:00
if ( BuildVars . LOGS_ENABLED ) {
2020-12-23 08:48:30 +01:00
FileLog . w ( " VoIPService: user == null AND chat == null " ) ;
2020-08-14 18:58:22 +02:00
}
2017-03-31 01:58:05 +02:00
stopSelf ( ) ;
return START_NOT_STICKY ;
}
2018-07-30 04:07:02 +02:00
sharedInstance = this ;
2020-12-23 08:48:30 +01:00
synchronized ( sync ) {
if ( setModeRunnable ! = null ) {
Utilities . globalQueue . cancelRunnable ( setModeRunnable ) ;
setModeRunnable = null ;
}
}
2017-03-31 01:58:05 +02:00
if ( isOutgoing ) {
2020-12-23 08:48:30 +01:00
if ( user ! = null ) {
dispatchStateChanged ( STATE_REQUESTING ) ;
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 ) ;
ContactsController . getInstance ( currentAccount ) . createOrUpdateConnectionServiceContact ( user . id , user . first_name , user . last_name ) ;
tm . placeCall ( Uri . fromParts ( " tel " , " +99084 " + user . id , null ) , extras ) ;
} else {
delayedStartOutgoingCall = ( ) - > {
delayedStartOutgoingCall = null ;
startOutgoingCall ( ) ;
} ;
AndroidUtilities . runOnUIThread ( delayedStartOutgoingCall , 2000 ) ;
}
2020-08-14 18:58:22 +02:00
} else {
2020-12-23 08:48:30 +01:00
micMute = true ;
2021-03-19 11:25:58 +01:00
startGroupCall ( 0 , null , false ) ;
2020-12-23 08:48:30 +01:00
if ( ! isBtHeadsetConnected & & ! isHeadsetPlugged ) {
setAudioOutput ( 0 ) ;
}
2018-07-30 04:07:02 +02:00
}
2017-03-31 01:58:05 +02:00
if ( intent . getBooleanExtra ( " start_incall_activity " , false ) ) {
2020-12-23 08:48:30 +01:00
Intent intent1 = new Intent ( this , LaunchActivity . class ) . setAction ( user ! = null ? " voip " : " voip_chat " ) . addFlags ( Intent . FLAG_ACTIVITY_NEW_TASK ) ;
if ( chat ! = null ) {
intent1 . putExtra ( " currentAccount " , currentAccount ) ;
}
startActivity ( intent1 ) ;
2017-03-31 01:58:05 +02:00
}
} else {
2018-07-30 04:07:02 +02:00
NotificationCenter . getGlobalInstance ( ) . postNotificationName ( NotificationCenter . closeInCallActivity ) ;
2020-12-23 08:48:30 +01:00
privateCall = callIShouldHavePutIntoIntent ;
videoCall = privateCall ! = null & & privateCall . video ;
2020-08-14 18:58:22 +02:00
if ( videoCall ) {
isVideoAvailable = true ;
}
2020-08-15 02:01:55 +02:00
if ( videoCall & & ! isBtHeadsetConnected & & ! isHeadsetPlugged ) {
2020-08-14 18:58:22 +02:00
setAudioOutput ( 0 ) ;
}
2017-03-31 01:58:05 +02:00
callIShouldHavePutIntoIntent = null ;
2020-08-14 18:58:22 +02:00
if ( USE_CONNECTION_SERVICE ) {
2018-07-30 04:07:02 +02:00
acknowledgeCall ( false ) ;
showNotification ( ) ;
2020-08-14 18:58:22 +02:00
} else {
2018-07-30 04:07:02 +02:00
acknowledgeCall ( true ) ;
}
2017-03-31 01:58:05 +02:00
}
2018-07-30 04:07:02 +02:00
initializeAccountRelatedThings ( ) ;
2020-08-14 18:58:22 +02:00
AndroidUtilities . runOnUIThread ( ( ) - > NotificationCenter . getInstance ( currentAccount ) . postNotificationName ( NotificationCenter . voipServiceCreated ) ) ;
2017-03-31 01:58:05 +02:00
return START_NOT_STICKY ;
}
public static VoIPService getSharedInstance ( ) {
2021-06-25 02:43:10 +02:00
return sharedInstance ;
2017-03-31 01:58:05 +02:00
}
public TLRPC . User getUser ( ) {
return user ;
}
2020-12-23 08:48:30 +01:00
public TLRPC . Chat getChat ( ) {
return chat ;
}
2021-06-25 02:43:10 +02:00
public void setNoiseSupressionEnabled ( boolean enabled ) {
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] = = null ) {
return ;
}
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setNoiseSuppressionEnabled ( enabled ) ;
}
2021-03-19 11:25:58 +01:00
public void setGroupCallHash ( String hash ) {
if ( ! currentGroupModeStreaming | | TextUtils . isEmpty ( hash ) | | hash . equals ( joinHash ) ) {
return ;
}
joinHash = hash ;
2021-06-25 02:43:10 +02:00
createGroupInstance ( CAPTURE_DEVICE_CAMERA , false ) ;
2021-03-19 11:25:58 +01:00
}
2020-12-23 08:48:30 +01:00
public int getCallerId ( ) {
if ( user ! = null ) {
return user . id ;
} else {
return - chat . id ;
}
}
public void hangUp ( int discard , Runnable onDone ) {
2020-08-14 18:58:22 +02:00
declineIncomingCall ( currentState = = STATE_RINGING | | ( currentState = = STATE_WAITING & & isOutgoing ) ? DISCARD_REASON_MISSED : DISCARD_REASON_HANGUP , onDone ) ;
2020-12-23 08:48:30 +01:00
if ( groupCall ! = null ) {
if ( discard = = 2 ) {
return ;
}
if ( discard = = 1 ) {
TLRPC . ChatFull chatFull = MessagesController . getInstance ( currentAccount ) . getChatFull ( chat . id ) ;
if ( chatFull ! = null ) {
chatFull . flags & = ~ 2097152 ;
chatFull . call = null ;
NotificationCenter . getInstance ( currentAccount ) . postNotificationName ( NotificationCenter . groupCallUpdated , chat . id , groupCall . call . id , false ) ;
}
TLRPC . TL_phone_discardGroupCall req = new TLRPC . TL_phone_discardGroupCall ( ) ;
req . call = groupCall . getInputGroupCall ( ) ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
if ( response instanceof TLRPC . TL_updates ) {
TLRPC . TL_updates updates = ( TLRPC . TL_updates ) response ;
MessagesController . getInstance ( currentAccount ) . processUpdates ( updates , false ) ;
}
} ) ;
} else {
TLRPC . TL_phone_leaveGroupCall req = new TLRPC . TL_phone_leaveGroupCall ( ) ;
req . call = groupCall . getInputGroupCall ( ) ;
2021-06-25 02:43:10 +02:00
req . source = mySource [ CAPTURE_DEVICE_CAMERA ] ;
2020-12-23 08:48:30 +01:00
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
if ( response instanceof TLRPC . TL_updates ) {
TLRPC . TL_updates updates = ( TLRPC . TL_updates ) response ;
MessagesController . getInstance ( currentAccount ) . processUpdates ( updates , false ) ;
}
} ) ;
}
}
2017-03-31 01:58:05 +02:00
}
private void startOutgoingCall ( ) {
2020-08-14 18:58:22 +02:00
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null ) {
2018-07-30 04:07:02 +02:00
systemCallConnection . setDialing ( ) ;
2020-08-14 18:58:22 +02:00
}
2017-03-31 01:58:05 +02:00
configureDeviceForCall ( ) ;
showNotification ( ) ;
startConnectingSound ( ) ;
dispatchStateChanged ( STATE_REQUESTING ) ;
2020-08-14 18:58:22 +02:00
AndroidUtilities . runOnUIThread ( ( ) - > 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 ( ) ;
2020-08-14 18:58:22 +02:00
callReqId = ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
callReqId = 0 ;
if ( endCallAfterRequest ) {
callEnded ( ) ;
return ;
}
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 ;
2017-03-31 01:58:05 +02:00
}
2020-08-14 18:58:22 +02:00
messagesStorage . setSecretPBytes ( res . p ) ;
messagesStorage . setSecretG ( res . g ) ;
messagesStorage . setLastSecretVersion ( res . version ) ;
messagesStorage . saveSecretParams ( messagesStorage . getLastSecretVersion ( ) , messagesStorage . getSecretG ( ) , messagesStorage . getSecretPBytes ( ) ) ;
}
final byte [ ] salt1 = new byte [ 256 ] ;
for ( int a = 0 ; a < 256 ; a + + ) {
salt1 [ a ] = ( byte ) ( ( byte ) ( Utilities . random . nextDouble ( ) * 256 ) ^ res . random [ a ] ) ;
}
2017-03-31 01:58:05 +02:00
2020-08-14 18:58:22 +02:00
BigInteger i_g_a = BigInteger . valueOf ( messagesStorage . getSecretG ( ) ) ;
i_g_a = i_g_a . modPow ( new BigInteger ( 1 , salt1 ) , new BigInteger ( 1 , messagesStorage . getSecretPBytes ( ) ) ) ;
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 ;
}
2017-03-31 01:58:05 +02:00
2020-08-14 18:58:22 +02:00
TLRPC . TL_phone_requestCall reqCall = new TLRPC . TL_phone_requestCall ( ) ;
reqCall . user_id = MessagesController . getInstance ( currentAccount ) . getInputUser ( user ) ;
reqCall . protocol = new TLRPC . TL_phoneCallProtocol ( ) ;
reqCall . video = videoCall ;
reqCall . protocol . udp_p2p = true ;
reqCall . protocol . udp_reflector = true ;
reqCall . protocol . min_layer = CALL_MIN_LAYER ;
reqCall . protocol . max_layer = Instance . getConnectionMaxLayer ( ) ;
reqCall . protocol . library_versions . addAll ( Instance . AVAILABLE_VERSIONS ) ;
VoIPService . this . g_a = g_a ;
reqCall . g_a_hash = Utilities . computeSHA256 ( g_a , 0 , g_a . length ) ;
reqCall . random_id = Utilities . random . nextInt ( ) ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( reqCall , ( response12 , error12 ) - > AndroidUtilities . runOnUIThread ( ( ) - > {
if ( error12 = = null ) {
2020-12-23 08:48:30 +01:00
privateCall = ( ( TLRPC . TL_phone_phoneCall ) response12 ) . phone_call ;
2020-08-14 18:58:22 +02:00
a_or_b = salt1 ;
dispatchStateChanged ( STATE_WAITING ) ;
if ( endCallAfterRequest ) {
hangUp ( ) ;
return ;
}
2020-12-23 08:48:30 +01:00
if ( pendingUpdates . size ( ) > 0 & & privateCall ! = null ) {
2020-08-14 18:58:22 +02:00
for ( TLRPC . PhoneCall call : pendingUpdates ) {
onCallUpdated ( call ) ;
}
pendingUpdates . clear ( ) ;
}
timeoutRunnable = ( ) - > {
timeoutRunnable = null ;
TLRPC . TL_phone_discardCall req1 = new TLRPC . TL_phone_discardCall ( ) ;
req1 . peer = new TLRPC . TL_inputPhoneCall ( ) ;
2020-12-23 08:48:30 +01:00
req1 . peer . access_hash = privateCall . access_hash ;
req1 . peer . id = privateCall . id ;
2020-08-14 18:58:22 +02:00
req1 . reason = new TLRPC . TL_phoneCallDiscardReasonMissed ( ) ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req1 , ( response1 , error1 ) - > {
if ( BuildVars . LOGS_ENABLED ) {
if ( error1 ! = null ) {
FileLog . e ( " error on phone.discardCall: " + error1 ) ;
2017-03-31 01:58:05 +02:00
} else {
2020-08-14 18:58:22 +02:00
FileLog . d ( " phone.discardCall " + response1 ) ;
2017-03-31 01:58:05 +02:00
}
}
2020-08-14 18:58:22 +02:00
AndroidUtilities . runOnUIThread ( VoIPService . this : : callFailed ) ;
} , ConnectionsManager . RequestFlagFailOnServerErrors ) ;
} ;
AndroidUtilities . runOnUIThread ( timeoutRunnable , MessagesController . getInstance ( currentAccount ) . callReceiveTimeout ) ;
} else {
if ( error12 . code = = 400 & & " PARTICIPANT_VERSION_OUTDATED " . equals ( error12 . text ) ) {
callFailed ( Instance . ERROR_PEER_OUTDATED ) ;
} else if ( error12 . code = = 403 ) {
callFailed ( Instance . ERROR_PRIVACY ) ;
} else if ( error12 . code = = 406 ) {
callFailed ( Instance . ERROR_LOCALIZED ) ;
} else {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error on phone.requestCall: " + error12 ) ;
}
callFailed ( ) ;
2017-03-31 01:58:05 +02:00
}
2020-08-14 18:58:22 +02:00
}
} ) , ConnectionsManager . RequestFlagFailOnServerErrors ) ;
} else {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error on getDhConfig " + error ) ;
2017-03-31 01:58:05 +02:00
}
2020-08-14 18:58:22 +02:00
callFailed ( ) ;
2017-03-31 01:58:05 +02:00
}
} , ConnectionsManager . RequestFlagFailOnServerErrors ) ;
}
2020-08-14 18:58:22 +02:00
private void acknowledgeCall ( final boolean startRinging ) {
2020-12-23 08:48:30 +01:00
if ( privateCall instanceof TLRPC . TL_phoneCallDiscarded ) {
2020-08-14 18:58:22 +02:00
if ( BuildVars . LOGS_ENABLED ) {
2020-12-23 08:48:30 +01:00
FileLog . w ( " Call " + privateCall . id + " was discarded before the service started, stopping " ) ;
2020-08-14 18:58:22 +02:00
}
2017-03-31 01:58:05 +02:00
stopSelf ( ) ;
return ;
}
2020-08-14 18:58:22 +02: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 ) {
2019-01-23 18:03:33 +01:00
FileLog . e ( " MIUI: no permission to show when locked but the screen is locked. ¯ \\ _(ツ)_/¯ " ) ;
2020-08-14 18:58:22 +02:00
}
2019-01-23 18:03:33 +01:00
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 ( ) ;
2020-12-23 08:48:30 +01:00
req . peer . id = privateCall . id ;
req . peer . access_hash = privateCall . access_hash ;
2020-08-14 18:58:22 +02:00
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > AndroidUtilities . runOnUIThread ( ( ) - > {
if ( sharedInstance = = null ) {
return ;
2017-03-31 01:58:05 +02:00
}
2020-08-14 18:58:22 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . w ( " receivedCall response = " + response ) ;
}
if ( error ! = null ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " error on receivedCall: " + error ) ;
}
stopSelf ( ) ;
} else {
if ( USE_CONNECTION_SERVICE ) {
ContactsController . getInstance ( currentAccount ) . createOrUpdateConnectionServiceContact ( user . id , user . first_name , user . last_name ) ;
TelecomManager tm = ( TelecomManager ) getSystemService ( TELECOM_SERVICE ) ;
Bundle extras = new Bundle ( ) ;
extras . putInt ( " call_type " , 1 ) ;
tm . addNewIncomingCall ( addAccountToTelecomManager ( ) , extras ) ;
}
if ( startRinging ) {
startRinging ( ) ;
}
}
} ) , ConnectionsManager . RequestFlagFailOnServerErrors ) ;
2017-03-31 01:58:05 +02:00
}
2021-06-25 02:43:10 +02:00
private boolean isRinging ( ) {
return currentState = = STATE_WAITING_INCOMING ;
}
public boolean isJoined ( ) {
return currentState ! = STATE_WAIT_INIT & & currentState ! = STATE_CREATING ;
}
public void requestVideoCall ( ) {
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] = = null ) {
2018-07-30 04:07:02 +02:00
return ;
2017-03-31 01:58:05 +02:00
}
2021-06-25 02:43:10 +02:00
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setupOutgoingVideo ( localSink [ CAPTURE_DEVICE_CAMERA ] , isFrontFaceCamera ) ;
}
public void switchCamera ( ) {
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] = = null | | ! tgVoip [ CAPTURE_DEVICE_CAMERA ] . hasVideoCapturer ( ) | | switchingCamera ) {
if ( captureDevice [ CAPTURE_DEVICE_CAMERA ] ! = 0 & & ! switchingCamera ) {
NativeInstance . switchCameraCapturer ( captureDevice [ CAPTURE_DEVICE_CAMERA ] , ! isFrontFaceCamera ) ;
}
return ;
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
switchingCamera = true ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . switchCamera ( ! isFrontFaceCamera ) ;
}
public void createCaptureDevice ( boolean screencast ) {
int index = screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ;
int deviceType ;
if ( screencast ) {
deviceType = 2 ;
} else {
deviceType = isFrontFaceCamera ? 1 : 0 ;
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
if ( index = = CAPTURE_DEVICE_SCREEN ) {
if ( captureDevice [ index ] ! = 0 ) {
return ;
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
captureDevice [ index ] = NativeInstance . createVideoCapturer ( localSink [ index ] , deviceType ) ;
createGroupInstance ( CAPTURE_DEVICE_SCREEN , false ) ;
setVideoState ( true , Instance . VIDEO_STATE_ACTIVE ) ;
AccountInstance . getInstance ( currentAccount ) . getNotificationCenter ( ) . postNotificationName ( NotificationCenter . groupCallScreencastStateChanged ) ;
2017-03-31 01:58:05 +02:00
} else {
2021-06-25 02:43:10 +02:00
if ( captureDevice [ index ] ! = 0 | | tgVoip [ index ] = = null ) {
if ( tgVoip [ index ] ! = null & & captureDevice [ index ] ! = 0 ) {
tgVoip [ index ] . activateVideoCapturer ( captureDevice [ index ] ) ;
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
return ;
2018-07-30 04:07:02 +02:00
}
2021-06-25 02:43:10 +02:00
captureDevice [ index ] = NativeInstance . createVideoCapturer ( localSink [ index ] , deviceType ) ;
2019-03-03 21:40:48 +01:00
}
}
2021-06-25 02:43:10 +02:00
public void setupCaptureDevice ( boolean screencast , boolean micEnabled ) {
int index = screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ;
if ( captureDevice [ index ] = = 0 | | tgVoip [ index ] = = null ) {
return ;
}
tgVoip [ index ] . setupOutgoingVideoCreated ( captureDevice [ index ] ) ;
destroyCaptureDevice [ index ] = false ;
videoState [ index ] = Instance . VIDEO_STATE_ACTIVE ;
if ( micMute = = micEnabled ) {
setMicMute ( ! micEnabled , false , false ) ;
micSwitching = true ;
}
if ( ! screencast & & groupCall ! = null ) {
editCallMember ( UserConfig . getInstance ( currentAccount ) . getCurrentUser ( ) , ! micEnabled , videoState [ index ] ! = Instance . VIDEO_STATE_ACTIVE , null , null , ( ) - > micSwitching = false ) ;
2017-03-31 01:58:05 +02:00
}
}
2021-06-25 02:43:10 +02:00
public void setVideoState ( boolean screencast , int state ) {
int index = screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ;
if ( tgVoip [ index ] = = null ) {
if ( captureDevice [ index ] ! = 0 ) {
videoState [ index ] = state ;
NativeInstance . setVideoStateCapturer ( captureDevice [ index ] , videoState [ index ] ) ;
} else if ( state = = Instance . VIDEO_STATE_ACTIVE & & currentState ! = STATE_BUSY & & currentState ! = STATE_ENDED ) {
captureDevice [ index ] = NativeInstance . createVideoCapturer ( localSink [ index ] , isFrontFaceCamera ? 1 : 0 ) ;
videoState [ index ] = Instance . VIDEO_STATE_ACTIVE ;
}
return ;
}
videoState [ index ] = state ;
tgVoip [ index ] . setVideoState ( videoState [ index ] ) ;
if ( captureDevice [ index ] ! = 0 ) {
NativeInstance . setVideoStateCapturer ( captureDevice [ index ] , videoState [ index ] ) ;
}
if ( ! screencast & & groupCall ! = null ) {
editCallMember ( UserConfig . getInstance ( currentAccount ) . getCurrentUser ( ) , null , videoState [ CAPTURE_DEVICE_CAMERA ] ! = Instance . VIDEO_STATE_ACTIVE , null , null , null ) ;
}
if ( ! screencast ) {
checkIsNear ( ) ;
}
2018-08-27 10:33:11 +02:00
}
2021-06-25 02:43:10 +02:00
public void stopScreenCapture ( ) {
if ( groupCall = = null | | videoState [ CAPTURE_DEVICE_SCREEN ] ! = Instance . VIDEO_STATE_ACTIVE ) {
return ;
}
TLRPC . TL_phone_leaveGroupCallPresentation req = new TLRPC . TL_phone_leaveGroupCallPresentation ( ) ;
req . call = groupCall . getInputGroupCall ( ) ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
if ( response ! = null ) {
TLRPC . Updates updates = ( TLRPC . Updates ) response ;
MessagesController . getInstance ( currentAccount ) . processUpdates ( updates , false ) ;
}
} ) ;
NativeInstance instance = tgVoip [ CAPTURE_DEVICE_SCREEN ] ;
if ( instance ! = null ) {
Utilities . globalQueue . postRunnable ( instance : : stopGroup ) ;
}
mySource [ CAPTURE_DEVICE_SCREEN ] = 0 ;
tgVoip [ CAPTURE_DEVICE_SCREEN ] = null ;
destroyCaptureDevice [ CAPTURE_DEVICE_SCREEN ] = true ;
captureDevice [ CAPTURE_DEVICE_SCREEN ] = 0 ;
videoState [ CAPTURE_DEVICE_SCREEN ] = Instance . VIDEO_STATE_INACTIVE ;
AccountInstance . getInstance ( currentAccount ) . getNotificationCenter ( ) . postNotificationName ( NotificationCenter . groupCallScreencastStateChanged ) ;
2020-12-23 08:48:30 +01:00
}
2021-06-25 02:43:10 +02:00
public int getVideoState ( boolean screencast ) {
return videoState [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] ;
}
2017-03-31 01:58:05 +02:00
2021-06-25 02:43:10 +02:00
public void setSinks ( VideoSink local , VideoSink remote ) {
setSinks ( local , false , remote ) ;
}
2020-08-14 18:58:22 +02:00
2021-06-25 02:43:10 +02:00
public void setSinks ( VideoSink local , boolean screencast , VideoSink remote ) {
localSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] . setTarget ( local ) ;
remoteSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] . setTarget ( remote ) ;
}
2020-08-14 18:58:22 +02:00
2021-06-25 02:43:10 +02:00
public void setLocalSink ( VideoSink local , boolean screencast ) {
if ( screencast ) {
//localSink[CAPTURE_DEVICE_SCREEN].setTarget(local);
} else {
localSink [ CAPTURE_DEVICE_CAMERA ] . setTarget ( local ) ;
}
2017-03-31 01:58:05 +02:00
}
2021-06-25 02:43:10 +02:00
public void setRemoteSink ( VideoSink remote , boolean screencast ) {
remoteSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] . setTarget ( remote ) ;
2017-03-31 01:58:05 +02:00
}
2021-06-25 02:43:10 +02:00
public ProxyVideoSink addRemoteSink ( TLRPC . TL_groupCallParticipant participant , boolean screencast , VideoSink remote , VideoSink background ) {
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] = = null ) {
return null ;
}
String endpointId = screencast ? participant . presentationEndpoint : participant . videoEndpoint ;
if ( endpointId = = null ) {
return null ;
}
ProxyVideoSink sink = remoteSinks . get ( endpointId ) ;
if ( sink ! = null & & sink . target = = remote ) {
return sink ;
}
if ( sink = = null ) {
sink = proxyVideoSinkLruCache . remove ( endpointId ) ;
}
if ( sink = = null ) {
sink = new ProxyVideoSink ( ) ;
}
if ( remote ! = null ) {
sink . setTarget ( remote ) ;
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
if ( background ! = null ) {
sink . setBackground ( background ) ;
}
remoteSinks . put ( endpointId , sink ) ;
sink . nativeInstance = tgVoip [ CAPTURE_DEVICE_CAMERA ] . addIncomingVideoOutput ( QUALITY_MEDIUM , endpointId , createSsrcGroups ( screencast ? participant . presentation : participant . video ) , sink ) ;
return sink ;
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
private NativeInstance . SsrcGroup [ ] createSsrcGroups ( TLRPC . TL_groupCallParticipantVideo video ) {
if ( video . source_groups . isEmpty ( ) ) {
return null ;
}
NativeInstance . SsrcGroup [ ] result = new NativeInstance . SsrcGroup [ video . source_groups . size ( ) ] ;
for ( int a = 0 ; a < result . length ; a + + ) {
result [ a ] = new NativeInstance . SsrcGroup ( ) ;
TLRPC . TL_groupCallParticipantVideoSourceGroup group = video . source_groups . get ( a ) ;
result [ a ] . semantics = group . semantics ;
result [ a ] . ssrcs = new int [ group . sources . size ( ) ] ;
for ( int b = 0 ; b < result [ a ] . ssrcs . length ; b + + ) {
result [ a ] . ssrcs [ b ] = group . sources . get ( b ) ;
2020-08-14 18:58:22 +02:00
}
}
2021-06-25 02:43:10 +02:00
return result ;
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
public void requestFullScreen ( TLRPC . TL_groupCallParticipant participant , boolean screencast ) {
if ( currentBackgroundSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] ! = null ) {
currentBackgroundSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] . setBackground ( null ) ;
}
if ( participant = = null ) {
currentBackgroundSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] = null ;
currentBackgroundEndpointId [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] = null ;
return ;
}
String endpointId = screencast ? participant . presentationEndpoint : participant . videoEndpoint ;
if ( endpointId = = null ) {
2020-08-14 18:58:22 +02:00
return ;
}
2021-06-25 02:43:10 +02:00
ProxyVideoSink sink = remoteSinks . get ( endpointId ) ;
if ( sink = = null ) {
sink = addRemoteSink ( participant , screencast , null , null ) ;
}
if ( sink ! = null ) {
sink . setBackground ( remoteSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] ) ;
//tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_FULL); TODO
currentBackgroundSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] = sink ;
currentBackgroundEndpointId [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] = endpointId ;
} else {
//tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_MEDIUM); TODO
currentBackgroundSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] = null ;
currentBackgroundEndpointId [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] = null ;
}
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
public void removeRemoteSink ( TLRPC . TL_groupCallParticipant participant , boolean presentation ) {
if ( presentation ) {
ProxyVideoSink sink = remoteSinks . remove ( participant . presentationEndpoint ) ;
if ( sink ! = null ) {
tgVoip [ CAPTURE_DEVICE_CAMERA ] . removeIncomingVideoOutput ( sink . nativeInstance ) ;
}
} else {
ProxyVideoSink sink = remoteSinks . remove ( participant . videoEndpoint ) ;
if ( sink ! = null ) {
tgVoip [ CAPTURE_DEVICE_CAMERA ] . removeIncomingVideoOutput ( sink . nativeInstance ) ;
}
}
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
public boolean isFullscreen ( TLRPC . TL_groupCallParticipant participant , boolean screencast ) {
return currentBackgroundSink [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] ! = null & & TextUtils . equals ( currentBackgroundEndpointId [ screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA ] , screencast ? participant . presentationEndpoint : participant . videoEndpoint ) ;
2020-08-14 18:58:22 +02:00
}
public void setBackgroundSinks ( VideoSink local , VideoSink remote ) {
2021-06-25 02:43:10 +02:00
localSink [ CAPTURE_DEVICE_CAMERA ] . setBackground ( local ) ;
remoteSink [ CAPTURE_DEVICE_CAMERA ] . setBackground ( remote ) ;
2020-08-14 18:58:22 +02:00
}
public void swapSinks ( ) {
2021-06-25 02:43:10 +02:00
localSink [ CAPTURE_DEVICE_CAMERA ] . swap ( ) ;
remoteSink [ CAPTURE_DEVICE_CAMERA ] . swap ( ) ;
2018-07-30 04:07:02 +02:00
}
2020-12-23 08:48:30 +01:00
public boolean isHangingUp ( ) {
return currentState = = STATE_HANGING_UP ;
}
public void onSignalingData ( TLRPC . TL_updatePhoneCallSignalingData data ) {
2021-06-25 02:43:10 +02:00
if ( user = = null | | tgVoip [ CAPTURE_DEVICE_CAMERA ] = = null | | tgVoip [ CAPTURE_DEVICE_CAMERA ] . isGroup ( ) | | getCallID ( ) ! = data . phone_call_id ) {
2020-12-23 08:48:30 +01:00
return ;
}
2021-06-25 02:43:10 +02:00
tgVoip [ CAPTURE_DEVICE_CAMERA ] . onSignalingDataReceive ( data . data ) ;
2020-12-23 08:48:30 +01:00
}
2021-03-19 11:25:58 +01:00
public int getSelfId ( ) {
if ( groupCallPeer = = null ) {
return UserConfig . getInstance ( currentAccount ) . clientUserId ;
}
if ( groupCallPeer instanceof TLRPC . TL_inputPeerUser ) {
return groupCallPeer . user_id ;
} else if ( groupCallPeer instanceof TLRPC . TL_inputPeerChannel ) {
return - groupCallPeer . channel_id ;
} else {
return - groupCallPeer . chat_id ;
}
}
2020-12-23 08:48:30 +01:00
public void onGroupCallParticipantsUpdate ( TLRPC . TL_updateGroupCallParticipants update ) {
if ( chat = = null | | groupCall = = null | | groupCall . call . id ! = update . call . id ) {
return ;
}
2021-03-19 11:25:58 +01:00
int selfId = getSelfId ( ) ;
2020-12-23 08:48:30 +01:00
for ( int a = 0 , N = update . participants . size ( ) ; a < N ; a + + ) {
TLRPC . TL_groupCallParticipant participant = update . participants . get ( a ) ;
if ( participant . left ) {
2021-03-19 11:25:58 +01:00
if ( participant . source ! = 0 ) {
2021-06-25 02:43:10 +02:00
if ( participant . source = = mySource [ CAPTURE_DEVICE_CAMERA ] ) {
2021-03-19 11:25:58 +01:00
int selfCount = 0 ;
for ( int b = 0 ; b < N ; b + + ) {
TLRPC . TL_groupCallParticipant p = update . participants . get ( b ) ;
2021-06-25 02:43:10 +02:00
if ( p . self | | p . source = = mySource [ CAPTURE_DEVICE_CAMERA ] ) {
2021-03-19 11:25:58 +01:00
selfCount + + ;
}
}
if ( selfCount > 1 ) {
hangUp ( 2 ) ;
return ;
}
}
}
} else if ( MessageObject . getPeerId ( participant . peer ) = = selfId ) {
2021-06-25 02:43:10 +02:00
if ( participant . source ! = mySource [ CAPTURE_DEVICE_CAMERA ] & & mySource [ CAPTURE_DEVICE_CAMERA ] ! = 0 & & participant . source ! = 0 ) {
2021-03-19 11:25:58 +01:00
if ( BuildVars . LOGS_ENABLED ) {
2021-06-25 02:43:10 +02:00
FileLog . d ( " source mismatch my = " + mySource [ CAPTURE_DEVICE_CAMERA ] + " psrc = " + participant . source ) ;
2021-03-19 11:25:58 +01:00
}
hangUp ( 2 ) ;
return ;
} else if ( ChatObject . isChannel ( chat ) & & currentGroupModeStreaming & & participant . can_self_unmute ) {
switchingStream = true ;
2021-06-25 02:43:10 +02:00
createGroupInstance ( CAPTURE_DEVICE_CAMERA , false ) ;
2021-03-19 11:25:58 +01:00
}
if ( participant . muted ) {
setMicMute ( true , false , false ) ;
2020-08-14 18:58:22 +02:00
}
}
}
}
2020-12-23 08:48:30 +01:00
public void onGroupCallUpdated ( TLRPC . GroupCall call ) {
if ( chat = = null ) {
2017-03-31 01:58:05 +02:00
return ;
}
2020-12-23 08:48:30 +01:00
if ( groupCall = = null | | groupCall . call . id ! = call . id ) {
return ;
}
if ( groupCall . call instanceof TLRPC . TL_groupCallDiscarded ) {
hangUp ( 2 ) ;
return ;
}
2021-03-19 11:25:58 +01:00
boolean newModeStreaming = false ;
2021-06-25 02:43:10 +02:00
if ( myParams ! = null ) {
2021-04-09 15:17:32 +02:00
try {
2021-06-25 02:43:10 +02:00
JSONObject object = new JSONObject ( myParams . data ) ;
2021-04-09 15:17:32 +02:00
newModeStreaming = object . optBoolean ( " stream " ) ;
} catch ( Exception e ) {
FileLog . e ( e ) ;
}
2021-03-19 11:25:58 +01:00
}
2021-06-25 02:43:10 +02:00
if ( ( currentState = = STATE_WAIT_INIT | | newModeStreaming ! = currentGroupModeStreaming ) & & myParams ! = null ) {
2021-03-19 11:25:58 +01:00
if ( playedConnectedSound & & newModeStreaming ! = currentGroupModeStreaming ) {
switchingStream = true ;
}
currentGroupModeStreaming = newModeStreaming ;
2020-12-23 08:48:30 +01:00
try {
2021-03-19 11:25:58 +01:00
if ( newModeStreaming ) {
2021-06-25 02:43:10 +02:00
tgVoip [ CAPTURE_DEVICE_CAMERA ] . prepareForStream ( ) ;
2021-03-19 11:25:58 +01:00
} else {
2021-06-25 02:43:10 +02:00
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setJoinResponsePayload ( myParams . data ) ;
2020-12-23 08:48:30 +01:00
}
dispatchStateChanged ( STATE_WAIT_INIT_ACK ) ;
} catch ( Exception e ) {
FileLog . e ( e ) ;
}
}
2020-08-14 18:58:22 +02:00
}
public void onCallUpdated ( TLRPC . PhoneCall phoneCall ) {
2020-12-23 08:48:30 +01:00
if ( user = = null ) {
return ;
}
if ( privateCall = = null ) {
2020-08-14 18:58:22 +02:00
pendingUpdates . add ( phoneCall ) ;
2017-03-31 01:58:05 +02:00
return ;
2020-08-14 18:58:22 +02:00
}
if ( phoneCall = = null ) {
2017-03-31 01:58:05 +02:00
return ;
}
2020-12-23 08:48:30 +01:00
if ( phoneCall . id ! = privateCall . id ) {
2020-08-14 18:58:22 +02:00
if ( BuildVars . LOGS_ENABLED ) {
2020-12-23 08:48:30 +01:00
FileLog . w ( " onCallUpdated called with wrong call id (got " + phoneCall . id + " , expected " + this . privateCall . id + " ) " ) ;
2020-08-14 18:58:22 +02:00
}
return ;
}
if ( phoneCall . access_hash = = 0 ) {
2020-12-23 08:48:30 +01:00
phoneCall . access_hash = this . privateCall . access_hash ;
2020-08-14 18:58:22 +02:00
}
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " Call updated: " + phoneCall ) ;
}
2020-12-23 08:48:30 +01:00
privateCall = phoneCall ;
2020-08-14 18:58:22 +02:00
if ( phoneCall instanceof TLRPC . TL_phoneCallDiscarded ) {
needSendDebugLog = phoneCall . need_debug ;
needRateCall = phoneCall . need_rating ;
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " call discarded, stopping service " ) ;
}
if ( phoneCall . reason instanceof TLRPC . TL_phoneCallDiscardReasonBusy ) {
2017-03-31 01:58:05 +02:00
dispatchStateChanged ( STATE_BUSY ) ;
playingSound = true ;
2020-12-23 08:48:30 +01:00
Utilities . globalQueue . postRunnable ( ( ) - > 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 ( ) ;
}
2020-08-14 18:58:22 +02:00
} else if ( phoneCall instanceof TLRPC . TL_phoneCall & & authKey = = null ) {
if ( phoneCall . g_a_or_b = = null ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . w ( " stopping VoIP service, Ga == null " ) ;
}
2017-03-31 01:58:05 +02:00
callFailed ( ) ;
return ;
}
2020-08-14 18:58:22 +02:00
if ( ! Arrays . equals ( g_a_hash , Utilities . computeSHA256 ( phoneCall . g_a_or_b , 0 , phoneCall . g_a_or_b . length ) ) ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . w ( " stopping VoIP service, Ga hash doesn't match " ) ;
}
2017-03-31 01:58:05 +02:00
callFailed ( ) ;
return ;
}
2020-08-14 18:58:22 +02:00
g_a = phoneCall . g_a_or_b ;
BigInteger g_a = new BigInteger ( 1 , phoneCall . 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 ) ) {
2020-08-14 18:58:22 +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 ) ;
2020-08-14 18:58:22 +02:00
if ( keyFingerprint ! = phoneCall . key_fingerprint ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . w ( " key fingerprints don't match " ) ;
}
2017-03-31 01:58:05 +02:00
callFailed ( ) ;
return ;
}
initiateActualEncryptedCall ( ) ;
2020-08-14 18:58:22 +02:00
} else if ( phoneCall instanceof TLRPC . TL_phoneCallAccepted & & authKey = = null ) {
2017-03-31 01:58:05 +02:00
processAcceptedCall ( ) ;
} else {
2020-08-14 18:58:22 +02:00
if ( currentState = = STATE_WAITING & & phoneCall . receive_date ! = 0 ) {
2017-03-31 01:58:05 +02:00
dispatchStateChanged ( STATE_RINGING ) ;
2020-08-14 18:58:22 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " !!!!!! CALL RECEIVED " ) ;
}
if ( connectingSoundRunnable ! = null ) {
2019-01-23 18:03:33 +01:00
AndroidUtilities . cancelRunOnUIThread ( connectingSoundRunnable ) ;
2020-08-14 18:58:22 +02:00
connectingSoundRunnable = null ;
2019-01-23 18:03:33 +01:00
}
2020-12-23 08:48:30 +01:00
Utilities . globalQueue . postRunnable ( ( ) - > {
2021-03-19 11:25:58 +01:00
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
2020-12-23 08:48:30 +01:00
}
2021-03-19 11:25:58 +01:00
spPlayId = soundPool . play ( spRingbackID , 1 , 1 , 0 , - 1 , 1 ) ;
2020-12-23 08:48:30 +01:00
} ) ;
2017-03-31 01:58:05 +02:00
if ( timeoutRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( timeoutRunnable ) ;
2020-08-14 18:58:22 +02:00
timeoutRunnable = null ;
2017-03-31 01:58:05 +02:00
}
2020-08-14 18:58:22 +02:00
timeoutRunnable = ( ) - > {
timeoutRunnable = null ;
declineIncomingCall ( DISCARD_REASON_MISSED , null ) ;
2017-03-31 01:58:05 +02:00
} ;
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 )
2020-12-23 08:48:30 +01:00
. putExtra ( " call_id " , privateCall . id )
. putExtra ( " call_access_hash " , privateCall . access_hash )
. putExtra ( " call_video " , privateCall . video )
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 ) {
2020-08-14 18:58:22 +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 ( ) {
dispatchStateChanged ( STATE_EXCHANGING_KEYS ) ;
2018-07-30 04:07:02 +02:00
BigInteger p = new BigInteger ( 1 , MessagesStorage . getInstance ( currentAccount ) . getSecretPBytes ( ) ) ;
2020-12-23 08:48:30 +01:00
BigInteger i_authKey = new BigInteger ( 1 , privateCall . g_b ) ;
2017-03-31 01:58:05 +02:00
if ( ! Utilities . isGoodGaAndGb ( i_authKey , p ) ) {
2020-08-14 18:58:22 +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 ) ;
2020-08-14 18:58:22 +02:00
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 ( ) ;
2020-12-23 08:48:30 +01:00
req . peer . id = privateCall . id ;
req . peer . access_hash = privateCall . access_hash ;
2020-08-14 18:58:22 +02:00
req . protocol = new TLRPC . TL_phoneCallProtocol ( ) ;
req . protocol . max_layer = Instance . getConnectionMaxLayer ( ) ;
req . protocol . min_layer = CALL_MIN_LAYER ;
req . protocol . udp_p2p = req . protocol . udp_reflector = true ;
req . protocol . library_versions . addAll ( Instance . AVAILABLE_VERSIONS ) ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > AndroidUtilities . runOnUIThread ( ( ) - > {
if ( error ! = null ) {
callFailed ( ) ;
} else {
2020-12-23 08:48:30 +01:00
privateCall = ( ( TLRPC . TL_phone_phoneCall ) response ) . phone_call ;
2020-08-14 18:58:22 +02:00
initiateActualEncryptedCall ( ) ;
2017-03-31 01:58:05 +02:00
}
2020-08-14 18:58:22 +02:00
} ) ) ;
2017-03-31 01:58:05 +02:00
}
2020-03-30 14:00:09 +02:00
private int convertDataSavingMode ( int mode ) {
2020-08-14 18:58:22 +02:00
if ( mode ! = Instance . DATA_SAVING_ROAMING ) {
2019-03-03 21:40:48 +01:00
return mode ;
2020-03-30 14:00:09 +02:00
}
2020-08-14 18:58:22 +02:00
return ApplicationLoader . isRoaming ( ) ? Instance . DATA_SAVING_MOBILE : Instance . DATA_SAVING_NEVER ;
2019-03-03 21:40:48 +01:00
}
2020-12-23 08:48:30 +01:00
public void migrateToChat ( TLRPC . Chat newChat ) {
chat = newChat ;
}
2021-03-19 11:25:58 +01:00
public void setGroupCallPeer ( TLRPC . InputPeer peer ) {
if ( groupCall = = null ) {
return ;
}
groupCallPeer = peer ;
groupCall . setSelfPeer ( groupCallPeer ) ;
2021-06-25 02:43:10 +02:00
createGroupInstance ( CAPTURE_DEVICE_CAMERA , true ) ;
if ( videoState [ CAPTURE_DEVICE_SCREEN ] = = Instance . VIDEO_STATE_ACTIVE ) {
createGroupInstance ( CAPTURE_DEVICE_SCREEN , true ) ;
}
2021-03-19 11:25:58 +01:00
}
private void startGroupCall ( int ssrc , String json , boolean create ) {
2020-12-23 08:48:30 +01:00
if ( sharedInstance ! = this ) {
return ;
}
if ( createGroupCall ) {
groupCall = new ChatObject . Call ( ) ;
groupCall . call = new TLRPC . TL_groupCall ( ) ;
groupCall . call . participants_count = 0 ;
groupCall . call . version = 1 ;
2021-06-25 02:43:10 +02:00
groupCall . call . can_start_video = true ;
2020-12-23 08:48:30 +01:00
groupCall . call . can_change_join_muted = true ;
groupCall . chatId = chat . id ;
2020-12-26 06:18:43 +01:00
groupCall . currentAccount = AccountInstance . getInstance ( currentAccount ) ;
2021-03-19 11:25:58 +01:00
groupCall . setSelfPeer ( groupCallPeer ) ;
2021-06-25 02:43:10 +02:00
groupCall . createNoVideoParticipant ( ) ;
2020-12-23 08:48:30 +01:00
dispatchStateChanged ( STATE_CREATING ) ;
TLRPC . TL_phone_createGroupCall req = new TLRPC . TL_phone_createGroupCall ( ) ;
req . peer = MessagesController . getInputPeer ( chat ) ;
req . random_id = Utilities . random . nextInt ( ) ;
2021-04-14 03:44:46 +02:00
if ( scheduleDate ! = 0 ) {
req . schedule_date = scheduleDate ;
req . flags | = 2 ;
}
2020-12-23 08:48:30 +01:00
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
if ( response ! = null ) {
TLRPC . Updates updates = ( TLRPC . Updates ) response ;
for ( int a = 0 ; a < updates . updates . size ( ) ; a + + ) {
TLRPC . Update update = updates . updates . get ( a ) ;
if ( update instanceof TLRPC . TL_updateGroupCall ) {
TLRPC . TL_updateGroupCall updateGroupCall = ( TLRPC . TL_updateGroupCall ) update ;
AndroidUtilities . runOnUIThread ( ( ) - > {
if ( sharedInstance = = null ) {
return ;
}
groupCall . call . access_hash = updateGroupCall . call . access_hash ;
groupCall . call . id = updateGroupCall . call . id ;
MessagesController . getInstance ( currentAccount ) . putGroupCall ( groupCall . chatId , groupCall ) ;
2021-03-19 11:25:58 +01:00
startGroupCall ( 0 , null , false ) ;
2020-12-23 08:48:30 +01:00
} ) ;
break ;
}
}
MessagesController . getInstance ( currentAccount ) . processUpdates ( updates , false ) ;
} else {
AndroidUtilities . runOnUIThread ( ( ) - > {
NotificationCenter . getInstance ( currentAccount ) . postNotificationName ( NotificationCenter . needShowAlert , 6 , error . text ) ;
hangUp ( 0 ) ;
} ) ;
}
} , ConnectionsManager . RequestFlagFailOnServerErrors ) ;
createGroupCall = false ;
return ;
}
if ( json = = null ) {
if ( groupCall = = null ) {
groupCall = MessagesController . getInstance ( currentAccount ) . getGroupCall ( chat . id , false ) ;
2021-03-19 11:25:58 +01:00
if ( groupCall ! = null ) {
groupCall . setSelfPeer ( groupCallPeer ) ;
}
2020-12-23 08:48:30 +01:00
}
configureDeviceForCall ( ) ;
showNotification ( ) ;
AndroidUtilities . runOnUIThread ( ( ) - > NotificationCenter . getGlobalInstance ( ) . postNotificationName ( NotificationCenter . didStartedCall ) ) ;
2021-06-25 02:43:10 +02:00
createGroupInstance ( CAPTURE_DEVICE_CAMERA , false ) ;
2020-12-23 08:48:30 +01:00
} else {
if ( getSharedInstance ( ) = = null | | groupCall = = null ) {
return ;
}
dispatchStateChanged ( STATE_WAIT_INIT ) ;
2021-03-19 11:25:58 +01:00
if ( BuildVars . LOGS_ENABLED ) {
2021-06-25 02:43:10 +02:00
FileLog . d ( " initital source = " + ssrc ) ;
2021-03-19 11:25:58 +01:00
}
2020-12-23 08:48:30 +01:00
TLRPC . TL_phone_joinGroupCall req = new TLRPC . TL_phone_joinGroupCall ( ) ;
req . muted = true ;
2021-06-25 02:43:10 +02:00
req . video_stopped = videoState [ CAPTURE_DEVICE_CAMERA ] ! = Instance . VIDEO_STATE_ACTIVE ;
2020-12-23 08:48:30 +01:00
req . call = groupCall . getInputGroupCall ( ) ;
req . params = new TLRPC . TL_dataJSON ( ) ;
req . params . data = json ;
2021-03-19 11:25:58 +01:00
if ( ! TextUtils . isEmpty ( joinHash ) ) {
req . invite_hash = joinHash ;
req . flags | = 2 ;
}
if ( groupCallPeer ! = null ) {
req . join_as = groupCallPeer ;
} else {
req . join_as = new TLRPC . TL_inputPeerUser ( ) ;
req . join_as . user_id = AccountInstance . getInstance ( currentAccount ) . getUserConfig ( ) . getClientUserId ( ) ;
}
2020-12-23 08:48:30 +01:00
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
if ( response ! = null ) {
2021-06-25 02:43:10 +02:00
AndroidUtilities . runOnUIThread ( ( ) - > mySource [ CAPTURE_DEVICE_CAMERA ] = ssrc ) ;
2021-03-19 11:25:58 +01:00
TLRPC . Updates updates = ( TLRPC . Updates ) response ;
int selfId = getSelfId ( ) ;
for ( int a = 0 , N = updates . updates . size ( ) ; a < N ; a + + ) {
TLRPC . Update update = updates . updates . get ( a ) ;
if ( update instanceof TLRPC . TL_updateGroupCallParticipants ) {
TLRPC . TL_updateGroupCallParticipants updateGroupCallParticipants = ( TLRPC . TL_updateGroupCallParticipants ) update ;
for ( int b = 0 , N2 = updateGroupCallParticipants . participants . size ( ) ; b < N2 ; b + + ) {
TLRPC . TL_groupCallParticipant participant = updateGroupCallParticipants . participants . get ( b ) ;
if ( MessageObject . getPeerId ( participant . peer ) = = selfId ) {
2021-06-25 02:43:10 +02:00
AndroidUtilities . runOnUIThread ( ( ) - > mySource [ CAPTURE_DEVICE_CAMERA ] = participant . source ) ;
2021-03-19 11:25:58 +01:00
if ( BuildVars . LOGS_ENABLED ) {
2021-06-25 02:43:10 +02:00
FileLog . d ( " join source = " + participant . source ) ;
2021-03-19 11:25:58 +01:00
}
break ;
}
}
2021-06-25 02:43:10 +02:00
} else if ( update instanceof TLRPC . TL_updateGroupCallConnection ) {
TLRPC . TL_updateGroupCallConnection updateGroupCallConnection = ( TLRPC . TL_updateGroupCallConnection ) update ;
if ( ! updateGroupCallConnection . presentation ) {
myParams = updateGroupCallConnection . params ;
}
2021-03-19 11:25:58 +01:00
}
}
MessagesController . getInstance ( currentAccount ) . processUpdates ( updates , false ) ;
AndroidUtilities . runOnUIThread ( ( ) - > groupCall . loadMembers ( create ) ) ;
2021-04-09 15:17:32 +02:00
startGroupCheckShortpoll ( ) ;
2020-12-23 08:48:30 +01:00
} else {
AndroidUtilities . runOnUIThread ( ( ) - > {
2021-04-09 15:17:32 +02:00
if ( " JOIN_AS_PEER_INVALID " . equals ( error . text ) ) {
TLRPC . ChatFull chatFull = MessagesController . getInstance ( currentAccount ) . getChatFull ( chat . id ) ;
if ( chatFull ! = null ) {
if ( chatFull instanceof TLRPC . TL_chatFull ) {
chatFull . flags & = ~ 32768 ;
} else {
chatFull . flags & = ~ 67108864 ;
}
chatFull . groupcall_default_join_as = null ;
JoinCallAlert . resetCache ( ) ;
}
hangUp ( 2 ) ;
} else if ( " GROUPCALL_SSRC_DUPLICATE_MUCH " . equals ( error . text ) ) {
2021-06-25 02:43:10 +02:00
createGroupInstance ( CAPTURE_DEVICE_CAMERA , false ) ;
2020-12-23 08:48:30 +01:00
} else {
2021-04-09 15:17:32 +02:00
if ( " GROUPCALL_INVALID " . equals ( error . text ) ) {
MessagesController . getInstance ( currentAccount ) . loadFullChat ( chat . id , 0 , true ) ;
}
2020-12-23 08:48:30 +01:00
NotificationCenter . getInstance ( currentAccount ) . postNotificationName ( NotificationCenter . needShowAlert , 6 , error . text ) ;
hangUp ( 0 ) ;
}
} ) ;
}
2021-06-25 02:43:10 +02:00
} ) ;
}
}
2021-07-15 16:24:57 +02:00
private void startScreenCapture ( int ssrc , String json ) {
2021-06-25 02:43:10 +02:00
if ( getSharedInstance ( ) = = null | | groupCall = = null ) {
return ;
2020-12-23 08:48:30 +01:00
}
2021-06-25 02:43:10 +02:00
mySource [ CAPTURE_DEVICE_SCREEN ] = 0 ;
TLRPC . TL_phone_joinGroupCallPresentation req = new TLRPC . TL_phone_joinGroupCallPresentation ( ) ;
req . call = groupCall . getInputGroupCall ( ) ;
req . params = new TLRPC . TL_dataJSON ( ) ;
req . params . data = json ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
if ( response ! = null ) {
2021-07-15 16:24:57 +02:00
AndroidUtilities . runOnUIThread ( ( ) - > mySource [ CAPTURE_DEVICE_SCREEN ] = ssrc ) ;
2021-06-25 02:43:10 +02:00
TLRPC . Updates updates = ( TLRPC . Updates ) response ;
AndroidUtilities . runOnUIThread ( ( ) - > {
if ( tgVoip [ CAPTURE_DEVICE_SCREEN ] ! = null ) {
int selfId = getSelfId ( ) ;
for ( int a = 0 , N = updates . updates . size ( ) ; a < N ; a + + ) {
TLRPC . Update update = updates . updates . get ( a ) ;
if ( update instanceof TLRPC . TL_updateGroupCallConnection ) {
TLRPC . TL_updateGroupCallConnection updateGroupCallConnection = ( TLRPC . TL_updateGroupCallConnection ) update ;
if ( updateGroupCallConnection . presentation ) {
tgVoip [ CAPTURE_DEVICE_SCREEN ] . setJoinResponsePayload ( updateGroupCallConnection . params . data ) ;
}
} else if ( update instanceof TLRPC . TL_updateGroupCallParticipants ) {
TLRPC . TL_updateGroupCallParticipants updateGroupCallParticipants = ( TLRPC . TL_updateGroupCallParticipants ) update ;
for ( int b = 0 , N2 = updateGroupCallParticipants . participants . size ( ) ; b < N2 ; b + + ) {
TLRPC . TL_groupCallParticipant participant = updateGroupCallParticipants . participants . get ( b ) ;
if ( MessageObject . getPeerId ( participant . peer ) = = selfId ) {
if ( participant . presentation ! = null ) {
2021-07-15 16:24:57 +02:00
if ( ( participant . presentation . flags & 2 ) ! = 0 ) {
mySource [ CAPTURE_DEVICE_SCREEN ] = participant . presentation . audio_source ;
} else {
for ( int c = 0 , N3 = participant . presentation . source_groups . size ( ) ; c < N3 ; c + + ) {
TLRPC . TL_groupCallParticipantVideoSourceGroup sourceGroup = participant . presentation . source_groups . get ( c ) ;
if ( sourceGroup . sources . size ( ) > 0 ) {
mySource [ CAPTURE_DEVICE_SCREEN ] = sourceGroup . sources . get ( 0 ) ;
}
2021-06-25 02:43:10 +02:00
}
}
}
break ;
}
}
}
}
}
} ) ;
MessagesController . getInstance ( currentAccount ) . processUpdates ( updates , false ) ;
startGroupCheckShortpoll ( ) ;
} else {
AndroidUtilities . runOnUIThread ( ( ) - > {
2021-07-15 16:24:57 +02:00
if ( " GROUPCALL_VIDEO_TOO_MUCH " . equals ( error . text ) ) {
groupCall . reloadGroupCall ( ) ;
} else if ( " JOIN_AS_PEER_INVALID " . equals ( error . text ) ) {
2021-06-25 02:43:10 +02:00
TLRPC . ChatFull chatFull = MessagesController . getInstance ( currentAccount ) . getChatFull ( chat . id ) ;
if ( chatFull ! = null ) {
if ( chatFull instanceof TLRPC . TL_chatFull ) {
chatFull . flags & = ~ 32768 ;
} else {
chatFull . flags & = ~ 67108864 ;
}
chatFull . groupcall_default_join_as = null ;
JoinCallAlert . resetCache ( ) ;
}
hangUp ( 2 ) ;
} else if ( " GROUPCALL_SSRC_DUPLICATE_MUCH " . equals ( error . text ) ) {
createGroupInstance ( CAPTURE_DEVICE_SCREEN , false ) ;
} else {
if ( " GROUPCALL_INVALID " . equals ( error . text ) ) {
MessagesController . getInstance ( currentAccount ) . loadFullChat ( chat . id , 0 , true ) ;
}
}
} ) ;
}
} ) ;
2020-12-23 08:48:30 +01:00
}
private Runnable shortPollRunnable ;
private int checkRequestId ;
private void startGroupCheckShortpoll ( ) {
2021-06-25 02:43:10 +02:00
if ( shortPollRunnable ! = null | | sharedInstance = = null | | groupCall = = null | | mySource [ CAPTURE_DEVICE_CAMERA ] = = 0 & & mySource [ CAPTURE_DEVICE_SCREEN ] = = 0 ) {
2020-12-23 08:48:30 +01:00
return ;
}
AndroidUtilities . runOnUIThread ( shortPollRunnable = ( ) - > {
2021-06-25 02:43:10 +02:00
if ( shortPollRunnable = = null | | sharedInstance = = null | | groupCall = = null | | mySource [ CAPTURE_DEVICE_CAMERA ] = = 0 & & mySource [ CAPTURE_DEVICE_SCREEN ] = = 0 ) {
2020-12-23 08:48:30 +01:00
return ;
}
TLRPC . TL_phone_checkGroupCall req = new TLRPC . TL_phone_checkGroupCall ( ) ;
req . call = groupCall . getInputGroupCall ( ) ;
2021-06-25 02:43:10 +02:00
for ( int a = 0 ; a < mySource . length ; a + + ) {
if ( mySource [ a ] ! = 0 ) {
req . sources . add ( mySource [ a ] ) ;
}
}
2020-12-23 08:48:30 +01:00
checkRequestId = ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > AndroidUtilities . runOnUIThread ( ( ) - > {
if ( shortPollRunnable = = null | | sharedInstance = = null | | groupCall = = null ) {
return ;
}
shortPollRunnable = null ;
checkRequestId = 0 ;
2021-06-25 02:43:10 +02:00
boolean recreateCamera = false ;
boolean recreateScreenCapture = false ;
if ( response instanceof TLRPC . Vector ) {
TLRPC . Vector vector = ( TLRPC . Vector ) response ;
if ( mySource [ CAPTURE_DEVICE_CAMERA ] ! = 0 & & req . sources . contains ( mySource [ CAPTURE_DEVICE_CAMERA ] ) ) {
if ( ! vector . objects . contains ( mySource [ CAPTURE_DEVICE_CAMERA ] ) ) {
recreateCamera = true ;
}
}
if ( mySource [ CAPTURE_DEVICE_SCREEN ] ! = 0 & & req . sources . contains ( mySource [ CAPTURE_DEVICE_SCREEN ] ) ) {
if ( ! vector . objects . contains ( mySource [ CAPTURE_DEVICE_SCREEN ] ) ) {
recreateScreenCapture = true ;
}
}
} else if ( error ! = null & & error . code = = 400 ) {
recreateCamera = true ;
if ( mySource [ CAPTURE_DEVICE_SCREEN ] ! = 0 & & req . sources . contains ( mySource [ CAPTURE_DEVICE_SCREEN ] ) ) {
recreateScreenCapture = true ;
}
}
if ( recreateCamera ) {
createGroupInstance ( CAPTURE_DEVICE_CAMERA , false ) ;
}
if ( recreateScreenCapture ) {
createGroupInstance ( CAPTURE_DEVICE_SCREEN , false ) ;
}
if ( mySource [ CAPTURE_DEVICE_SCREEN ] ! = 0 | | mySource [ CAPTURE_DEVICE_CAMERA ] ! = 0 ) {
2020-12-23 08:48:30 +01:00
startGroupCheckShortpoll ( ) ;
}
} ) ) ;
} , 4000 ) ;
}
private void cancelGroupCheckShortPoll ( ) {
2021-06-25 02:43:10 +02:00
if ( mySource [ CAPTURE_DEVICE_SCREEN ] ! = 0 | | mySource [ CAPTURE_DEVICE_CAMERA ] ! = 0 ) {
return ;
}
2020-12-23 08:48:30 +01:00
if ( checkRequestId ! = 0 ) {
ConnectionsManager . getInstance ( currentAccount ) . cancelRequest ( checkRequestId , false ) ;
checkRequestId = 0 ;
}
if ( shortPollRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( shortPollRunnable ) ;
shortPollRunnable = null ;
}
}
2021-06-25 02:43:10 +02:00
private static class RequestedParticipant {
public int audioSsrc ;
public TLRPC . TL_groupCallParticipant participant ;
2021-07-15 16:24:57 +02:00
public RequestedParticipant ( TLRPC . TL_groupCallParticipant p , int ssrc ) {
2021-06-25 02:43:10 +02:00
participant = p ;
2021-07-15 16:24:57 +02:00
audioSsrc = ssrc ;
2021-06-25 02:43:10 +02:00
}
}
private void broadcastUnknownParticipants ( long taskPtr , int [ ] unknown ) {
if ( groupCall = = null | | tgVoip [ CAPTURE_DEVICE_CAMERA ] = = null ) {
2021-03-19 11:25:58 +01:00
return ;
2020-12-23 08:48:30 +01:00
}
2021-03-19 11:25:58 +01:00
int selfId = getSelfId ( ) ;
2021-06-25 02:43:10 +02:00
ArrayList < RequestedParticipant > participants = null ;
for ( int a = 0 , N = unknown . length ; a < N ; a + + ) {
TLRPC . TL_groupCallParticipant p = groupCall . participantsBySources . get ( unknown [ a ] ) ;
if ( p = = null ) {
p = groupCall . participantsByVideoSources . get ( unknown [ a ] ) ;
if ( p = = null ) {
p = groupCall . participantsByPresentationSources . get ( unknown [ a ] ) ;
}
2021-03-19 11:25:58 +01:00
}
if ( p = = null | | MessageObject . getPeerId ( p . peer ) = = selfId | | p . source = = 0 ) {
continue ;
}
if ( participants = = null ) {
participants = new ArrayList < > ( ) ;
2020-12-23 08:48:30 +01:00
}
2021-07-15 16:24:57 +02:00
participants . add ( new RequestedParticipant ( p , unknown [ a ] ) ) ;
2021-03-19 11:25:58 +01:00
}
if ( participants ! = null ) {
int [ ] ssrcs = new int [ participants . size ( ) ] ;
for ( int a = 0 , N = participants . size ( ) ; a < N ; a + + ) {
2021-06-25 02:43:10 +02:00
RequestedParticipant p = participants . get ( a ) ;
ssrcs [ a ] = p . audioSsrc ;
2021-03-19 11:25:58 +01:00
}
2021-06-25 02:43:10 +02:00
tgVoip [ CAPTURE_DEVICE_CAMERA ] . onMediaDescriptionAvailable ( taskPtr , ssrcs ) ;
2020-12-23 08:48:30 +01:00
2021-03-19 11:25:58 +01:00
for ( int a = 0 , N = participants . size ( ) ; a < N ; a + + ) {
2021-06-25 02:43:10 +02:00
RequestedParticipant p = participants . get ( a ) ;
if ( p . participant . muted_by_you ) {
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setVolume ( p . audioSsrc , 0 ) ;
2021-03-19 11:25:58 +01:00
} else {
2021-06-25 02:43:10 +02:00
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setVolume ( p . audioSsrc , ChatObject . getParticipantVolume ( p . participant ) / 10000 . 0 ) ;
2020-12-23 08:48:30 +01:00
}
}
2021-03-19 11:25:58 +01:00
}
}
2021-06-25 02:43:10 +02:00
private void createGroupInstance ( int type , boolean switchAccount ) {
if ( switchAccount ) {
mySource [ type ] = 0 ;
if ( type = = CAPTURE_DEVICE_CAMERA ) {
switchingAccount = switchAccount ;
}
}
cancelGroupCheckShortPoll ( ) ;
if ( type = = CAPTURE_DEVICE_CAMERA ) {
wasConnected = false ;
} else if ( ! wasConnected ) {
reconnectScreenCapture = true ;
return ;
}
boolean created = false ;
if ( tgVoip [ type ] = = null ) {
created = true ;
final String logFilePath = BuildVars . DEBUG_VERSION ? VoIPHelper . getLogFilePath ( " voip_ " + type + " _ " + groupCall . call . id ) : VoIPHelper . getLogFilePath ( groupCall . call . id , false ) ;
tgVoip [ type ] = NativeInstance . makeGroup ( logFilePath , captureDevice [ type ] , type = = CAPTURE_DEVICE_SCREEN , type = = CAPTURE_DEVICE_CAMERA & & SharedConfig . noiseSupression , ( ssrc , json ) - > {
if ( type = = CAPTURE_DEVICE_CAMERA ) {
startGroupCall ( ssrc , json , true ) ;
} else {
2021-07-15 16:24:57 +02:00
startScreenCapture ( ssrc , json ) ;
2021-06-25 02:43:10 +02:00
}
} , ( uids , levels , voice ) - > {
if ( sharedInstance = = null | | groupCall = = null | | type ! = CAPTURE_DEVICE_CAMERA ) {
2021-03-19 11:25:58 +01:00
return ;
2020-12-23 08:48:30 +01:00
}
2021-03-19 11:25:58 +01:00
groupCall . processVoiceLevelsUpdate ( uids , levels , voice ) ;
float maxAmplitude = 0 ;
boolean hasOther = false ;
for ( int a = 0 ; a < uids . length ; a + + ) {
if ( uids [ a ] = = 0 ) {
if ( lastTypingTimeSend < SystemClock . uptimeMillis ( ) - 5000 & & levels [ a ] > 0 . 1f & & voice [ a ] ) {
lastTypingTimeSend = SystemClock . uptimeMillis ( ) ;
TLRPC . TL_messages_setTyping req = new TLRPC . TL_messages_setTyping ( ) ;
req . action = new TLRPC . TL_speakingInGroupCallAction ( ) ;
req . peer = MessagesController . getInputPeer ( chat ) ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
} ) ;
2020-12-24 06:36:01 +01:00
}
2021-03-19 11:25:58 +01:00
NotificationCenter . getGlobalInstance ( ) . postNotificationName ( NotificationCenter . webRtcMicAmplitudeEvent , levels [ a ] ) ;
continue ;
}
hasOther = true ;
maxAmplitude = Math . max ( maxAmplitude , levels [ a ] ) ;
2020-12-23 08:48:30 +01:00
}
2021-03-19 11:25:58 +01:00
if ( hasOther ) {
NotificationCenter . getGlobalInstance ( ) . postNotificationName ( NotificationCenter . webRtcSpeakerAmplitudeEvent , maxAmplitude ) ;
if ( audioLevelsCallback ! = null ) {
audioLevelsCallback . run ( uids , levels , voice ) ;
2020-12-23 08:48:30 +01:00
}
}
2021-06-25 02:43:10 +02:00
} , ( taskPtr , unknown ) - > {
if ( sharedInstance = = null | | groupCall = = null | | type ! = CAPTURE_DEVICE_CAMERA ) {
2021-03-19 11:25:58 +01:00
return ;
}
groupCall . processUnknownVideoParticipants ( unknown , ( ssrcs ) - > {
if ( sharedInstance = = null | | groupCall = = null ) {
return ;
}
2021-06-25 02:43:10 +02:00
broadcastUnknownParticipants ( taskPtr , unknown ) ;
2021-03-19 11:25:58 +01:00
} ) ;
} , ( timestamp , duration ) - > {
2021-06-25 02:43:10 +02:00
if ( type ! = CAPTURE_DEVICE_CAMERA ) {
return ;
}
2021-03-19 11:25:58 +01:00
TLRPC . TL_upload_getFile req = new TLRPC . TL_upload_getFile ( ) ;
req . limit = 128 * 1024 ;
TLRPC . TL_inputGroupCallStream inputGroupCallStream = new TLRPC . TL_inputGroupCallStream ( ) ;
inputGroupCallStream . call = groupCall . getInputGroupCall ( ) ;
inputGroupCallStream . time_ms = timestamp ;
if ( duration = = 500 ) {
inputGroupCallStream . scale = 1 ;
}
req . location = inputGroupCallStream ;
currentStreamRequestTimestamp = timestamp ;
currentStreamRequestId = AccountInstance . getInstance ( currentAccount ) . getConnectionsManager ( ) . sendRequest ( req , ( response , error , responseTime ) - > {
2021-06-25 02:43:10 +02:00
if ( tgVoip [ type ] = = null ) {
2021-03-19 11:25:58 +01:00
return ;
}
if ( response ! = null ) {
TLRPC . TL_upload_file res = ( TLRPC . TL_upload_file ) response ;
2021-06-25 02:43:10 +02:00
tgVoip [ type ] . onStreamPartAvailable ( timestamp , res . bytes . buffer , res . bytes . limit ( ) , responseTime ) ;
2021-03-19 11:25:58 +01:00
} else {
if ( " GROUPCALL_JOIN_MISSING " . equals ( error . text ) ) {
2021-06-25 02:43:10 +02:00
AndroidUtilities . runOnUIThread ( ( ) - > createGroupInstance ( type , false ) ) ;
2021-03-19 11:25:58 +01:00
} else {
int status ;
2021-04-09 15:17:32 +02:00
if ( " TIME_TOO_BIG " . equals ( error . text ) | | error . text . startsWith ( " FLOOD_WAIT " ) ) {
2021-03-19 11:25:58 +01:00
status = 0 ;
2021-04-09 15:17:32 +02:00
} else {
status = - 1 ;
2021-03-19 11:25:58 +01:00
}
2021-06-25 02:43:10 +02:00
tgVoip [ type ] . onStreamPartAvailable ( timestamp , null , status , responseTime ) ;
2021-01-28 15:15:51 +01:00
}
2021-03-19 11:25:58 +01:00
}
} , ConnectionsManager . RequestFlagFailOnServerErrors , ConnectionsManager . ConnectionTypeDownload , groupCall . call . stream_dc_id ) ;
} , ( timestamp , duration ) - > {
2021-06-25 02:43:10 +02:00
if ( type ! = CAPTURE_DEVICE_CAMERA ) {
return ;
}
2021-03-19 11:25:58 +01:00
if ( currentStreamRequestTimestamp = = timestamp ) {
AccountInstance . getInstance ( currentAccount ) . getConnectionsManager ( ) . cancelRequest ( currentStreamRequestId , true ) ;
currentStreamRequestId = 0 ;
}
} ) ;
2021-06-25 02:43:10 +02:00
tgVoip [ type ] . setOnStateUpdatedListener ( ( state , inTransition ) - > updateConnectionState ( type , state , inTransition ) ) ;
}
tgVoip [ type ] . resetGroupInstance ( ! created , false ) ;
if ( captureDevice [ type ] ! = 0 ) {
destroyCaptureDevice [ type ] = false ;
}
if ( type = = CAPTURE_DEVICE_CAMERA ) {
dispatchStateChanged ( STATE_WAIT_INIT ) ;
2021-04-09 15:17:32 +02:00
}
}
2021-06-25 02:43:10 +02:00
private void updateConnectionState ( int type , int state , boolean inTransition ) {
if ( type ! = CAPTURE_DEVICE_CAMERA ) {
return ;
}
2021-04-09 15:17:32 +02:00
dispatchStateChanged ( state = = 1 | | switchingStream ? STATE_ESTABLISHED : STATE_RECONNECTING ) ;
if ( switchingStream & & ( state = = 0 | | state = = 1 & & inTransition ) ) {
AndroidUtilities . runOnUIThread ( switchingStreamTimeoutRunnable = ( ) - > {
if ( switchingStreamTimeoutRunnable = = null ) {
return ;
}
switchingStream = false ;
2021-06-25 02:43:10 +02:00
updateConnectionState ( type , 0 , true ) ;
2021-04-09 15:17:32 +02:00
switchingStreamTimeoutRunnable = null ;
} , 3000 ) ;
}
if ( state = = 0 ) {
startGroupCheckShortpoll ( ) ;
2021-06-25 02:43:10 +02:00
if ( playedConnectedSound & & spPlayId = = 0 & & ! switchingStream & & ! switchingAccount ) {
2021-04-09 15:17:32 +02:00
Utilities . globalQueue . postRunnable ( ( ) - > {
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
2021-03-19 11:25:58 +01:00
}
2021-04-09 15:17:32 +02:00
spPlayId = soundPool . play ( spVoiceChatConnecting , 1 . 0f , 1 . 0f , 0 , - 1 , 1 ) ;
} ) ;
}
} else {
cancelGroupCheckShortPoll ( ) ;
if ( ! inTransition ) {
switchingStream = false ;
2021-06-25 02:43:10 +02:00
switchingAccount = false ;
2021-04-09 15:17:32 +02:00
}
if ( switchingStreamTimeoutRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( switchingStreamTimeoutRunnable ) ;
switchingStreamTimeoutRunnable = null ;
}
if ( playedConnectedSound ) {
Utilities . globalQueue . postRunnable ( ( ) - > {
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
spPlayId = 0 ;
2021-03-19 11:25:58 +01:00
}
2021-04-09 15:17:32 +02:00
} ) ;
if ( connectingSoundRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( connectingSoundRunnable ) ;
connectingSoundRunnable = null ;
}
} else {
2021-04-14 03:44:46 +02:00
playConnectedSound ( ) ;
2021-04-09 15:17:32 +02:00
}
if ( ! wasConnected ) {
wasConnected = true ;
2021-06-25 02:43:10 +02:00
if ( reconnectScreenCapture ) {
createGroupInstance ( CAPTURE_DEVICE_SCREEN , false ) ;
reconnectScreenCapture = false ;
}
NativeInstance instance = tgVoip [ CAPTURE_DEVICE_CAMERA ] ;
2021-04-09 15:17:32 +02:00
if ( instance ! = null ) {
if ( ! micMute ) {
2021-06-25 02:43:10 +02:00
instance . setMuteMicrophone ( false ) ;
2021-01-28 15:15:51 +01:00
}
2020-12-24 06:36:01 +01:00
}
2021-04-09 15:17:32 +02:00
setParticipantsVolume ( ) ;
}
2021-03-19 11:25:58 +01:00
}
2020-12-23 08:48:30 +01:00
}
2021-03-19 11:25:58 +01:00
public void setParticipantsVolume ( ) {
2021-06-25 02:43:10 +02:00
NativeInstance instance = tgVoip [ CAPTURE_DEVICE_CAMERA ] ;
2021-03-19 11:25:58 +01:00
if ( instance ! = null ) {
for ( int a = 0 , N = groupCall . participants . size ( ) ; a < N ; a + + ) {
TLRPC . TL_groupCallParticipant participant = groupCall . participants . valueAt ( a ) ;
if ( participant . self | | participant . source = = 0 | | ! participant . can_self_unmute & & participant . muted ) {
continue ;
}
if ( participant . muted_by_you ) {
2021-07-15 16:24:57 +02:00
setParticipantVolume ( participant , 0 ) ;
2021-03-19 11:25:58 +01:00
} else {
2021-07-15 16:24:57 +02:00
setParticipantVolume ( participant , ChatObject . getParticipantVolume ( participant ) ) ;
2021-03-19 11:25:58 +01:00
}
}
}
}
2021-07-15 16:24:57 +02:00
public void setParticipantVolume ( TLRPC . TL_groupCallParticipant participant , int volume ) {
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setVolume ( participant . source , volume / 10000 . 0 ) ;
if ( participant . presentation ! = null & & participant . presentation . audio_source ! = 0 ) {
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setVolume ( participant . presentation . audio_source , volume / 10000 . 0 ) ;
}
2021-01-28 15:15:51 +01:00
}
2021-03-19 11:25:58 +01:00
public boolean isSwitchingStream ( ) {
return switchingStream ;
}
2017-03-31 01:58:05 +02:00
private void initiateActualEncryptedCall ( ) {
if ( timeoutRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( timeoutRunnable ) ;
timeoutRunnable = null ;
}
try {
2020-08-14 18:58:22 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " InitCall: keyID= " + keyFingerprint ) ;
}
SharedPreferences nprefs = MessagesController . getNotificationsSettings ( currentAccount ) ;
Set < String > set = nprefs . getStringSet ( " calls_access_hashes " , null ) ;
HashSet < String > hashes ;
if ( set ! = null ) {
hashes = new HashSet < > ( set ) ;
} else {
hashes = new HashSet < > ( ) ;
}
2020-12-23 08:48:30 +01:00
hashes . add ( privateCall . id + " " + privateCall . access_hash + " " + System . currentTimeMillis ( ) ) ;
2020-08-14 18:58:22 +02:00
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 ) {
2017-07-08 18:32:04 +02:00
itr . remove ( ) ;
2020-08-14 18:58:22 +02:00
} else {
try {
long t = Long . parseLong ( s [ 2 ] ) ;
if ( t < oldestTime ) {
oldestTime = t ;
oldest = item ;
2017-07-08 18:32:04 +02:00
}
2020-08-14 18:58:22 +02:00
} catch ( Exception x ) {
2017-07-08 18:32:04 +02:00
itr . remove ( ) ;
}
}
}
2020-08-14 18:58:22 +02:00
if ( oldest ! = null ) {
2017-07-08 18:32:04 +02:00
hashes . remove ( oldest ) ;
2020-08-14 18:58:22 +02:00
}
2017-07-08 18:32:04 +02:00
}
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 ( ) ;
// 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 ( ) ) ) ;
2020-08-14 18:58:22 +02:00
final Instance . ServerConfig serverConfig = Instance . getGlobalServerConfig ( ) ;
2020-03-30 14:00:09 +02:00
final boolean enableAec = ! ( sysAecAvailable & & serverConfig . useSystemAec ) ;
final boolean enableNs = ! ( sysNsAvailable & & serverConfig . useSystemNs ) ;
2020-12-23 08:48:30 +01:00
final String logFilePath = BuildVars . DEBUG_VERSION ? VoIPHelper . getLogFilePath ( " voip " + privateCall . id ) : VoIPHelper . getLogFilePath ( privateCall . id , false ) ;
2020-10-01 03:59:32 +02:00
final String statisLogFilePath = " " ;
2020-12-23 08:48:30 +01:00
final Instance . Config config = new Instance . Config ( initializationTimeout , receiveTimeout , voipDataSaving , privateCall . p2p_allowed , enableAec , enableNs , true , false , serverConfig . enableStunMarking , logFilePath , statisLogFilePath , privateCall . protocol . max_layer ) ;
2020-03-30 14:00:09 +02:00
// 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 ) ;
2020-08-14 18:58:22 +02:00
final int endpointType = forceTcp ? Instance . ENDPOINT_TYPE_TCP_RELAY : Instance . ENDPOINT_TYPE_UDP_RELAY ;
2020-12-23 08:48:30 +01:00
final Instance . Endpoint [ ] endpoints = new Instance . Endpoint [ privateCall . connections . size ( ) ] ;
2020-03-30 14:00:09 +02:00
for ( int i = 0 ; i < endpoints . length ; i + + ) {
2020-12-23 08:48:30 +01:00
final TLRPC . PhoneConnection connection = privateCall . connections . get ( i ) ;
2020-08-14 18:58:22 +02:00
endpoints [ i ] = new Instance . Endpoint ( connection instanceof TLRPC . TL_phoneConnectionWebrtc , connection . id , connection . ip , connection . ipv6 , connection . port , endpointType , connection . peer_tag , connection . turn , connection . stun , connection . username , connection . password ) ;
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
2020-08-14 18:58:22 +02:00
Instance . Proxy proxy = null ;
2020-03-30 14:00:09 +02:00
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-08-14 18:58:22 +02:00
proxy = new Instance . 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
2020-08-14 18:58:22 +02:00
final Instance . EncryptionKey encryptionKey = new Instance . EncryptionKey ( authKey , isOutgoing ) ;
2020-03-30 14:00:09 +02:00
2020-12-23 08:48:30 +01:00
boolean newAvailable = " 2.7.7 " . compareTo ( privateCall . protocol . library_versions . get ( 0 ) ) < = 0 ;
2021-06-25 02:43:10 +02:00
if ( captureDevice [ CAPTURE_DEVICE_CAMERA ] ! = 0 & & ! newAvailable ) {
NativeInstance . destroyVideoCapturer ( captureDevice [ CAPTURE_DEVICE_CAMERA ] ) ;
captureDevice [ CAPTURE_DEVICE_CAMERA ] = 0 ;
videoState [ CAPTURE_DEVICE_CAMERA ] = Instance . VIDEO_STATE_INACTIVE ;
}
if ( ! isOutgoing ) {
if ( videoCall & & ( Build . VERSION . SDK_INT < 23 | | checkSelfPermission ( Manifest . permission . CAMERA ) = = PackageManager . PERMISSION_GRANTED ) ) {
captureDevice [ CAPTURE_DEVICE_CAMERA ] = NativeInstance . createVideoCapturer ( localSink [ CAPTURE_DEVICE_CAMERA ] , isFrontFaceCamera ? 1 : 0 ) ;
videoState [ CAPTURE_DEVICE_CAMERA ] = Instance . VIDEO_STATE_ACTIVE ;
} else {
videoState [ CAPTURE_DEVICE_CAMERA ] = Instance . VIDEO_STATE_INACTIVE ;
}
2020-08-14 18:58:22 +02:00
}
2020-03-30 14:00:09 +02:00
// init
2021-06-25 02:43:10 +02:00
tgVoip [ CAPTURE_DEVICE_CAMERA ] = Instance . makeInstance ( privateCall . protocol . library_versions . get ( 0 ) , config , persistentStateFilePath , endpoints , proxy , getNetworkType ( ) , encryptionKey , remoteSink [ CAPTURE_DEVICE_CAMERA ] , captureDevice [ CAPTURE_DEVICE_CAMERA ] , ( uids , levels , voice ) - > {
2021-01-28 15:15:51 +01:00
if ( sharedInstance = = null | | privateCall = = null ) {
return ;
}
NotificationCenter . getGlobalInstance ( ) . postNotificationName ( NotificationCenter . webRtcMicAmplitudeEvent , levels [ 0 ] ) ;
} ) ;
2021-06-25 02:43:10 +02:00
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setOnStateUpdatedListener ( this : : onConnectionStateChanged ) ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setOnSignalBarsUpdatedListener ( this : : onSignalBarCountChanged ) ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setOnSignalDataListener ( this : : onSignalingData ) ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setOnRemoteMediaStateUpdatedListener ( ( audioState , videoState ) - > AndroidUtilities . runOnUIThread ( ( ) - > {
remoteAudioState = audioState ;
remoteVideoState = videoState ;
checkIsNear ( ) ;
for ( int a = 0 ; a < stateListeners . size ( ) ; a + + ) {
StateListener l = stateListeners . get ( a ) ;
l . onMediaStateUpdated ( audioState , videoState ) ;
}
} ) ) ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setMuteMicrophone ( micMute ) ;
2020-08-14 18:58:22 +02:00
if ( newAvailable ! = isVideoAvailable ) {
isVideoAvailable = newAvailable ;
for ( int a = 0 ; a < stateListeners . size ( ) ; a + + ) {
StateListener l = stateListeners . get ( a ) ;
l . onVideoAvailableChange ( isVideoAvailable ) ;
}
}
2021-06-25 02:43:10 +02:00
captureDevice [ CAPTURE_DEVICE_CAMERA ] = 0 ;
2020-03-30 14:00:09 +02:00
2017-03-31 01:58:05 +02:00
AndroidUtilities . runOnUIThread ( new Runnable ( ) {
@Override
public void run ( ) {
2021-06-25 02:43:10 +02:00
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ) {
updateTrafficStats ( tgVoip [ CAPTURE_DEVICE_CAMERA ] , null ) ;
2020-03-30 14:00:09 +02:00
AndroidUtilities . runOnUIThread ( this , 5000 ) ;
}
2017-03-31 01:58:05 +02:00
}
} , 5000 ) ;
} catch ( Exception x ) {
2020-08-14 18:58:22 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " error starting call " , x ) ;
}
2017-03-31 01:58:05 +02:00
callFailed ( ) ;
}
}
2021-04-14 03:44:46 +02:00
public void playConnectedSound ( ) {
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spVoiceChatStartId , 1 . 0f , 1 . 0f , 0 , 0 , 1 ) ) ;
playedConnectedSound = true ;
}
2017-03-31 01:58:05 +02:00
private void startConnectingSound ( ) {
2020-12-23 08:48:30 +01:00
Utilities . globalQueue . postRunnable ( ( ) - > {
2021-03-19 11:25:58 +01:00
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
2020-12-23 08:48:30 +01:00
}
2021-03-19 11:25:58 +01:00
spPlayId = soundPool . play ( spConnectingId , 1 , 1 , 0 , - 1 , 1 ) ;
if ( spPlayId = = 0 ) {
2020-12-23 08:48:30 +01:00
AndroidUtilities . runOnUIThread ( connectingSoundRunnable = new Runnable ( ) {
@Override
public void run ( ) {
if ( sharedInstance = = null ) {
return ;
}
Utilities . globalQueue . postRunnable ( ( ) - > {
2021-03-19 11:25:58 +01:00
if ( spPlayId = = 0 ) {
spPlayId = soundPool . play ( spConnectingId , 1 , 1 , 0 , - 1 , 1 ) ;
2020-12-23 08:48:30 +01:00
}
2021-03-19 11:25:58 +01:00
if ( spPlayId = = 0 ) {
2020-12-23 08:48:30 +01:00
AndroidUtilities . runOnUIThread ( this , 100 ) ;
} else {
connectingSoundRunnable = null ;
}
} ) ;
2020-08-14 18:58:22 +02:00
}
2020-12-23 08:48:30 +01:00
} , 100 ) ;
}
} ) ;
2017-03-31 01:58:05 +02:00
}
2020-08-14 18:58:22 +02:00
public void onSignalingData ( byte [ ] data ) {
2020-12-23 08:48:30 +01:00
if ( privateCall = = null ) {
2020-08-14 18:58:22 +02:00
return ;
}
TLRPC . TL_phone_sendSignalingData req = new TLRPC . TL_phone_sendSignalingData ( ) ;
req . peer = new TLRPC . TL_inputPhoneCall ( ) ;
2020-12-23 08:48:30 +01:00
req . peer . access_hash = privateCall . access_hash ;
req . peer . id = privateCall . id ;
2020-08-14 18:58:22 +02:00
req . data = data ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
} ) ;
}
public boolean isVideoAvailable ( ) {
return isVideoAvailable ;
2017-03-31 01:58:05 +02:00
}
2020-08-14 18:58:22 +02:00
void onMediaButtonEvent ( KeyEvent ev ) {
2021-06-25 02:43:10 +02:00
if ( ev = = null ) {
return ;
}
2020-08-14 18:58:22 +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 {
2020-12-23 08:48:30 +01:00
setMicMute ( ! isMicMute ( ) , false , true ) ;
2017-03-31 01:58:05 +02:00
}
}
}
}
2020-08-14 18:58:22 +02:00
public byte [ ] getGA ( ) {
2017-03-31 01:58:05 +02:00
return g_a ;
}
2020-08-14 18:58:22 +02:00
public void forceRating ( ) {
forceRating = true ;
2017-03-31 01:58:05 +02:00
}
2017-12-08 18:35:59 +01:00
2020-08-14 18:58:22 +02:00
private String [ ] getEmoji ( ) {
ByteArrayOutputStream os = new ByteArrayOutputStream ( ) ;
try {
2018-07-30 04:07:02 +02:00
os . write ( authKey ) ;
os . write ( g_a ) ;
2020-08-14 18:58:22 +02:00
} catch ( IOException ignore ) {
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
return EncryptionKeyEmojifier . emojifyForCall ( Utilities . computeSHA256 ( os . toByteArray ( ) , 0 , os . size ( ) ) ) ;
2018-07-30 04:07:02 +02:00
}
2021-06-25 02:43:10 +02:00
public boolean hasEarpiece ( ) {
if ( USE_CONNECTION_SERVICE ) {
if ( systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
int routeMask = systemCallConnection . getCallAudioState ( ) . getSupportedRouteMask ( ) ;
return ( routeMask & ( CallAudioState . ROUTE_EARPIECE | CallAudioState . ROUTE_WIRED_HEADSET ) ) ! = 0 ;
}
}
if ( ( ( TelephonyManager ) getSystemService ( TELEPHONY_SERVICE ) ) . getPhoneType ( ) ! = TelephonyManager . PHONE_TYPE_NONE ) {
return true ;
}
if ( mHasEarpiece ! = null ) {
return mHasEarpiece ;
}
// not calculated yet, do it now
try {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
Method method = AudioManager . class . getMethod ( " getDevicesForStream " , Integer . TYPE ) ;
Field field = AudioManager . class . getField ( " DEVICE_OUT_EARPIECE " ) ;
int earpieceFlag = field . getInt ( null ) ;
int bitmaskResult = ( int ) method . invoke ( am , AudioManager . STREAM_VOICE_CALL ) ;
// check if masked by the earpiece flag
if ( ( bitmaskResult & earpieceFlag ) = = earpieceFlag ) {
mHasEarpiece = Boolean . TRUE ;
} else {
mHasEarpiece = Boolean . FALSE ;
}
} catch ( Throwable error ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error while checking earpiece! " , error ) ;
}
mHasEarpiece = Boolean . TRUE ;
}
return mHasEarpiece ;
}
private int getStatsNetworkType ( ) {
int netType = StatsController . TYPE_WIFI ;
if ( lastNetInfo ! = null ) {
if ( lastNetInfo . getType ( ) = = ConnectivityManager . TYPE_MOBILE ) {
netType = lastNetInfo . isRoaming ( ) ? StatsController . TYPE_ROAMING : StatsController . TYPE_MOBILE ;
}
}
return netType ;
}
protected void setSwitchingCamera ( boolean switching , boolean isFrontFace ) {
switchingCamera = switching ;
if ( ! switching ) {
isFrontFaceCamera = isFrontFace ;
for ( int a = 0 ; a < stateListeners . size ( ) ; a + + ) {
StateListener l = stateListeners . get ( a ) ;
l . onCameraSwitch ( isFrontFaceCamera ) ;
}
}
}
public void registerStateListener ( StateListener l ) {
if ( stateListeners . contains ( l ) ) {
return ;
}
stateListeners . add ( l ) ;
if ( currentState ! = 0 ) {
l . onStateChanged ( currentState ) ;
}
if ( signalBarCount ! = 0 ) {
l . onSignalBarsCountChanged ( signalBarCount ) ;
}
}
public void unregisterStateListener ( StateListener l ) {
stateListeners . remove ( l ) ;
}
public void editCallMember ( TLObject object , Boolean mute , Boolean muteVideo , Integer volume , Boolean raiseHand , Runnable onComplete ) {
if ( object = = null | | groupCall = = null ) {
return ;
}
TLRPC . TL_phone_editGroupCallParticipant req = new TLRPC . TL_phone_editGroupCallParticipant ( ) ;
req . call = groupCall . getInputGroupCall ( ) ;
if ( object instanceof TLRPC . User ) {
TLRPC . User user = ( TLRPC . User ) object ;
if ( UserObject . isUserSelf ( user ) & & groupCallPeer ! = null ) {
req . participant = groupCallPeer ;
} else {
req . participant = MessagesController . getInputPeer ( user ) ;
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " edit group call part id = " + req . participant . user_id + " access_hash = " + req . participant . user_id ) ;
2020-12-23 08:48:30 +01:00
}
2020-03-30 14:00:09 +02:00
}
2021-06-25 02:43:10 +02:00
} else if ( object instanceof TLRPC . Chat ) {
TLRPC . Chat chat = ( TLRPC . Chat ) object ;
req . participant = MessagesController . getInputPeer ( chat ) ;
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " edit group call part id = " + ( req . participant . chat_id ! = 0 ? req . participant . chat_id : req . participant . channel_id ) + " access_hash = " + req . participant . access_hash ) ;
}
}
if ( mute ! = null ) {
req . muted = mute ;
req . flags | = 1 ;
}
if ( volume ! = null ) {
req . volume = volume ;
req . flags | = 2 ;
}
if ( raiseHand ! = null ) {
req . raise_hand = raiseHand ;
req . flags | = 4 ;
}
if ( muteVideo ! = null ) {
req . video_stopped = muteVideo ;
req . flags | = 8 ;
}
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " edit group call flags = " + req . flags ) ;
}
int account = currentAccount ;
AccountInstance . getInstance ( account ) . getConnectionsManager ( ) . sendRequest ( req , ( response , error ) - > {
if ( response ! = null ) {
AccountInstance . getInstance ( account ) . getMessagesController ( ) . processUpdates ( ( TLRPC . Updates ) response , false ) ;
2021-07-15 16:24:57 +02:00
} else if ( error ! = null ) {
if ( " GROUPCALL_VIDEO_TOO_MUCH " . equals ( error . text ) ) {
groupCall . reloadGroupCall ( ) ;
}
2021-06-25 02:43:10 +02:00
}
if ( onComplete ! = null ) {
AndroidUtilities . runOnUIThread ( onComplete ) ;
}
2020-12-23 08:48:30 +01:00
} ) ;
2018-07-30 04:07:02 +02:00
}
2021-06-25 02:43:10 +02:00
public boolean isMicMute ( ) {
return micMute ;
}
public void toggleSpeakerphoneOrShowRouteSheet ( Context context , boolean fromOverlayWindow ) {
if ( isBluetoothHeadsetConnected ( ) & & hasEarpiece ( ) ) {
BottomSheet . Builder builder = new BottomSheet . Builder ( context )
. setTitle ( LocaleController . getString ( " VoipOutputDevices " , R . string . VoipOutputDevices ) , true )
. setItems ( new CharSequence [ ] {
LocaleController . getString ( " VoipAudioRoutingSpeaker " , R . string . VoipAudioRoutingSpeaker ) ,
isHeadsetPlugged ? LocaleController . getString ( " VoipAudioRoutingHeadset " , R . string . VoipAudioRoutingHeadset ) : LocaleController . getString ( " VoipAudioRoutingEarpiece " , R . string . VoipAudioRoutingEarpiece ) ,
currentBluetoothDeviceName ! = null ? currentBluetoothDeviceName : LocaleController . getString ( " VoipAudioRoutingBluetooth " , R . string . VoipAudioRoutingBluetooth ) } ,
new int [ ] { R . drawable . calls_menu_speaker ,
isHeadsetPlugged ? R . drawable . calls_menu_headset : R . drawable . calls_menu_phone ,
R . drawable . calls_menu_bluetooth } , ( dialog , which ) - > {
if ( getSharedInstance ( ) = = null ) {
return ;
}
setAudioOutput ( which ) ;
} ) ;
BottomSheet bottomSheet = builder . create ( ) ;
if ( fromOverlayWindow ) {
if ( Build . VERSION . SDK_INT > = 26 ) {
bottomSheet . getWindow ( ) . setType ( WindowManager . LayoutParams . TYPE_APPLICATION_OVERLAY ) ;
} else {
bottomSheet . getWindow ( ) . setType ( WindowManager . LayoutParams . TYPE_SYSTEM_ALERT ) ;
}
}
builder . show ( ) ;
return ;
}
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
if ( hasEarpiece ( ) ) {
systemCallConnection . setAudioRoute ( systemCallConnection . getCallAudioState ( ) . getRoute ( ) = = CallAudioState . ROUTE_SPEAKER ? CallAudioState . ROUTE_WIRED_OR_EARPIECE : CallAudioState . ROUTE_SPEAKER ) ;
} else {
systemCallConnection . setAudioRoute ( systemCallConnection . getCallAudioState ( ) . getRoute ( ) = = CallAudioState . ROUTE_BLUETOOTH ? CallAudioState . ROUTE_WIRED_OR_EARPIECE : CallAudioState . ROUTE_BLUETOOTH ) ;
}
} else if ( audioConfigured & & ! USE_CONNECTION_SERVICE ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( hasEarpiece ( ) ) {
am . setSpeakerphoneOn ( ! am . isSpeakerphoneOn ( ) ) ;
} else {
am . setBluetoothScoOn ( ! am . isBluetoothScoOn ( ) ) ;
}
updateOutputGainControlState ( ) ;
} else {
speakerphoneStateToSet = ! speakerphoneStateToSet ;
}
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
}
public void setAudioOutput ( int which ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " setAudioOutput " + which ) ;
}
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null ) {
switch ( which ) {
case 2 :
systemCallConnection . setAudioRoute ( CallAudioState . ROUTE_BLUETOOTH ) ;
break ;
case 1 :
systemCallConnection . setAudioRoute ( CallAudioState . ROUTE_WIRED_OR_EARPIECE ) ;
break ;
case 0 :
systemCallConnection . setAudioRoute ( CallAudioState . ROUTE_SPEAKER ) ;
break ;
}
} else if ( audioConfigured & & ! USE_CONNECTION_SERVICE ) {
switch ( which ) {
case 2 :
if ( ! bluetoothScoActive ) {
needSwitchToBluetoothAfterScoActivates = true ;
try {
am . startBluetoothSco ( ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
} else {
am . setBluetoothScoOn ( true ) ;
am . setSpeakerphoneOn ( false ) ;
}
2021-07-15 16:24:57 +02:00
audioRouteToSet = AUDIO_ROUTE_BLUETOOTH ;
2021-06-25 02:43:10 +02:00
break ;
case 1 :
needSwitchToBluetoothAfterScoActivates = false ;
if ( bluetoothScoActive | | bluetoothScoConnecting ) {
am . stopBluetoothSco ( ) ;
bluetoothScoActive = false ;
bluetoothScoConnecting = false ;
}
am . setSpeakerphoneOn ( false ) ;
am . setBluetoothScoOn ( false ) ;
2021-07-15 16:24:57 +02:00
audioRouteToSet = AUDIO_ROUTE_EARPIECE ;
2021-06-25 02:43:10 +02:00
break ;
case 0 :
needSwitchToBluetoothAfterScoActivates = false ;
if ( bluetoothScoActive | | bluetoothScoConnecting ) {
am . stopBluetoothSco ( ) ;
bluetoothScoActive = false ;
bluetoothScoConnecting = false ;
}
am . setBluetoothScoOn ( false ) ;
am . setSpeakerphoneOn ( true ) ;
2021-07-15 16:24:57 +02:00
audioRouteToSet = AUDIO_ROUTE_SPEAKER ;
2021-06-25 02:43:10 +02:00
break ;
}
updateOutputGainControlState ( ) ;
} else {
switch ( which ) {
case 2 :
audioRouteToSet = AUDIO_ROUTE_BLUETOOTH ;
speakerphoneStateToSet = false ;
break ;
case 1 :
audioRouteToSet = AUDIO_ROUTE_EARPIECE ;
speakerphoneStateToSet = false ;
break ;
case 0 :
audioRouteToSet = AUDIO_ROUTE_SPEAKER ;
speakerphoneStateToSet = true ;
break ;
}
}
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
}
public boolean isSpeakerphoneOn ( ) {
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
int route = systemCallConnection . getCallAudioState ( ) . getRoute ( ) ;
return hasEarpiece ( ) ? route = = CallAudioState . ROUTE_SPEAKER : route = = CallAudioState . ROUTE_BLUETOOTH ;
} else if ( audioConfigured & & ! USE_CONNECTION_SERVICE ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
return hasEarpiece ( ) ? am . isSpeakerphoneOn ( ) : am . isBluetoothScoOn ( ) ;
}
return speakerphoneStateToSet ;
}
public int getCurrentAudioRoute ( ) {
if ( USE_CONNECTION_SERVICE ) {
if ( systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
switch ( systemCallConnection . getCallAudioState ( ) . getRoute ( ) ) {
case CallAudioState . ROUTE_BLUETOOTH :
return AUDIO_ROUTE_BLUETOOTH ;
case CallAudioState . ROUTE_EARPIECE :
case CallAudioState . ROUTE_WIRED_HEADSET :
return AUDIO_ROUTE_EARPIECE ;
case CallAudioState . ROUTE_SPEAKER :
return AUDIO_ROUTE_SPEAKER ;
}
}
return audioRouteToSet ;
}
if ( audioConfigured ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( am . isBluetoothScoOn ( ) ) {
return AUDIO_ROUTE_BLUETOOTH ;
} else if ( am . isSpeakerphoneOn ( ) ) {
return AUDIO_ROUTE_SPEAKER ;
} else {
return AUDIO_ROUTE_EARPIECE ;
}
}
return audioRouteToSet ;
}
public String getDebugString ( ) {
return tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ? tgVoip [ CAPTURE_DEVICE_CAMERA ] . getDebugInfo ( ) : " " ;
}
public long getCallDuration ( ) {
if ( callStartTime = = 0 ) {
return 0 ;
}
return SystemClock . elapsedRealtime ( ) - callStartTime ;
}
public void stopRinging ( ) {
if ( ringtonePlayer ! = null ) {
ringtonePlayer . stop ( ) ;
ringtonePlayer . release ( ) ;
ringtonePlayer = null ;
}
if ( vibrator ! = null ) {
vibrator . cancel ( ) ;
vibrator = null ;
}
}
private void showNotification ( String name , Bitmap photo ) {
Intent intent = new Intent ( this , LaunchActivity . class ) . setAction ( groupCall ! = null ? " voip_chat " : " voip " ) ;
if ( groupCall ! = null ) {
intent . putExtra ( " currentAccount " , currentAccount ) ;
}
Notification . Builder builder = new Notification . Builder ( this )
. setContentTitle ( groupCall ! = null ? LocaleController . getString ( " VoipVoiceChat " , R . string . VoipVoiceChat ) : LocaleController . getString ( " VoipOutgoingCall " , R . string . VoipOutgoingCall ) )
. setContentText ( name )
. setContentIntent ( PendingIntent . getActivity ( this , 50 , intent , 0 ) ) ;
if ( groupCall ! = null ) {
builder . setSmallIcon ( isMicMute ( ) ? R . drawable . voicechat_muted : R . drawable . voicechat_active ) ;
} else {
builder . setSmallIcon ( R . drawable . notification ) ;
}
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN ) {
Intent endIntent = new Intent ( this , VoIPActionsReceiver . class ) ;
endIntent . setAction ( getPackageName ( ) + " .END_CALL " ) ;
builder . addAction ( R . drawable . ic_call_end_white_24dp , groupCall ! = null ? LocaleController . getString ( " VoipGroupLeaveAlertTitle " , R . string . VoipGroupLeaveAlertTitle ) : LocaleController . getString ( " VoipEndCall " , R . string . VoipEndCall ) , PendingIntent . getBroadcast ( this , 0 , endIntent , PendingIntent . FLAG_UPDATE_CURRENT ) ) ;
builder . setPriority ( Notification . PRIORITY_MAX ) ;
}
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN_MR1 ) {
builder . setShowWhen ( false ) ;
}
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . O ) {
builder . setColor ( 0xff282e31 ) ;
builder . setColorized ( true ) ;
} else if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . LOLLIPOP ) {
builder . setColor ( 0xff2ca5e0 ) ;
}
if ( Build . VERSION . SDK_INT > = 26 ) {
NotificationsController . checkOtherNotificationsChannel ( ) ;
builder . setChannelId ( NotificationsController . OTHER_NOTIFICATIONS_CHANNEL ) ;
}
if ( photo ! = null ) {
builder . setLargeIcon ( photo ) ;
}
startForeground ( ID_ONGOING_CALL_NOTIFICATION , builder . getNotification ( ) ) ;
}
private void startRingtoneAndVibration ( int chatID ) {
SharedPreferences prefs = MessagesController . getNotificationsSettings ( currentAccount ) ;
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
boolean needRing = am . getRingerMode ( ) ! = AudioManager . RINGER_MODE_SILENT ;
if ( needRing ) {
ringtonePlayer = new MediaPlayer ( ) ;
ringtonePlayer . setOnPreparedListener ( mediaPlayer - > {
try {
ringtonePlayer . start ( ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
} ) ;
ringtonePlayer . setLooping ( true ) ;
if ( isHeadsetPlugged ) {
ringtonePlayer . setAudioStreamType ( AudioManager . STREAM_VOICE_CALL ) ;
} else {
ringtonePlayer . setAudioStreamType ( AudioManager . STREAM_RING ) ;
if ( ! USE_CONNECTION_SERVICE ) {
am . requestAudioFocus ( this , AudioManager . STREAM_RING , AudioManager . AUDIOFOCUS_GAIN ) ;
}
}
try {
String notificationUri ;
if ( prefs . getBoolean ( " custom_ " + chatID , false ) ) {
notificationUri = prefs . getString ( " ringtone_path_ " + chatID , RingtoneManager . getDefaultUri ( RingtoneManager . TYPE_RINGTONE ) . toString ( ) ) ;
} else {
notificationUri = prefs . getString ( " CallsRingtonePath " , RingtoneManager . getDefaultUri ( RingtoneManager . TYPE_RINGTONE ) . toString ( ) ) ;
}
ringtonePlayer . setDataSource ( this , Uri . parse ( notificationUri ) ) ;
ringtonePlayer . prepareAsync ( ) ;
} catch ( Exception e ) {
FileLog . e ( e ) ;
if ( ringtonePlayer ! = null ) {
ringtonePlayer . release ( ) ;
ringtonePlayer = null ;
}
}
int vibrate ;
if ( prefs . getBoolean ( " custom_ " + chatID , false ) ) {
vibrate = prefs . getInt ( " calls_vibrate_ " + chatID , 0 ) ;
} else {
vibrate = prefs . getInt ( " vibrate_calls " , 0 ) ;
}
if ( ( vibrate ! = 2 & & vibrate ! = 4 & & ( am . getRingerMode ( ) = = AudioManager . RINGER_MODE_VIBRATE | | am . getRingerMode ( ) = = AudioManager . RINGER_MODE_NORMAL ) ) | | ( vibrate = = 4 & & am . getRingerMode ( ) = = AudioManager . RINGER_MODE_VIBRATE ) ) {
vibrator = ( Vibrator ) getSystemService ( VIBRATOR_SERVICE ) ;
long duration = 700 ;
if ( vibrate = = 1 ) {
duration / = 2 ;
} else if ( vibrate = = 3 ) {
duration * = 2 ;
}
vibrator . vibrate ( new long [ ] { 0 , duration , 500 } , 0 ) ;
}
}
}
2018-07-30 04:07:02 +02:00
@Override
2021-06-25 02:43:10 +02:00
public void onDestroy ( ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " =============== VoIPService STOPPING =============== " ) ;
}
stopForeground ( true ) ;
stopRinging ( ) ;
if ( ApplicationLoader . mainInterfacePaused | | ! ApplicationLoader . isScreenOn ) {
MessagesController . getInstance ( currentAccount ) . ignoreSetOnline = false ;
}
NotificationCenter . getInstance ( currentAccount ) . removeObserver ( this , NotificationCenter . appDidLogout ) ;
SensorManager sm = ( SensorManager ) getSystemService ( SENSOR_SERVICE ) ;
Sensor proximity = sm . getDefaultSensor ( Sensor . TYPE_PROXIMITY ) ;
if ( proximity ! = null ) {
sm . unregisterListener ( this ) ;
}
if ( proximityWakelock ! = null & & proximityWakelock . isHeld ( ) ) {
proximityWakelock . release ( ) ;
}
if ( updateNotificationRunnable ! = null ) {
Utilities . globalQueue . cancelRunnable ( updateNotificationRunnable ) ;
updateNotificationRunnable = null ;
}
if ( switchingStreamTimeoutRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( switchingStreamTimeoutRunnable ) ;
switchingStreamTimeoutRunnable = null ;
}
unregisterReceiver ( receiver ) ;
if ( timeoutRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( timeoutRunnable ) ;
timeoutRunnable = null ;
}
super . onDestroy ( ) ;
sharedInstance = null ;
Arrays . fill ( mySource , 0 ) ;
cancelGroupCheckShortPoll ( ) ;
AndroidUtilities . runOnUIThread ( ( ) - > NotificationCenter . getGlobalInstance ( ) . postNotificationName ( NotificationCenter . didEndCall ) ) ;
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ) {
StatsController . getInstance ( currentAccount ) . incrementTotalCallsTime ( getStatsNetworkType ( ) , ( int ) ( getCallDuration ( ) / 1000 ) % 5 ) ;
onTgVoipPreStop ( ) ;
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] . isGroup ( ) ) {
NativeInstance instance = tgVoip [ CAPTURE_DEVICE_CAMERA ] ;
Utilities . globalQueue . postRunnable ( instance : : stopGroup ) ;
AccountInstance . getInstance ( currentAccount ) . getConnectionsManager ( ) . cancelRequest ( currentStreamRequestId , true ) ;
currentStreamRequestId = 0 ;
} else {
Instance . FinalState state = tgVoip [ CAPTURE_DEVICE_CAMERA ] . stop ( ) ;
updateTrafficStats ( tgVoip [ CAPTURE_DEVICE_CAMERA ] , state . trafficStats ) ;
onTgVoipStop ( state ) ;
2020-08-14 18:58:22 +02:00
}
2021-06-25 02:43:10 +02:00
prevTrafficStats = null ;
callStartTime = 0 ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] = null ;
Instance . destroyInstance ( ) ;
}
if ( tgVoip [ CAPTURE_DEVICE_SCREEN ] ! = null ) {
NativeInstance instance = tgVoip [ CAPTURE_DEVICE_SCREEN ] ;
Utilities . globalQueue . postRunnable ( instance : : stopGroup ) ;
tgVoip [ CAPTURE_DEVICE_SCREEN ] = null ;
}
for ( int a = 0 ; a < captureDevice . length ; a + + ) {
if ( captureDevice [ a ] ! = 0 ) {
if ( destroyCaptureDevice [ a ] ) {
NativeInstance . destroyVideoCapturer ( captureDevice [ a ] ) ;
}
captureDevice [ a ] = 0 ;
2018-07-30 04:07:02 +02:00
}
}
2021-06-25 02:43:10 +02:00
cpuWakelock . release ( ) ;
if ( ! playingSound ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( ! USE_CONNECTION_SERVICE ) {
if ( isBtHeadsetConnected | | bluetoothScoActive | | bluetoothScoConnecting ) {
am . stopBluetoothSco ( ) ;
am . setBluetoothScoOn ( false ) ;
am . setSpeakerphoneOn ( false ) ;
bluetoothScoActive = false ;
bluetoothScoConnecting = false ;
}
if ( onDestroyRunnable = = null ) {
Utilities . globalQueue . postRunnable ( setModeRunnable = ( ) - > {
synchronized ( sync ) {
if ( setModeRunnable = = null ) {
return ;
}
setModeRunnable = null ;
}
try {
am . setMode ( AudioManager . MODE_NORMAL ) ;
} catch ( SecurityException x ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error setting audio more to normal " , x ) ;
}
}
} ) ;
}
am . abandonAudioFocus ( this ) ;
}
am . unregisterMediaButtonEventReceiver ( new ComponentName ( this , VoIPMediaButtonReceiver . class ) ) ;
if ( hasAudioFocus ) {
am . abandonAudioFocus ( this ) ;
}
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . release ( ) ) ;
}
if ( USE_CONNECTION_SERVICE ) {
if ( ! didDeleteConnectionServiceContact ) {
ContactsController . getInstance ( currentAccount ) . deleteConnectionServiceContact ( ) ;
}
if ( systemCallConnection ! = null & & ! playingSound ) {
systemCallConnection . destroy ( ) ;
}
}
ConnectionsManager . getInstance ( currentAccount ) . setAppPaused ( true , false ) ;
VoIPHelper . lastCallTime = SystemClock . elapsedRealtime ( ) ;
setSinks ( null , null ) ;
if ( onDestroyRunnable ! = null ) {
onDestroyRunnable . run ( ) ;
}
if ( ChatObject . isChannel ( chat ) ) {
MessagesController . getInstance ( currentAccount ) . startShortPoll ( chat , classGuid , true ) ;
}
}
public long getCallID ( ) {
return privateCall ! = null ? privateCall . id : 0 ;
}
public void hangUp ( ) {
hangUp ( 0 , null ) ;
}
public void hangUp ( int discard ) {
hangUp ( discard , null ) ;
}
public void hangUp ( Runnable onDone ) {
hangUp ( 0 , onDone ) ;
}
public void acceptIncomingCall ( ) {
MessagesController . getInstance ( currentAccount ) . ignoreSetOnline = false ;
stopRinging ( ) ;
showNotification ( ) ;
configureDeviceForCall ( ) ;
startConnectingSound ( ) ;
dispatchStateChanged ( STATE_EXCHANGING_KEYS ) ;
AndroidUtilities . runOnUIThread ( ( ) - > NotificationCenter . getGlobalInstance ( ) . postNotificationName ( NotificationCenter . didStartedCall ) ) ;
final MessagesStorage messagesStorage = MessagesStorage . getInstance ( currentAccount ) ;
TLRPC . TL_messages_getDhConfig req = new TLRPC . TL_messages_getDhConfig ( ) ;
req . random_length = 256 ;
req . version = messagesStorage . getLastSecretVersion ( ) ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , 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 ) ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " stopping VoIP service, bad prime " ) ;
}
callFailed ( ) ;
return ;
}
messagesStorage . setSecretPBytes ( res . p ) ;
messagesStorage . setSecretG ( res . g ) ;
messagesStorage . setLastSecretVersion ( res . version ) ;
MessagesStorage . getInstance ( currentAccount ) . saveSecretParams ( messagesStorage . getLastSecretVersion ( ) , messagesStorage . getSecretG ( ) , messagesStorage . getSecretPBytes ( ) ) ;
}
byte [ ] salt = new byte [ 256 ] ;
for ( int a = 0 ; a < 256 ; a + + ) {
salt [ a ] = ( byte ) ( ( byte ) ( Utilities . random . nextDouble ( ) * 256 ) ^ res . random [ a ] ) ;
}
if ( privateCall = = null ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " call is null " ) ;
}
callFailed ( ) ;
return ;
}
a_or_b = salt ;
BigInteger g_b = BigInteger . valueOf ( messagesStorage . getSecretG ( ) ) ;
BigInteger p = new BigInteger ( 1 , messagesStorage . getSecretPBytes ( ) ) ;
g_b = g_b . modPow ( new BigInteger ( 1 , salt ) , p ) ;
g_a_hash = privateCall . 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 req1 = new TLRPC . TL_phone_acceptCall ( ) ;
req1 . g_b = g_b_bytes ;
req1 . peer = new TLRPC . TL_inputPhoneCall ( ) ;
req1 . peer . id = privateCall . id ;
req1 . peer . access_hash = privateCall . access_hash ;
req1 . protocol = new TLRPC . TL_phoneCallProtocol ( ) ;
req1 . protocol . udp_p2p = req1 . protocol . udp_reflector = true ;
req1 . protocol . min_layer = CALL_MIN_LAYER ;
req1 . protocol . max_layer = Instance . getConnectionMaxLayer ( ) ;
req1 . protocol . library_versions . addAll ( Instance . AVAILABLE_VERSIONS ) ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req1 , ( response1 , error1 ) - > AndroidUtilities . runOnUIThread ( ( ) - > {
if ( error1 = = null ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . w ( " accept call ok! " + response1 ) ;
}
privateCall = ( ( TLRPC . TL_phone_phoneCall ) response1 ) . phone_call ;
if ( privateCall instanceof TLRPC . TL_phoneCallDiscarded ) {
onCallUpdated ( privateCall ) ;
}
} else {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error on phone.acceptCall: " + error1 ) ;
}
callFailed ( ) ;
}
} ) , ConnectionsManager . RequestFlagFailOnServerErrors ) ;
} else {
callFailed ( ) ;
}
} ) ;
}
public void declineIncomingCall ( int reason , final Runnable onDone ) {
stopRinging ( ) ;
callDiscardReason = reason ;
if ( currentState = = STATE_REQUESTING ) {
if ( delayedStartOutgoingCall ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( delayedStartOutgoingCall ) ;
callEnded ( ) ;
} else {
dispatchStateChanged ( STATE_HANGING_UP ) ;
endCallAfterRequest = true ;
AndroidUtilities . runOnUIThread ( ( ) - > {
if ( currentState = = STATE_HANGING_UP ) {
callEnded ( ) ;
}
} , 5000 ) ;
}
return ;
}
if ( currentState = = STATE_HANGING_UP | | currentState = = STATE_ENDED ) {
return ;
}
dispatchStateChanged ( STATE_HANGING_UP ) ;
if ( privateCall = = null ) {
onDestroyRunnable = onDone ;
callEnded ( ) ;
if ( callReqId ! = 0 ) {
ConnectionsManager . getInstance ( currentAccount ) . cancelRequest ( callReqId , false ) ;
callReqId = 0 ;
}
return ;
}
TLRPC . TL_phone_discardCall req = new TLRPC . TL_phone_discardCall ( ) ;
req . peer = new TLRPC . TL_inputPhoneCall ( ) ;
req . peer . access_hash = privateCall . access_hash ;
req . peer . id = privateCall . id ;
req . duration = ( int ) ( getCallDuration ( ) / 1000 ) ;
req . connection_id = tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ? tgVoip [ CAPTURE_DEVICE_CAMERA ] . getPreferredRelayId ( ) : 0 ;
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 ;
}
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
if ( error ! = null ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " error on phone.discardCall: " + error ) ;
}
} else {
if ( response instanceof TLRPC . TL_updates ) {
TLRPC . TL_updates updates = ( TLRPC . TL_updates ) response ;
MessagesController . getInstance ( currentAccount ) . processUpdates ( updates , false ) ;
}
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " phone.discardCall " + response ) ;
}
}
} , ConnectionsManager . RequestFlagFailOnServerErrors ) ;
onDestroyRunnable = onDone ;
callEnded ( ) ;
}
public void declineIncomingCall ( ) {
declineIncomingCall ( DISCARD_REASON_HANGUP , null ) ;
}
private Class < ? extends Activity > getUIActivityClass ( ) {
return LaunchActivity . class ;
}
@TargetApi ( Build . VERSION_CODES . O )
public CallConnection getConnectionAndStartCall ( ) {
if ( systemCallConnection = = null ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " creating call connection " ) ;
}
systemCallConnection = new CallConnection ( ) ;
systemCallConnection . setInitializing ( ) ;
if ( isOutgoing ) {
delayedStartOutgoingCall = ( ) - > {
delayedStartOutgoingCall = null ;
startOutgoingCall ( ) ;
} ;
AndroidUtilities . runOnUIThread ( delayedStartOutgoingCall , 2000 ) ;
}
systemCallConnection . setAddress ( Uri . fromParts ( " tel " , " +99084 " + user . id , null ) , TelecomManager . PRESENTATION_ALLOWED ) ;
systemCallConnection . setCallerDisplayName ( ContactsController . formatName ( user . first_name , user . last_name ) , TelecomManager . PRESENTATION_ALLOWED ) ;
}
return systemCallConnection ;
}
private void startRinging ( ) {
if ( currentState = = STATE_WAITING_INCOMING ) {
return ;
}
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null ) {
systemCallConnection . setRinging ( ) ;
}
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " starting ringing for call " + privateCall . id ) ;
}
dispatchStateChanged ( STATE_WAITING_INCOMING ) ;
if ( ! notificationsDisabled & & Build . VERSION . SDK_INT > = Build . VERSION_CODES . LOLLIPOP ) {
showIncomingNotification ( ContactsController . formatName ( user . first_name , user . last_name ) , null , user , privateCall . video , 0 ) ;
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " Showing incoming call notification " ) ;
}
} else {
startRingtoneAndVibration ( user . id ) ;
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " Starting incall activity for incoming call " ) ;
}
try {
PendingIntent . getActivity ( VoIPService . this , 12345 , new Intent ( VoIPService . this , LaunchActivity . class ) . setAction ( " voip " ) , 0 ) . send ( ) ;
} catch ( Exception x ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error starting incall activity " , x ) ;
}
}
}
}
public void startRingtoneAndVibration ( ) {
if ( ! startedRinging ) {
startRingtoneAndVibration ( user . id ) ;
startedRinging = true ;
}
}
private void updateServerConfig ( ) {
final SharedPreferences preferences = MessagesController . getMainSettings ( currentAccount ) ;
Instance . 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 ;
Instance . setGlobalServerConfig ( data ) ;
preferences . edit ( ) . putString ( " voip_server_config " , data ) . commit ( ) ;
}
} ) ;
}
private void showNotification ( ) {
if ( user ! = null ) {
showNotification ( ContactsController . formatName ( user . first_name , user . last_name ) , getRoundAvatarBitmap ( user ) ) ;
} else {
showNotification ( chat . title , getRoundAvatarBitmap ( chat ) ) ;
}
}
private void onTgVoipPreStop ( ) {
/ * if ( BuildConfig . DEBUG ) {
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 ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , new RequestDelegate ( ) {
@Override
public void run ( TLObject response , TLRPC . TL_error error ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " Sent debug logs, response= " + response ) ;
}
}
} ) ;
} * /
}
private void onTgVoipStop ( Instance . FinalState finalState ) {
if ( user = = null ) {
return ;
}
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 = privateCall . access_hash ;
req . peer . id = privateCall . id ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error ) - > {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " Sent debug logs, response = " + response ) ;
}
} ) ;
needSendDebugLog = false ;
}
}
private void initializeAccountRelatedThings ( ) {
updateServerConfig ( ) ;
NotificationCenter . getInstance ( currentAccount ) . addObserver ( this , NotificationCenter . appDidLogout ) ;
ConnectionsManager . getInstance ( currentAccount ) . setAppPaused ( false , false ) ;
}
@SuppressLint ( " InvalidWakeLockTag " )
@Override
public void onCreate ( ) {
super . onCreate ( ) ;
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " =============== VoIPService STARTING =============== " ) ;
}
try {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN_MR1 & & am . getProperty ( AudioManager . PROPERTY_OUTPUT_FRAMES_PER_BUFFER ) ! = null ) {
int outFramesPerBuffer = Integer . parseInt ( am . getProperty ( AudioManager . PROPERTY_OUTPUT_FRAMES_PER_BUFFER ) ) ;
Instance . setBufferSize ( outFramesPerBuffer ) ;
} else {
Instance . setBufferSize ( AudioTrack . getMinBufferSize ( 48000 , AudioFormat . CHANNEL_OUT_MONO , AudioFormat . ENCODING_PCM_16BIT ) / 2 ) ;
}
cpuWakelock = ( ( PowerManager ) getSystemService ( POWER_SERVICE ) ) . newWakeLock ( PowerManager . PARTIAL_WAKE_LOCK , " telegram-voip " ) ;
cpuWakelock . acquire ( ) ;
btAdapter = am . isBluetoothScoAvailableOffCall ( ) ? BluetoothAdapter . getDefaultAdapter ( ) : null ;
IntentFilter filter = new IntentFilter ( ) ;
filter . addAction ( ConnectivityManager . CONNECTIVITY_ACTION ) ;
if ( ! USE_CONNECTION_SERVICE ) {
filter . addAction ( ACTION_HEADSET_PLUG ) ;
if ( btAdapter ! = null ) {
filter . addAction ( BluetoothHeadset . ACTION_CONNECTION_STATE_CHANGED ) ;
filter . addAction ( AudioManager . ACTION_SCO_AUDIO_STATE_UPDATED ) ;
}
filter . addAction ( TelephonyManager . ACTION_PHONE_STATE_CHANGED ) ;
filter . addAction ( Intent . ACTION_SCREEN_ON ) ;
filter . addAction ( Intent . ACTION_SCREEN_OFF ) ;
}
registerReceiver ( receiver , filter ) ;
fetchBluetoothDeviceName ( ) ;
am . registerMediaButtonEventReceiver ( new ComponentName ( this , VoIPMediaButtonReceiver . class ) ) ;
if ( ! USE_CONNECTION_SERVICE & & btAdapter ! = null & & btAdapter . isEnabled ( ) ) {
try {
MediaRouter mr = ( MediaRouter ) getSystemService ( Context . MEDIA_ROUTER_SERVICE ) ;
if ( Build . VERSION . SDK_INT < 24 ) {
int headsetState = btAdapter . getProfileConnectionState ( BluetoothProfile . HEADSET ) ;
updateBluetoothHeadsetState ( headsetState = = BluetoothProfile . STATE_CONNECTED ) ;
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
} else {
MediaRouter . RouteInfo ri = mr . getSelectedRoute ( MediaRouter . ROUTE_TYPE_LIVE_AUDIO ) ;
if ( ri . getDeviceType ( ) = = MediaRouter . RouteInfo . DEVICE_TYPE_BLUETOOTH ) {
int headsetState = btAdapter . getProfileConnectionState ( BluetoothProfile . HEADSET ) ;
updateBluetoothHeadsetState ( headsetState = = BluetoothProfile . STATE_CONNECTED ) ;
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
} else {
updateBluetoothHeadsetState ( false ) ;
}
}
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
}
} catch ( Exception x ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " error initializing voip controller " , x ) ;
}
callFailed ( ) ;
}
if ( callIShouldHavePutIntoIntent ! = null & & Build . VERSION . SDK_INT > = Build . VERSION_CODES . O ) {
NotificationsController . checkOtherNotificationsChannel ( ) ;
Notification . Builder bldr = new Notification . Builder ( this , NotificationsController . OTHER_NOTIFICATIONS_CHANNEL )
. setContentTitle ( LocaleController . getString ( " VoipOutgoingCall " , R . string . VoipOutgoingCall ) )
. setShowWhen ( false ) ;
if ( groupCall ! = null ) {
bldr . setSmallIcon ( isMicMute ( ) ? R . drawable . voicechat_muted : R . drawable . voicechat_active ) ;
} else {
bldr . setSmallIcon ( R . drawable . notification ) ;
}
startForeground ( ID_ONGOING_CALL_NOTIFICATION , bldr . build ( ) ) ;
}
}
private void loadResources ( ) {
if ( Build . VERSION . SDK_INT > = 21 ) {
WebRtcAudioTrack . setAudioTrackUsageAttribute ( AudioAttributes . USAGE_VOICE_COMMUNICATION ) ;
}
Utilities . globalQueue . postRunnable ( ( ) - > {
soundPool = new SoundPool ( 1 , AudioManager . STREAM_VOICE_CALL , 0 ) ;
spConnectingId = soundPool . load ( this , R . raw . voip_connecting , 1 ) ;
spRingbackID = soundPool . load ( this , R . raw . voip_ringback , 1 ) ;
spFailedID = soundPool . load ( this , R . raw . voip_failed , 1 ) ;
spEndId = soundPool . load ( this , R . raw . voip_end , 1 ) ;
spBusyId = soundPool . load ( this , R . raw . voip_busy , 1 ) ;
spVoiceChatEndId = soundPool . load ( this , R . raw . voicechat_leave , 1 ) ;
spVoiceChatStartId = soundPool . load ( this , R . raw . voicechat_join , 1 ) ;
spVoiceChatConnecting = soundPool . load ( this , R . raw . voicechat_connecting , 1 ) ;
spAllowTalkId = soundPool . load ( this , R . raw . voip_onallowtalk , 1 ) ;
spStartRecordId = soundPool . load ( this , R . raw . voip_recordstart , 1 ) ;
} ) ;
}
private void dispatchStateChanged ( int state ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " == Call " + getCallID ( ) + " state changed to " + state + " == " ) ;
}
currentState = state ;
if ( USE_CONNECTION_SERVICE & & state = = STATE_ESTABLISHED /*&& !wasEstablished*/ & & systemCallConnection ! = null ) {
systemCallConnection . setActive ( ) ;
}
for ( int a = 0 ; a < stateListeners . size ( ) ; a + + ) {
StateListener l = stateListeners . get ( a ) ;
l . onStateChanged ( state ) ;
}
}
private void updateTrafficStats ( NativeInstance instance , Instance . TrafficStats trafficStats ) {
if ( trafficStats = = null ) {
trafficStats = instance . getTrafficStats ( ) ;
}
final long wifiSentDiff = trafficStats . bytesSentWifi - ( prevTrafficStats ! = null ? prevTrafficStats . bytesSentWifi : 0 ) ;
final long wifiRecvdDiff = trafficStats . bytesReceivedWifi - ( prevTrafficStats ! = null ? prevTrafficStats . bytesReceivedWifi : 0 ) ;
final long mobileSentDiff = trafficStats . bytesSentMobile - ( prevTrafficStats ! = null ? prevTrafficStats . bytesSentMobile : 0 ) ;
final long mobileRecvdDiff = trafficStats . bytesReceivedMobile - ( prevTrafficStats ! = null ? prevTrafficStats . bytesReceivedMobile : 0 ) ;
prevTrafficStats = trafficStats ;
if ( wifiSentDiff > 0 ) {
StatsController . getInstance ( currentAccount ) . incrementSentBytesCount ( StatsController . TYPE_WIFI , StatsController . TYPE_CALLS , wifiSentDiff ) ;
}
if ( wifiRecvdDiff > 0 ) {
StatsController . getInstance ( currentAccount ) . incrementReceivedBytesCount ( StatsController . TYPE_WIFI , StatsController . TYPE_CALLS , wifiRecvdDiff ) ;
}
if ( mobileSentDiff > 0 ) {
StatsController . getInstance ( currentAccount ) . incrementSentBytesCount ( lastNetInfo ! = null & & lastNetInfo . isRoaming ( ) ? StatsController . TYPE_ROAMING : StatsController . TYPE_MOBILE , StatsController . TYPE_CALLS , mobileSentDiff ) ;
}
if ( mobileRecvdDiff > 0 ) {
StatsController . getInstance ( currentAccount ) . incrementReceivedBytesCount ( lastNetInfo ! = null & & lastNetInfo . isRoaming ( ) ? StatsController . TYPE_ROAMING : StatsController . TYPE_MOBILE , StatsController . TYPE_CALLS , mobileRecvdDiff ) ;
}
}
@SuppressLint ( " InvalidWakeLockTag " )
private void configureDeviceForCall ( ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " configureDeviceForCall, route to set = " + audioRouteToSet ) ;
}
needPlayEndSound = true ;
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( ! USE_CONNECTION_SERVICE ) {
Utilities . globalQueue . postRunnable ( ( ) - > {
try {
am . setMode ( AudioManager . MODE_IN_COMMUNICATION ) ;
} catch ( Exception e ) {
FileLog . e ( e ) ;
}
AndroidUtilities . runOnUIThread ( ( ) - > {
am . requestAudioFocus ( VoIPService . this , AudioManager . STREAM_VOICE_CALL , AudioManager . AUDIOFOCUS_GAIN ) ;
if ( isBluetoothHeadsetConnected ( ) & & hasEarpiece ( ) ) {
switch ( audioRouteToSet ) {
case AUDIO_ROUTE_BLUETOOTH :
if ( ! bluetoothScoActive ) {
needSwitchToBluetoothAfterScoActivates = true ;
try {
am . startBluetoothSco ( ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
} else {
am . setBluetoothScoOn ( true ) ;
am . setSpeakerphoneOn ( false ) ;
}
break ;
case AUDIO_ROUTE_EARPIECE :
am . setBluetoothScoOn ( false ) ;
am . setSpeakerphoneOn ( false ) ;
break ;
case AUDIO_ROUTE_SPEAKER :
am . setBluetoothScoOn ( false ) ;
am . setSpeakerphoneOn ( true ) ;
break ;
}
} else if ( isBluetoothHeadsetConnected ( ) ) {
am . setBluetoothScoOn ( speakerphoneStateToSet ) ;
} else {
am . setSpeakerphoneOn ( speakerphoneStateToSet ) ;
2021-07-15 16:24:57 +02:00
if ( speakerphoneStateToSet ) {
audioRouteToSet = AUDIO_ROUTE_SPEAKER ;
} else {
audioRouteToSet = AUDIO_ROUTE_EARPIECE ;
}
2021-06-25 02:43:10 +02:00
}
updateOutputGainControlState ( ) ;
audioConfigured = true ;
} ) ;
} ) ;
}
SensorManager sm = ( SensorManager ) getSystemService ( SENSOR_SERVICE ) ;
Sensor proximity = sm . getDefaultSensor ( Sensor . TYPE_PROXIMITY ) ;
try {
if ( proximity ! = null ) {
proximityWakelock = ( ( PowerManager ) getSystemService ( Context . POWER_SERVICE ) ) . newWakeLock ( PROXIMITY_SCREEN_OFF_WAKE_LOCK , " telegram-voip-prx " ) ;
sm . registerListener ( this , proximity , SensorManager . SENSOR_DELAY_NORMAL ) ;
}
} catch ( Exception x ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error initializing proximity sensor " , x ) ;
}
}
}
private void fetchBluetoothDeviceName ( ) {
if ( fetchingBluetoothDeviceName ) {
return ;
}
try {
currentBluetoothDeviceName = null ;
fetchingBluetoothDeviceName = true ;
BluetoothAdapter . getDefaultAdapter ( ) . getProfileProxy ( this , serviceListener , BluetoothProfile . HEADSET ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
}
@SuppressLint ( " NewApi " )
@Override
public void onSensorChanged ( SensorEvent event ) {
if ( unmutedByHold | | remoteVideoState = = Instance . VIDEO_STATE_ACTIVE | | videoState [ CAPTURE_DEVICE_CAMERA ] = = Instance . VIDEO_STATE_ACTIVE ) {
return ;
}
if ( event . sensor . getType ( ) = = Sensor . TYPE_PROXIMITY ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
2021-07-15 16:24:57 +02:00
if ( audioRouteToSet ! = AUDIO_ROUTE_EARPIECE | | isHeadsetPlugged | | am . isSpeakerphoneOn ( ) | | ( isBluetoothHeadsetConnected ( ) & & am . isBluetoothScoOn ( ) ) ) {
2021-06-25 02:43:10 +02:00
return ;
}
boolean newIsNear = event . values [ 0 ] < Math . min ( event . sensor . getMaximumRange ( ) , 3 ) ;
checkIsNear ( newIsNear ) ;
}
}
private void checkIsNear ( ) {
if ( remoteVideoState = = Instance . VIDEO_STATE_ACTIVE | | videoState [ CAPTURE_DEVICE_CAMERA ] = = Instance . VIDEO_STATE_ACTIVE ) {
checkIsNear ( false ) ;
}
}
private void checkIsNear ( boolean newIsNear ) {
if ( newIsNear ! = isProximityNear ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " proximity " + newIsNear ) ;
}
isProximityNear = newIsNear ;
try {
if ( isProximityNear ) {
proximityWakelock . acquire ( ) ;
} else {
proximityWakelock . release ( 1 ) ; // this is non-public API before L
}
} catch ( Exception x ) {
FileLog . e ( x ) ;
}
}
}
@Override
public void onAccuracyChanged ( Sensor sensor , int accuracy ) {
}
public boolean isBluetoothHeadsetConnected ( ) {
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
return ( systemCallConnection . getCallAudioState ( ) . getSupportedRouteMask ( ) & CallAudioState . ROUTE_BLUETOOTH ) ! = 0 ;
}
return isBtHeadsetConnected ;
}
public void onAudioFocusChange ( int focusChange ) {
if ( focusChange = = AudioManager . AUDIOFOCUS_GAIN ) {
hasAudioFocus = true ;
} else {
hasAudioFocus = false ;
}
}
private void updateBluetoothHeadsetState ( boolean connected ) {
if ( connected = = isBtHeadsetConnected ) {
return ;
}
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " updateBluetoothHeadsetState: " + connected ) ;
}
isBtHeadsetConnected = connected ;
final AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( connected & & ! isRinging ( ) & & currentState ! = 0 ) {
if ( bluetoothScoActive ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " SCO already active, setting audio routing " ) ;
}
am . setSpeakerphoneOn ( false ) ;
am . setBluetoothScoOn ( true ) ;
} else {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " startBluetoothSco " ) ;
}
needSwitchToBluetoothAfterScoActivates = true ;
AndroidUtilities . runOnUIThread ( ( ) - > {
try {
am . startBluetoothSco ( ) ;
} catch ( Throwable ignore ) {
}
} , 500 ) ;
}
} else {
bluetoothScoActive = false ;
bluetoothScoConnecting = false ;
}
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
}
public String getLastError ( ) {
return lastError ;
}
public int getCallState ( ) {
return currentState ;
}
public TLRPC . InputPeer getGroupCallPeer ( ) {
return groupCallPeer ;
}
private void updateNetworkType ( ) {
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ) {
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] . isGroup ( ) ) {
} else {
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setNetworkType ( getNetworkType ( ) ) ;
}
} else {
lastNetInfo = getActiveNetworkInfo ( ) ;
}
}
private int getNetworkType ( ) {
final NetworkInfo info = lastNetInfo = getActiveNetworkInfo ( ) ;
int type = Instance . NET_TYPE_UNKNOWN ;
if ( info ! = null ) {
switch ( info . getType ( ) ) {
case ConnectivityManager . TYPE_MOBILE :
switch ( info . getSubtype ( ) ) {
case TelephonyManager . NETWORK_TYPE_GPRS :
type = Instance . NET_TYPE_GPRS ;
break ;
case TelephonyManager . NETWORK_TYPE_EDGE :
case TelephonyManager . NETWORK_TYPE_1xRTT :
type = Instance . NET_TYPE_EDGE ;
break ;
case TelephonyManager . NETWORK_TYPE_UMTS :
case TelephonyManager . NETWORK_TYPE_EVDO_0 :
type = Instance . NET_TYPE_3G ;
break ;
case TelephonyManager . NETWORK_TYPE_HSDPA :
case TelephonyManager . NETWORK_TYPE_HSPA :
case TelephonyManager . NETWORK_TYPE_HSPAP :
case TelephonyManager . NETWORK_TYPE_HSUPA :
case TelephonyManager . NETWORK_TYPE_EVDO_A :
case TelephonyManager . NETWORK_TYPE_EVDO_B :
type = Instance . NET_TYPE_HSPA ;
break ;
case TelephonyManager . NETWORK_TYPE_LTE :
type = Instance . NET_TYPE_LTE ;
break ;
default :
type = Instance . NET_TYPE_OTHER_MOBILE ;
break ;
}
break ;
case ConnectivityManager . TYPE_WIFI :
type = Instance . NET_TYPE_WIFI ;
break ;
case ConnectivityManager . TYPE_ETHERNET :
type = Instance . NET_TYPE_ETHERNET ;
break ;
}
}
return type ;
}
private NetworkInfo getActiveNetworkInfo ( ) {
return ( ( ConnectivityManager ) getSystemService ( CONNECTIVITY_SERVICE ) ) . getActiveNetworkInfo ( ) ;
}
private void callFailed ( ) {
callFailed ( tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ? tgVoip [ CAPTURE_DEVICE_CAMERA ] . getLastError ( ) : Instance . ERROR_UNKNOWN ) ;
}
private Bitmap getRoundAvatarBitmap ( TLObject userOrChat ) {
Bitmap bitmap = null ;
try {
if ( userOrChat instanceof TLRPC . User ) {
TLRPC . User user = ( TLRPC . User ) userOrChat ;
if ( user . photo ! = null & & user . photo . photo_small ! = null ) {
BitmapDrawable img = ImageLoader . getInstance ( ) . getImageFromMemory ( user . photo . photo_small , null , " 50_50 " ) ;
if ( img ! = null ) {
bitmap = img . getBitmap ( ) . copy ( Bitmap . Config . ARGB_8888 , true ) ;
} else {
try {
BitmapFactory . Options opts = new BitmapFactory . Options ( ) ;
opts . inMutable = true ;
bitmap = BitmapFactory . decodeFile ( FileLoader . getPathToAttach ( user . photo . photo_small , true ) . toString ( ) , opts ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
}
}
} else {
TLRPC . Chat chat = ( TLRPC . Chat ) userOrChat ;
if ( chat . photo ! = null & & chat . photo . photo_small ! = null ) {
BitmapDrawable img = ImageLoader . getInstance ( ) . getImageFromMemory ( chat . photo . photo_small , null , " 50_50 " ) ;
if ( img ! = null ) {
bitmap = img . getBitmap ( ) . copy ( Bitmap . Config . ARGB_8888 , true ) ;
} else {
try {
BitmapFactory . Options opts = new BitmapFactory . Options ( ) ;
opts . inMutable = true ;
bitmap = BitmapFactory . decodeFile ( FileLoader . getPathToAttach ( chat . photo . photo_small , true ) . toString ( ) , opts ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
}
}
}
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
if ( bitmap = = null ) {
Theme . createDialogsResources ( this ) ;
AvatarDrawable placeholder ;
if ( userOrChat instanceof TLRPC . User ) {
placeholder = new AvatarDrawable ( ( TLRPC . User ) userOrChat ) ;
} else {
placeholder = new AvatarDrawable ( ( TLRPC . Chat ) userOrChat ) ;
}
bitmap = Bitmap . createBitmap ( AndroidUtilities . dp ( 42 ) , AndroidUtilities . dp ( 42 ) , Bitmap . Config . ARGB_8888 ) ;
placeholder . setBounds ( 0 , 0 , bitmap . getWidth ( ) , bitmap . getHeight ( ) ) ;
placeholder . draw ( new Canvas ( bitmap ) ) ;
}
Canvas canvas = new Canvas ( bitmap ) ;
Path circlePath = new Path ( ) ;
circlePath . addCircle ( bitmap . getWidth ( ) / 2 , bitmap . getHeight ( ) / 2 , bitmap . getWidth ( ) / 2 , Path . Direction . CW ) ;
circlePath . toggleInverseFillType ( ) ;
Paint paint = new Paint ( Paint . ANTI_ALIAS_FLAG ) ;
paint . setXfermode ( new PorterDuffXfermode ( PorterDuff . Mode . CLEAR ) ) ;
canvas . drawPath ( circlePath , paint ) ;
return bitmap ;
}
private void showIncomingNotification ( String name , CharSequence subText , TLObject userOrChat , boolean video , int additionalMemberCount ) {
Intent intent = new Intent ( this , LaunchActivity . class ) ;
intent . setAction ( " voip " ) ;
Notification . Builder builder = new Notification . Builder ( this )
. setContentTitle ( video ? LocaleController . getString ( " VoipInVideoCallBranding " , R . string . VoipInVideoCallBranding ) : LocaleController . getString ( " VoipInCallBranding " , R . string . VoipInCallBranding ) )
. setContentText ( name )
. setSmallIcon ( R . drawable . notification )
. setSubText ( subText )
. setContentIntent ( PendingIntent . getActivity ( this , 0 , intent , 0 ) ) ;
Uri soundProviderUri = Uri . parse ( " content:// " + BuildConfig . APPLICATION_ID + " .call_sound_provider/start_ringing " ) ;
if ( Build . VERSION . SDK_INT > = 26 ) {
SharedPreferences nprefs = MessagesController . getGlobalNotificationsSettings ( ) ;
int chanIndex = nprefs . getInt ( " calls_notification_channel " , 0 ) ;
NotificationManager nm = ( NotificationManager ) getSystemService ( NOTIFICATION_SERVICE ) ;
NotificationChannel oldChannel = nm . getNotificationChannel ( " incoming_calls2 " + chanIndex ) ;
if ( oldChannel ! = null ) {
nm . deleteNotificationChannel ( oldChannel . getId ( ) ) ;
}
NotificationChannel existingChannel = nm . getNotificationChannel ( " incoming_calls3 " + chanIndex ) ;
boolean needCreate = true ;
if ( existingChannel ! = null ) {
if ( existingChannel . getImportance ( ) < NotificationManager . IMPORTANCE_HIGH | | ! soundProviderUri . equals ( existingChannel . getSound ( ) ) | | existingChannel . getVibrationPattern ( ) ! = null | | existingChannel . shouldVibrate ( ) ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " User messed up the notification channel; deleting it and creating a proper one " ) ;
}
nm . deleteNotificationChannel ( " incoming_calls3 " + chanIndex ) ;
chanIndex + + ;
nprefs . edit ( ) . putInt ( " calls_notification_channel " , chanIndex ) . commit ( ) ;
} else {
needCreate = false ;
}
}
if ( needCreate ) {
AudioAttributes attrs = new AudioAttributes . Builder ( )
. setContentType ( AudioAttributes . CONTENT_TYPE_SONIFICATION )
. setLegacyStreamType ( AudioManager . STREAM_RING )
. setUsage ( AudioAttributes . USAGE_VOICE_COMMUNICATION )
. build ( ) ;
NotificationChannel chan = new NotificationChannel ( " incoming_calls3 " + chanIndex , LocaleController . getString ( " IncomingCalls " , R . string . IncomingCalls ) , NotificationManager . IMPORTANCE_HIGH ) ;
chan . setSound ( soundProviderUri , attrs ) ;
chan . enableVibration ( false ) ;
chan . enableLights ( false ) ;
chan . setBypassDnd ( true ) ;
try {
nm . createNotificationChannel ( chan ) ;
} catch ( Exception e ) {
FileLog . e ( e ) ;
this . stopSelf ( ) ;
return ;
}
}
builder . setChannelId ( " incoming_calls3 " + chanIndex ) ;
} else if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . LOLLIPOP ) {
builder . setSound ( soundProviderUri , AudioManager . STREAM_RING ) ;
}
Intent endIntent = new Intent ( this , VoIPActionsReceiver . class ) ;
endIntent . setAction ( getPackageName ( ) + " .DECLINE_CALL " ) ;
endIntent . putExtra ( " call_id " , getCallID ( ) ) ;
CharSequence endTitle = LocaleController . getString ( " VoipDeclineCall " , R . string . VoipDeclineCall ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . N ) {
endTitle = new SpannableString ( endTitle ) ;
( ( SpannableString ) endTitle ) . setSpan ( new ForegroundColorSpan ( 0xFFF44336 ) , 0 , endTitle . length ( ) , 0 ) ;
}
PendingIntent endPendingIntent = PendingIntent . getBroadcast ( this , 0 , endIntent , PendingIntent . FLAG_CANCEL_CURRENT ) ;
builder . addAction ( R . drawable . ic_call_end_white_24dp , endTitle , endPendingIntent ) ;
Intent answerIntent = new Intent ( this , VoIPActionsReceiver . class ) ;
answerIntent . setAction ( getPackageName ( ) + " .ANSWER_CALL " ) ;
answerIntent . putExtra ( " call_id " , getCallID ( ) ) ;
CharSequence answerTitle = LocaleController . getString ( " VoipAnswerCall " , R . string . VoipAnswerCall ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . N ) {
answerTitle = new SpannableString ( answerTitle ) ;
( ( SpannableString ) answerTitle ) . setSpan ( new ForegroundColorSpan ( 0xFF00AA00 ) , 0 , answerTitle . length ( ) , 0 ) ;
}
PendingIntent answerPendingIntent = PendingIntent . getBroadcast ( this , 0 , answerIntent , PendingIntent . FLAG_CANCEL_CURRENT ) ;
builder . addAction ( R . drawable . ic_call , answerTitle , answerPendingIntent ) ;
builder . setPriority ( Notification . PRIORITY_MAX ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN_MR1 ) {
builder . setShowWhen ( false ) ;
}
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . LOLLIPOP ) {
builder . setColor ( 0xff2ca5e0 ) ;
builder . setVibrate ( new long [ 0 ] ) ;
builder . setCategory ( Notification . CATEGORY_CALL ) ;
builder . setFullScreenIntent ( PendingIntent . getActivity ( this , 0 , intent , 0 ) , true ) ;
if ( userOrChat instanceof TLRPC . User ) {
TLRPC . User user = ( TLRPC . User ) userOrChat ;
if ( ! TextUtils . isEmpty ( user . phone ) ) {
builder . addPerson ( " tel: " + user . phone ) ;
}
}
}
Notification incomingNotification = builder . getNotification ( ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . LOLLIPOP ) {
RemoteViews customView = new RemoteViews ( getPackageName ( ) , LocaleController . isRTL ? R . layout . call_notification_rtl : R . layout . call_notification ) ;
customView . setTextViewText ( R . id . name , name ) ;
boolean subtitleVisible = true ;
if ( TextUtils . isEmpty ( subText ) ) {
customView . setViewVisibility ( R . id . subtitle , View . GONE ) ;
if ( UserConfig . getActivatedAccountsCount ( ) > 1 ) {
TLRPC . User self = UserConfig . getInstance ( currentAccount ) . getCurrentUser ( ) ;
customView . setTextViewText ( R . id . title , video ? LocaleController . formatString ( " VoipInVideoCallBrandingWithName " , R . string . VoipInVideoCallBrandingWithName , ContactsController . formatName ( self . first_name , self . last_name ) ) : LocaleController . formatString ( " VoipInCallBrandingWithName " , R . string . VoipInCallBrandingWithName , ContactsController . formatName ( self . first_name , self . last_name ) ) ) ;
} else {
customView . setTextViewText ( R . id . title , video ? LocaleController . getString ( " VoipInVideoCallBranding " , R . string . VoipInVideoCallBranding ) : LocaleController . getString ( " VoipInCallBranding " , R . string . VoipInCallBranding ) ) ;
}
} else {
if ( UserConfig . getActivatedAccountsCount ( ) > 1 ) {
TLRPC . User self = UserConfig . getInstance ( currentAccount ) . getCurrentUser ( ) ;
customView . setTextViewText ( R . id . subtitle , LocaleController . formatString ( " VoipAnsweringAsAccount " , R . string . VoipAnsweringAsAccount , ContactsController . formatName ( self . first_name , self . last_name ) ) ) ;
} else {
customView . setViewVisibility ( R . id . subtitle , View . GONE ) ;
}
customView . setTextViewText ( R . id . title , subText ) ;
}
Bitmap avatar = getRoundAvatarBitmap ( userOrChat ) ;
customView . setTextViewText ( R . id . answer_text , LocaleController . getString ( " VoipAnswerCall " , R . string . VoipAnswerCall ) ) ;
customView . setTextViewText ( R . id . decline_text , LocaleController . getString ( " VoipDeclineCall " , R . string . VoipDeclineCall ) ) ;
customView . setImageViewBitmap ( R . id . photo , avatar ) ;
customView . setOnClickPendingIntent ( R . id . answer_btn , answerPendingIntent ) ;
customView . setOnClickPendingIntent ( R . id . decline_btn , endPendingIntent ) ;
builder . setLargeIcon ( avatar ) ;
incomingNotification . headsUpContentView = incomingNotification . bigContentView = customView ;
}
startForeground ( ID_INCOMING_CALL_NOTIFICATION , incomingNotification ) ;
startRingtoneAndVibration ( ) ;
}
private void callFailed ( String error ) {
if ( privateCall ! = null ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " Discarding failed call " ) ;
}
TLRPC . TL_phone_discardCall req = new TLRPC . TL_phone_discardCall ( ) ;
req . peer = new TLRPC . TL_inputPhoneCall ( ) ;
req . peer . access_hash = privateCall . access_hash ;
req . peer . id = privateCall . id ;
req . duration = ( int ) ( getCallDuration ( ) / 1000 ) ;
req . connection_id = tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ? tgVoip [ CAPTURE_DEVICE_CAMERA ] . getPreferredRelayId ( ) : 0 ;
req . reason = new TLRPC . TL_phoneCallDiscardReasonDisconnect ( ) ;
ConnectionsManager . getInstance ( currentAccount ) . sendRequest ( req , ( response , error1 ) - > {
if ( error1 ! = null ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " error on phone.discardCall: " + error1 ) ;
}
} else {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " phone.discardCall " + response ) ;
}
}
} ) ;
}
try {
throw new Exception ( " Call " + getCallID ( ) + " failed with error: " + error ) ;
} catch ( Exception x ) {
FileLog . e ( x ) ;
}
lastError = error ;
AndroidUtilities . runOnUIThread ( ( ) - > dispatchStateChanged ( STATE_FAILED ) ) ;
if ( TextUtils . equals ( error , Instance . ERROR_LOCALIZED ) & & soundPool ! = null ) {
playingSound = true ;
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spFailedID , 1 , 1 , 0 , 0 , 1 ) ) ;
AndroidUtilities . runOnUIThread ( afterSoundRunnable , 1000 ) ;
}
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null ) {
systemCallConnection . setDisconnected ( new DisconnectCause ( DisconnectCause . ERROR ) ) ;
systemCallConnection . destroy ( ) ;
systemCallConnection = null ;
}
stopSelf ( ) ;
}
void callFailedFromConnectionService ( ) {
if ( isOutgoing ) {
callFailed ( Instance . ERROR_CONNECTION_SERVICE ) ;
} else {
hangUp ( ) ;
}
}
@Override
public void onConnectionStateChanged ( int newState , boolean inTransition ) {
AndroidUtilities . runOnUIThread ( ( ) - > {
if ( newState = = STATE_ESTABLISHED ) {
if ( callStartTime = = 0 ) {
callStartTime = SystemClock . elapsedRealtime ( ) ;
}
//peerCapabilities = tgVoip.getPeerCapabilities();
}
if ( newState = = STATE_FAILED ) {
callFailed ( ) ;
return ;
}
if ( newState = = STATE_ESTABLISHED ) {
if ( connectingSoundRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( connectingSoundRunnable ) ;
connectingSoundRunnable = null ;
}
Utilities . globalQueue . postRunnable ( ( ) - > {
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
spPlayId = 0 ;
}
} ) ;
if ( groupCall = = null & & ! wasEstablished ) {
wasEstablished = true ;
if ( ! isProximityNear & & ! privateCall . video ) {
Vibrator vibrator = ( Vibrator ) getSystemService ( VIBRATOR_SERVICE ) ;
if ( vibrator . hasVibrator ( ) ) {
vibrator . vibrate ( 100 ) ;
}
}
AndroidUtilities . runOnUIThread ( new Runnable ( ) {
@Override
public void run ( ) {
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ) {
StatsController . getInstance ( currentAccount ) . incrementTotalCallsTime ( getStatsNetworkType ( ) , 5 ) ;
AndroidUtilities . runOnUIThread ( this , 5000 ) ;
}
}
} , 5000 ) ;
if ( isOutgoing ) {
StatsController . getInstance ( currentAccount ) . incrementSentItemsCount ( getStatsNetworkType ( ) , StatsController . TYPE_CALLS , 1 ) ;
} else {
StatsController . getInstance ( currentAccount ) . incrementReceivedItemsCount ( getStatsNetworkType ( ) , StatsController . TYPE_CALLS , 1 ) ;
}
}
}
if ( newState = = STATE_RECONNECTING ) {
Utilities . globalQueue . postRunnable ( ( ) - > {
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
}
spPlayId = soundPool . play ( groupCall ! = null ? spVoiceChatConnecting : spConnectingId , 1 , 1 , 0 , - 1 , 1 ) ;
} ) ;
}
dispatchStateChanged ( newState ) ;
} ) ;
}
public void playStartRecordSound ( ) {
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spStartRecordId , 0 . 5f , 0 . 5f , 0 , 0 , 1 ) ) ;
}
public void playAllowTalkSound ( ) {
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spAllowTalkId , 0 . 5f , 0 . 5f , 0 , 0 , 1 ) ) ;
}
@Override
public void onSignalBarCountChanged ( int newCount ) {
AndroidUtilities . runOnUIThread ( ( ) - > {
signalBarCount = newCount ;
for ( int a = 0 ; a < stateListeners . size ( ) ; a + + ) {
StateListener l = stateListeners . get ( a ) ;
l . onSignalBarsCountChanged ( newCount ) ;
}
} ) ;
}
public boolean isBluetoothOn ( ) {
final AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
return am . isBluetoothScoOn ( ) ;
}
public boolean isBluetoothWillOn ( ) {
return needSwitchToBluetoothAfterScoActivates ;
}
public boolean isHeadsetPlugged ( ) {
return isHeadsetPlugged ;
}
private void callEnded ( ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " Call " + getCallID ( ) + " ended " ) ;
}
if ( groupCall ! = null & & ( ! playedConnectedSound | | onDestroyRunnable ! = null ) ) {
needPlayEndSound = false ;
}
AndroidUtilities . runOnUIThread ( ( ) - > dispatchStateChanged ( STATE_ENDED ) ) ;
int delay = 700 ;
Utilities . globalQueue . postRunnable ( ( ) - > {
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
spPlayId = 0 ;
}
} ) ;
if ( connectingSoundRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( connectingSoundRunnable ) ;
connectingSoundRunnable = null ;
}
if ( needPlayEndSound ) {
playingSound = true ;
if ( groupCall = = null ) {
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spEndId , 1 , 1 , 0 , 0 , 1 ) ) ;
} else {
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spVoiceChatEndId , 1 . 0f , 1 . 0f , 0 , 0 , 1 ) , 100 ) ;
delay = 500 ;
}
AndroidUtilities . runOnUIThread ( afterSoundRunnable , delay ) ;
}
if ( timeoutRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( timeoutRunnable ) ;
timeoutRunnable = null ;
}
endConnectionServiceCall ( needPlayEndSound ? delay : 0 ) ;
stopSelf ( ) ;
}
private void endConnectionServiceCall ( long delay ) {
if ( USE_CONNECTION_SERVICE ) {
Runnable r = ( ) - > {
if ( systemCallConnection ! = null ) {
switch ( callDiscardReason ) {
case DISCARD_REASON_HANGUP :
systemCallConnection . setDisconnected ( new DisconnectCause ( isOutgoing ? DisconnectCause . LOCAL : DisconnectCause . REJECTED ) ) ;
break ;
case DISCARD_REASON_DISCONNECT :
systemCallConnection . setDisconnected ( new DisconnectCause ( DisconnectCause . ERROR ) ) ;
break ;
case DISCARD_REASON_LINE_BUSY :
systemCallConnection . setDisconnected ( new DisconnectCause ( DisconnectCause . BUSY ) ) ;
break ;
case DISCARD_REASON_MISSED :
systemCallConnection . setDisconnected ( new DisconnectCause ( isOutgoing ? DisconnectCause . CANCELED : DisconnectCause . MISSED ) ) ;
break ;
default :
systemCallConnection . setDisconnected ( new DisconnectCause ( DisconnectCause . REMOTE ) ) ;
break ;
}
systemCallConnection . destroy ( ) ;
systemCallConnection = null ;
}
} ;
if ( delay > 0 ) {
AndroidUtilities . runOnUIThread ( r , delay ) ;
} else {
r . run ( ) ;
}
}
}
public boolean isOutgoing ( ) {
return isOutgoing ;
}
public void handleNotificationAction ( Intent intent ) {
if ( ( getPackageName ( ) + " .END_CALL " ) . equals ( intent . getAction ( ) ) ) {
stopForeground ( true ) ;
hangUp ( ) ;
} else if ( ( getPackageName ( ) + " .DECLINE_CALL " ) . equals ( intent . getAction ( ) ) ) {
stopForeground ( true ) ;
declineIncomingCall ( DISCARD_REASON_LINE_BUSY , null ) ;
} else if ( ( getPackageName ( ) + " .ANSWER_CALL " ) . equals ( intent . getAction ( ) ) ) {
acceptIncomingCallFromNotification ( ) ;
}
}
private void acceptIncomingCallFromNotification ( ) {
showNotification ( ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . M & & Build . VERSION . SDK_INT < Build . VERSION_CODES . R & & ( checkSelfPermission ( Manifest . permission . RECORD_AUDIO ) ! = PackageManager . PERMISSION_GRANTED | | privateCall . video & & checkSelfPermission ( Manifest . permission . CAMERA ) ! = PackageManager . PERMISSION_GRANTED ) ) {
try {
//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
PendingIntent . getActivity ( VoIPService . this , 0 , new Intent ( VoIPService . this , VoIPPermissionActivity . class ) . addFlags ( Intent . FLAG_ACTIVITY_NEW_TASK ) , PendingIntent . FLAG_ONE_SHOT ) . send ( ) ;
} catch ( Exception x ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error starting permission activity " , x ) ;
}
}
return ;
}
acceptIncomingCall ( ) ;
try {
PendingIntent . getActivity ( VoIPService . this , 0 , new Intent ( VoIPService . this , getUIActivityClass ( ) ) . setAction ( " voip " ) , 0 ) . send ( ) ;
} catch ( Exception x ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error starting incall activity " , x ) ;
}
}
}
public void updateOutputGainControlState ( ) {
if ( tgVoip [ CAPTURE_DEVICE_CAMERA ] ! = null ) {
if ( ! USE_CONNECTION_SERVICE ) {
final AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setAudioOutputGainControlEnabled ( hasEarpiece ( ) & & ! am . isSpeakerphoneOn ( ) & & ! am . isBluetoothScoOn ( ) & & ! isHeadsetPlugged ) ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setEchoCancellationStrength ( isHeadsetPlugged | | ( hasEarpiece ( ) & & ! am . isSpeakerphoneOn ( ) & & ! am . isBluetoothScoOn ( ) & & ! isHeadsetPlugged ) ? 0 : 1 ) ;
} else {
final boolean isEarpiece = systemCallConnection . getCallAudioState ( ) . getRoute ( ) = = CallAudioState . ROUTE_EARPIECE ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setAudioOutputGainControlEnabled ( isEarpiece ) ;
tgVoip [ CAPTURE_DEVICE_CAMERA ] . setEchoCancellationStrength ( isEarpiece ? 0 : 1 ) ;
}
}
}
public int getAccount ( ) {
return currentAccount ;
}
@Override
public void didReceivedNotification ( int id , int account , Object . . . args ) {
if ( id = = NotificationCenter . appDidLogout ) {
callEnded ( ) ;
}
}
public static boolean isAnyKindOfCallActive ( ) {
if ( VoIPService . getSharedInstance ( ) ! = null ) {
return VoIPService . getSharedInstance ( ) . getCallState ( ) ! = VoIPService . STATE_WAITING_INCOMING ;
}
return false ;
}
private boolean isFinished ( ) {
return currentState = = STATE_ENDED | | currentState = = STATE_FAILED ;
}
public int getRemoteAudioState ( ) {
return remoteAudioState ;
}
public int getRemoteVideoState ( ) {
return remoteVideoState ;
}
@TargetApi ( Build . VERSION_CODES . O )
private PhoneAccountHandle addAccountToTelecomManager ( ) {
TelecomManager tm = ( TelecomManager ) getSystemService ( TELECOM_SERVICE ) ;
TLRPC . User self = UserConfig . getInstance ( currentAccount ) . getCurrentUser ( ) ;
PhoneAccountHandle handle = new PhoneAccountHandle ( new ComponentName ( this , TelegramConnectionService . class ) , " " + self . id ) ;
PhoneAccount account = new PhoneAccount . Builder ( handle , ContactsController . formatName ( self . first_name , self . last_name ) )
. setCapabilities ( PhoneAccount . CAPABILITY_SELF_MANAGED )
. setIcon ( Icon . createWithResource ( this , R . drawable . ic_launcher_dr ) )
. setHighlightColor ( 0xff2ca5e0 )
. addSupportedUriScheme ( " sip " )
. build ( ) ;
tm . registerPhoneAccount ( account ) ;
return handle ;
}
private static boolean isDeviceCompatibleWithConnectionServiceAPI ( ) {
if ( Build . VERSION . SDK_INT < Build . VERSION_CODES . O ) {
return false ;
}
// some non-Google devices don't implement the ConnectionService API correctly so, sadly,
// we'll have to whitelist only a handful of known-compatible devices for now
return false ; / * " angler " . equals ( Build . PRODUCT ) // Nexus 6P
| | " bullhead " . equals ( Build . PRODUCT ) // Nexus 5X
| | " sailfish " . equals ( Build . PRODUCT ) // Pixel
| | " marlin " . equals ( Build . PRODUCT ) // Pixel XL
| | " walleye " . equals ( Build . PRODUCT ) // Pixel 2
| | " taimen " . equals ( Build . PRODUCT ) // Pixel 2 XL
| | " blueline " . equals ( Build . PRODUCT ) // Pixel 3
| | " crosshatch " . equals ( Build . PRODUCT ) // Pixel 3 XL
| | MessagesController . getGlobalMainSettings ( ) . getBoolean ( " dbg_force_connection_service " , false ) ; * /
}
public interface StateListener {
default void onStateChanged ( int state ) {
}
default void onSignalBarsCountChanged ( int count ) {
}
default void onAudioSettingsChanged ( ) {
}
default void onMediaStateUpdated ( int audioState , int videoState ) {
}
default void onCameraSwitch ( boolean isFrontFace ) {
}
default void onVideoAvailableChange ( boolean isAvailable ) {
}
default void onScreenOnChange ( boolean screenOn ) {
}
}
public class CallConnection extends Connection {
public CallConnection ( ) {
setConnectionProperties ( PROPERTY_SELF_MANAGED ) ;
setAudioModeIsVoip ( true ) ;
}
@Override
public void onCallAudioStateChanged ( CallAudioState state ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " ConnectionService call audio state changed: " + state ) ;
}
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
}
@Override
public void onDisconnect ( ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " ConnectionService onDisconnect " ) ;
}
setDisconnected ( new DisconnectCause ( DisconnectCause . LOCAL ) ) ;
destroy ( ) ;
systemCallConnection = null ;
hangUp ( ) ;
}
@Override
public void onAnswer ( ) {
acceptIncomingCallFromNotification ( ) ;
}
@Override
public void onReject ( ) {
needPlayEndSound = false ;
declineIncomingCall ( DISCARD_REASON_HANGUP , null ) ;
}
@Override
public void onShowIncomingCallUi ( ) {
startRinging ( ) ;
}
@Override
public void onStateChanged ( int state ) {
super . onStateChanged ( state ) ;
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " ConnectionService onStateChanged " + stateToString ( state ) ) ;
}
if ( state = = Connection . STATE_ACTIVE ) {
ContactsController . getInstance ( currentAccount ) . deleteConnectionServiceContact ( ) ;
didDeleteConnectionServiceContact = true ;
}
}
@Override
public void onCallEvent ( String event , Bundle extras ) {
super . onCallEvent ( event , extras ) ;
if ( BuildVars . LOGS_ENABLED )
FileLog . d ( " ConnectionService onCallEvent " + event ) ;
}
//undocumented API
public void onSilence ( ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " onSlience " ) ;
}
stopRinging ( ) ;
}
}
public static class SharedUIParams {
public boolean tapToVideoTooltipWasShowed ;
public boolean cameraAlertWasShowed ;
public boolean wasVideoCall ;
2018-07-30 04:07:02 +02:00
}
2017-03-31 01:58:05 +02:00
}