From eefc86f27ee1f29db646decb733b003402dcc9ab Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Fri, 12 Apr 2024 09:36:11 -0400 Subject: [PATCH] Fix dangling call notification and remove active call manager flag. --- app/src/main/AndroidManifest.xml | 6 - .../securesms/ApplicationContext.java | 4 +- .../service/webrtc/ActiveCallManager.kt | 84 ++- .../service/webrtc/AndroidCallConnection.kt | 8 +- .../service/webrtc/WebRtcCallService.java | 487 ------------------ .../service/webrtc/WebRtcInteractor.java | 31 +- .../securesms/util/FeatureFlags.java | 7 - .../webrtc/CallNotificationBuilder.java | 12 +- .../webrtc/audio/AudioManagerCommand.kt | 5 +- 9 files changed, 110 insertions(+), 534 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2fc587e570..f419d81f70 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1099,12 +1099,6 @@ android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:exported="false"/> - - ActiveCallManager.clearNotifications(this)) .execute(); Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ActiveCallManager.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ActiveCallManager.kt index 7f5301b44f..04828a7e2e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ActiveCallManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ActiveCallManager.kt @@ -6,6 +6,7 @@ package org.thoughtcrime.securesms.service.webrtc import android.app.Notification +import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -24,6 +25,7 @@ import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.kotlin.subscribeBy import io.reactivex.rxjava3.schedulers.Schedulers +import org.signal.core.util.PendingIntentFlags import org.signal.core.util.ThreadUtil import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.dependencies.ApplicationDependencies @@ -38,12 +40,13 @@ import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCommand import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.Companion.create import org.thoughtcrime.securesms.webrtc.locks.LockManager +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes /** - * Entry point for [SignalCallManager] and friends to interact with the Android system as - * previously done via [WebRtcCallService]. + * Entry point for [SignalCallManager] and friends to interact with the Android system. * * This tries to limit the use of a foreground service until a call has been fully established * and the user has likely foregrounded us by accepting a call. @@ -54,6 +57,79 @@ class ActiveCallManager( companion object { private val TAG = Log.tag(ActiveCallManager::class.java) + + private var activeCallManager: ActiveCallManager? = null + private val activeCallManagerLock = ReentrantLock() + + @JvmStatic + fun clearNotifications(context: Context) { + NotificationManagerCompat.from(context).apply { + cancel(CallNotificationBuilder.WEBRTC_NOTIFICATION) + cancel(CallNotificationBuilder.WEBRTC_NOTIFICATION_RINGING) + } + } + + @JvmStatic + fun update(context: Context, type: Int, recipientId: RecipientId, isVideoCall: Boolean) { + activeCallManagerLock.withLock { + if (activeCallManager == null) { + activeCallManager = ActiveCallManager(context) + } + activeCallManager!!.update(type, recipientId, isVideoCall) + } + } + + @JvmStatic + fun denyCall() { + ApplicationDependencies.getSignalCallManager().denyCall() + } + + @JvmStatic + fun hangup() { + ApplicationDependencies.getSignalCallManager().localHangup() + } + + @JvmStatic + fun stop() { + activeCallManagerLock.withLock { + activeCallManager?.shutdown() + activeCallManager = null + } + } + + @JvmStatic + fun denyCallIntent(context: Context): PendingIntent { + val intent = Intent(context, ActiveCallServiceReceiver::class.java) + intent.setAction(ActiveCallServiceReceiver.ACTION_DENY) + return PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable()) + } + + @JvmStatic + fun hangupIntent(context: Context): PendingIntent { + val intent = Intent(context, ActiveCallServiceReceiver::class.java) + intent.setAction(ActiveCallServiceReceiver.ACTION_HANGUP) + return PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable()) + } + + @JvmStatic + fun sendAudioManagerCommand(context: Context, command: AudioManagerCommand) { + activeCallManagerLock.withLock { + if (activeCallManager == null) { + activeCallManager = ActiveCallManager(context) + } + activeCallManager!!.sendAudioCommand(command) + } + } + + @JvmStatic + fun changePowerButtonReceiver(context: Context, register: Boolean) { + activeCallManagerLock.withLock { + if (activeCallManager == null) { + activeCallManager = ActiveCallManager(context) + } + activeCallManager!!.changePowerButton(register) + } + } } private val callManager = ApplicationDependencies.getSignalCallManager() @@ -72,8 +148,8 @@ class ActiveCallManager( webSocketKeepAliveTask.start() } - fun stop() { - Log.v(TAG, "stop") + fun shutdown() { + Log.v(TAG, "shutdown") uncaughtExceptionHandlerManager?.unregister() uncaughtExceptionHandlerManager = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AndroidCallConnection.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AndroidCallConnection.kt index ca8b34046c..e963ba94aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AndroidCallConnection.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AndroidCallConnection.kt @@ -38,7 +38,7 @@ class AndroidCallConnection( override fun onShowIncomingCallUi() { Log.i(TAG, "onShowIncomingCallUi()") - WebRtcCallService.update(context, CallNotificationBuilder.TYPE_INCOMING_CONNECTING, recipientId, isVideoCall) + ActiveCallManager.update(context, CallNotificationBuilder.TYPE_INCOMING_CONNECTING, recipientId, isVideoCall) setRinging() } @@ -74,17 +74,17 @@ class AndroidCallConnection( } override fun onSilence() { - WebRtcCallService.sendAudioManagerCommand(context, AudioManagerCommand.SilenceIncomingRinger()) + ActiveCallManager.sendAudioManagerCommand(context, AudioManagerCommand.SilenceIncomingRinger()) } override fun onReject() { Log.i(TAG, "onReject()") - WebRtcCallService.denyCall(context) + ActiveCallManager.denyCall() } override fun onDisconnect() { Log.i(TAG, "onDisconnect()") - WebRtcCallService.hangup(context) + ActiveCallManager.hangup() } companion object { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java deleted file mode 100644 index 27dfa2b93a..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java +++ /dev/null @@ -1,487 +0,0 @@ -package org.thoughtcrime.securesms.service.webrtc; - -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ServiceInfo; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Build; -import android.os.IBinder; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; - -import androidx.annotation.MainThread; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.signal.core.util.PendingIntentFlags; -import org.signal.core.util.ThreadUtil; -import org.signal.core.util.concurrent.SignalExecutors; -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.jobs.ForegroundServiceUtil; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.util.FeatureFlags; -import org.thoughtcrime.securesms.util.TelephonyUtil; -import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; -import org.thoughtcrime.securesms.webrtc.UncaughtExceptionHandlerManager; -import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCommand; -import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager; -import org.thoughtcrime.securesms.webrtc.locks.LockManager; - -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; - -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.schedulers.Schedulers; - -/** - * Provide a foreground service for {@link SignalCallManager} to leverage to run in the background when necessary. Also - * provides devices listeners needed for during a call (i.e., bluetooth, power button). - */ -public final class WebRtcCallService extends Service implements SignalAudioManager.EventListener { - - private static final String TAG = Log.tag(WebRtcCallService.class); - private static final String WEBSOCKET_KEEP_ALIVE_TOKEN = WebRtcCallService.class.getName(); - - private static final String ACTION_UPDATE = "UPDATE"; - private static final String ACTION_STOP = "STOP"; - private static final String ACTION_DENY_CALL = "DENY_CALL"; - private static final String ACTION_LOCAL_HANGUP = "LOCAL_HANGUP"; - private static final String ACTION_CHANGE_POWER_BUTTON = "CHANGE_POWER_BUTTON"; - private static final String ACTION_SEND_AUDIO_COMMAND = "SEND_AUDIO_COMMAND"; - - private static final String EXTRA_UPDATE_TYPE = "UPDATE_TYPE"; - private static final String EXTRA_RECIPIENT_ID = "RECIPIENT_ID"; - private static final String EXTRA_ENABLED = "ENABLED"; - private static final String EXTRA_AUDIO_COMMAND = "AUDIO_COMMAND"; - private static final String EXTRA_IS_VIDEO_CALL = "IS_VIDEO_CALL"; - - private static final int INVALID_NOTIFICATION_ID = -1; - private static final long REQUEST_WEBSOCKET_STAY_OPEN_DELAY = TimeUnit.MINUTES.toMillis(1); - private static final long FOREGROUND_SERVICE_TIMEOUT = TimeUnit.SECONDS.toMillis(10); - - private final WebSocketKeepAliveTask webSocketKeepAliveTask = new WebSocketKeepAliveTask(); - private final Executor singleThreadExecutor = SignalExecutors.newCachedSingleThreadExecutor("signal-webrtc-in-call", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD); - - private SignalCallManager callManager; - - private NetworkReceiver networkReceiver; - private PowerButtonReceiver powerButtonReceiver; - private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager; - private PhoneStateListener hangUpRtcOnDeviceCallAnswered; - private SignalAudioManager signalAudioManager; - private int lastNotificationId; - private Notification lastNotification; - private long lastNotificationRequestTime; - private Disposable lastNotificationDisposable = Disposable.disposed(); - private boolean stopping = false; - - private static ActiveCallManager activeCallManager = null; - - public synchronized static void update(@NonNull Context context, int type, @NonNull RecipientId recipientId, boolean isVideoCall) { - if (FeatureFlags.useActiveCallManager()) { - if (activeCallManager == null) { - activeCallManager = new ActiveCallManager(context); - } - activeCallManager.update(type, recipientId, isVideoCall); - - return; - } - - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_UPDATE) - .putExtra(EXTRA_UPDATE_TYPE, type) - .putExtra(EXTRA_RECIPIENT_ID, recipientId) - .putExtra(EXTRA_IS_VIDEO_CALL, isVideoCall); - - ForegroundServiceUtil.tryToStartWhenCapable(context, intent, FOREGROUND_SERVICE_TIMEOUT); - } - - public static void denyCall(@NonNull Context context) { - if (FeatureFlags.useActiveCallManager()) { - ApplicationDependencies.getSignalCallManager().denyCall(); - return; - } - - ForegroundServiceUtil.tryToStartWhenCapable(context, new Intent(context, WebRtcCallService.class).setAction(ACTION_DENY_CALL), FOREGROUND_SERVICE_TIMEOUT); - } - - public static void hangup(@NonNull Context context) { - if (FeatureFlags.useActiveCallManager()) { - ApplicationDependencies.getSignalCallManager().localHangup(); - return; - } - - ForegroundServiceUtil.tryToStartWhenCapable(context, new Intent(context, WebRtcCallService.class).setAction(ACTION_LOCAL_HANGUP), FOREGROUND_SERVICE_TIMEOUT); - } - - public synchronized static void stop(@NonNull Context context) { - if (FeatureFlags.useActiveCallManager()) { - if (activeCallManager != null) { - activeCallManager.stop(); - activeCallManager = null; - } - return; - } - - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_STOP); - - ForegroundServiceUtil.tryToStartWhenCapable(context, intent, FOREGROUND_SERVICE_TIMEOUT); - } - - public synchronized static @NonNull PendingIntent denyCallIntent(@NonNull Context context) { - if (FeatureFlags.useActiveCallManager()) { - Intent intent = new Intent(context, ActiveCallManager.ActiveCallServiceReceiver.class); - intent.setAction(ActiveCallManager.ActiveCallServiceReceiver.ACTION_DENY); - return PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable()); - } - - return getServicePendingIntent(context, new Intent(context, WebRtcCallService.class).setAction(ACTION_DENY_CALL)); - } - - public synchronized static @NonNull PendingIntent hangupIntent(@NonNull Context context) { - if (FeatureFlags.useActiveCallManager()) { - Intent intent = new Intent(context, ActiveCallManager.ActiveCallServiceReceiver.class); - intent.setAction(ActiveCallManager.ActiveCallServiceReceiver.ACTION_HANGUP); - return PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable()); - } - - return getServicePendingIntent(context, new Intent(context, WebRtcCallService.class).setAction(ACTION_LOCAL_HANGUP)); - } - - public synchronized static void sendAudioManagerCommand(@NonNull Context context, @NonNull AudioManagerCommand command) { - if (FeatureFlags.useActiveCallManager()) { - if (activeCallManager == null) { - activeCallManager = new ActiveCallManager(context); - } - activeCallManager.sendAudioCommand(command); - - return; - } - - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_SEND_AUDIO_COMMAND) - .putExtra(EXTRA_AUDIO_COMMAND, command); - ForegroundServiceUtil.tryToStartWhenCapable(context, intent, FOREGROUND_SERVICE_TIMEOUT); - } - - public synchronized static void changePowerButtonReceiver(@NonNull Context context, boolean register) { - if (FeatureFlags.useActiveCallManager()) { - if (activeCallManager == null) { - activeCallManager = new ActiveCallManager(context); - } - activeCallManager.changePowerButton(register); - - return; - } - - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_CHANGE_POWER_BUTTON) - .putExtra(EXTRA_ENABLED, register); - - ForegroundServiceUtil.tryToStartWhenCapable(context, intent, FOREGROUND_SERVICE_TIMEOUT); - } - - @Override - public void onCreate() { - Log.v(TAG, "onCreate"); - super.onCreate(); - this.callManager = ApplicationDependencies.getSignalCallManager(); - this.hangUpRtcOnDeviceCallAnswered = new HangUpRtcOnPstnCallAnsweredListener(); - this.lastNotificationId = INVALID_NOTIFICATION_ID; - - registerUncaughtExceptionHandler(); - registerNetworkReceiver(); - - if (!AndroidTelecomUtil.getTelecomSupported()) { - try { - TelephonyUtil.getManager(this).listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_CALL_STATE); - } catch (SecurityException e) { - Log.w(TAG, "Failed to listen to PSTN call answers!", e); - } - } - } - - @Override - public void onDestroy() { - Log.v(TAG, "onDestroy"); - super.onDestroy(); - - if (uncaughtExceptionHandlerManager != null) { - uncaughtExceptionHandlerManager.unregister(); - } - - if (signalAudioManager != null) { - signalAudioManager.shutdown(); - } - - unregisterNetworkReceiver(); - unregisterPowerButtonReceiver(); - - if (!AndroidTelecomUtil.getTelecomSupported()) { - TelephonyUtil.getManager(this).listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_NONE); - } - - webSocketKeepAliveTask.stop(); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if (intent == null || intent.getAction() == null) { - Log.w(TAG, "Service running with null intent/action likely from system restart, stopping"); - stop(); - return START_NOT_STICKY; - } - - Log.i(TAG, "action: " + intent.getAction()); - webSocketKeepAliveTask.start(); - - switch (intent.getAction()) { - case ACTION_UPDATE: - setCallInProgressNotification(intent.getIntExtra(EXTRA_UPDATE_TYPE, 0), - Objects.requireNonNull(intent.getParcelableExtra(EXTRA_RECIPIENT_ID)), - intent.getBooleanExtra(EXTRA_IS_VIDEO_CALL, false)); - return START_STICKY; - case ACTION_SEND_AUDIO_COMMAND: - setCallNotification(); - if (signalAudioManager == null) { - signalAudioManager = SignalAudioManager.create(this, this); - } - AudioManagerCommand audioCommand = Objects.requireNonNull(intent.getParcelableExtra(EXTRA_AUDIO_COMMAND)); - Log.i(TAG, "Sending audio command [" + audioCommand.getClass().getSimpleName() + "] to " + signalAudioManager.getClass().getSimpleName()); - signalAudioManager.handleCommand(audioCommand); - return START_STICKY; - case ACTION_CHANGE_POWER_BUTTON: - setCallNotification(); - if (intent.getBooleanExtra(EXTRA_ENABLED, false)) { - registerPowerButtonReceiver(); - } else { - unregisterPowerButtonReceiver(); - } - return START_STICKY; - case ACTION_STOP: - setCallNotification(true); - stop(); - return START_NOT_STICKY; - case ACTION_DENY_CALL: - setCallNotification(); - callManager.denyCall(); - return START_NOT_STICKY; - case ACTION_LOCAL_HANGUP: - setCallNotification(); - callManager.localHangup(); - return START_NOT_STICKY; - default: - throw new AssertionError("Unknown action: " + intent.getAction()); - } - } - - private void setCallNotification() { - setCallNotification(false); - } - - private void setCallNotification(boolean stopping) { - if (!stopping && lastNotificationId != INVALID_NOTIFICATION_ID) { - startForegroundCompat(lastNotificationId, lastNotification); - } else { - if (!stopping) { - Log.i(TAG, "Service was started without calling UPDATE first, using temporary notification."); - } - startForegroundCompat(CallNotificationBuilder.getStartingStoppingNotificationId(), stopping ? CallNotificationBuilder.getStoppingNotification(this) - : CallNotificationBuilder.getStartingNotification(this)); - } - } - - public void setCallInProgressNotification(int type, @NonNull RecipientId id, boolean isVideoCall) { - lastNotificationDisposable.dispose(); - - boolean requiresAsyncNotificationLoad = Build.VERSION.SDK_INT <= 29; - - lastNotificationId = CallNotificationBuilder.getNotificationId(type); - lastNotification = CallNotificationBuilder.getCallInProgressNotification(this, type, Recipient.resolved(id), isVideoCall, requiresAsyncNotificationLoad); - - startForegroundCompat(lastNotificationId, lastNotification); - - if (requiresAsyncNotificationLoad) { - final long requestTime = System.currentTimeMillis(); - lastNotificationRequestTime = requestTime; - lastNotificationDisposable = Single - .fromCallable(() -> CallNotificationBuilder.getCallInProgressNotification(this, type, Recipient.resolved(id), isVideoCall, false)) - .subscribeOn(Schedulers.from(singleThreadExecutor)) - .observeOn(AndroidSchedulers.mainThread()) - .filter(unused -> requestTime == lastNotificationRequestTime && !stopping) - .subscribe(notification -> { - lastNotification = notification; - startForegroundCompat(lastNotificationId, lastNotification); - }); - } - } - - private synchronized void startForegroundCompat(int notificationId, Notification notification) { - if (stopping) { - return; - } - - if (Build.VERSION.SDK_INT >= 30) { - startForeground(notificationId, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE); - } else { - startForeground(notificationId, notification); - } - } - - private synchronized void stop() { - stopping = true; - stopForeground(true); - stopSelf(); - } - - private void registerUncaughtExceptionHandler() { - uncaughtExceptionHandlerManager = new UncaughtExceptionHandlerManager(); - uncaughtExceptionHandlerManager.registerHandler(new ProximityLockRelease(callManager.getLockManager())); - } - - private void registerNetworkReceiver() { - if (networkReceiver == null) { - networkReceiver = new NetworkReceiver(); - - registerReceiver(networkReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); - } - } - - private void unregisterNetworkReceiver() { - if (networkReceiver != null) { - unregisterReceiver(networkReceiver); - - networkReceiver = null; - } - } - - public void registerPowerButtonReceiver() { - if (!AndroidTelecomUtil.getTelecomSupported() && powerButtonReceiver == null) { - powerButtonReceiver = new PowerButtonReceiver(); - - registerReceiver(powerButtonReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); - } - } - - public void unregisterPowerButtonReceiver() { - if (powerButtonReceiver != null) { - unregisterReceiver(powerButtonReceiver); - - powerButtonReceiver = null; - } - } - - @Override - public @Nullable IBinder onBind(Intent intent) { - return null; - } - - @Override - public void onAudioDeviceChanged(@NonNull SignalAudioManager.AudioDevice activeDevice, @NonNull Set availableDevices) { - callManager.onAudioDeviceChanged(activeDevice, availableDevices); - } - - @Override - public void onBluetoothPermissionDenied() { - callManager.onBluetoothPermissionDenied(); - } - - public static PendingIntent getServicePendingIntent(@NonNull Context context, @NonNull Intent intent) { - return Build.VERSION.SDK_INT >= 26 ? PendingIntent.getForegroundService(context, 0, intent, PendingIntentFlags.mutable()) - : PendingIntent.getService(context, 0, intent, PendingIntentFlags.mutable()); - } - - @SuppressWarnings("deprecation") - private class HangUpRtcOnPstnCallAnsweredListener extends PhoneStateListener { - @Override - public void onCallStateChanged(int state, @NonNull String phoneNumber) { - super.onCallStateChanged(state, phoneNumber); - if (state == TelephonyManager.CALL_STATE_OFFHOOK) { - hangup(); - Log.i(TAG, "Device phone call ended Signal call."); - } - } - - private void hangup() { - callManager.localHangup(); - } - } - - /** - * Periodically request the web socket stay open if we are doing anything call related. - */ - private class WebSocketKeepAliveTask implements Runnable { - private boolean keepRunning = false; - - @MainThread - public void start() { - if (!keepRunning) { - keepRunning = true; - run(); - } - } - - @MainThread - public void stop() { - keepRunning = false; - ThreadUtil.cancelRunnableOnMain(webSocketKeepAliveTask); - ApplicationDependencies.getIncomingMessageObserver().removeKeepAliveToken(WEBSOCKET_KEEP_ALIVE_TOKEN); - } - - @MainThread - @Override - public void run() { - if (keepRunning) { - ApplicationDependencies.getIncomingMessageObserver().registerKeepAliveToken(WEBSOCKET_KEEP_ALIVE_TOKEN); - ThreadUtil.runOnMainDelayed(this, REQUEST_WEBSOCKET_STAY_OPEN_DELAY); - } - } - } - - private static class NetworkReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); - - ApplicationDependencies.getSignalCallManager().networkChange(activeNetworkInfo != null && activeNetworkInfo.isConnected()); - ApplicationDependencies.getSignalCallManager().dataModeUpdate(); - } - } - - private static class PowerButtonReceiver extends BroadcastReceiver { - @Override - public void onReceive(@NonNull Context context, @NonNull Intent intent) { - if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { - ApplicationDependencies.getSignalCallManager().screenOff(); - } - } - } - - private static class ProximityLockRelease implements Thread.UncaughtExceptionHandler { - private final LockManager lockManager; - - private ProximityLockRelease(@NonNull LockManager lockManager) { - this.lockManager = lockManager; - } - - @Override - public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) { - Log.i(TAG, "Uncaught exception - releasing proximity lock", throwable); - lockManager.updatePhoneState(LockManager.PhoneState.IDLE); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java index 37c5e7e31d..219c3431cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java @@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCommand; import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager; import org.thoughtcrime.securesms.webrtc.locks.LockManager; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; -import org.whispersystems.signalservice.internal.push.SyncMessage; import java.util.Collection; import java.util.UUID; @@ -96,11 +95,11 @@ public class WebRtcInteractor { } void setCallInProgressNotification(int type, @NonNull RemotePeer remotePeer, boolean isVideoCall) { - WebRtcCallService.update(context, type, remotePeer.getRecipient().getId(), isVideoCall); + ActiveCallManager.update(context, type, remotePeer.getRecipient().getId(), isVideoCall); } void setCallInProgressNotification(int type, @NonNull Recipient recipient, boolean isVideoCall) { - WebRtcCallService.update(context, type, recipient.getId(), isVideoCall); + ActiveCallManager.update(context, type, recipient.getId(), isVideoCall); } void retrieveTurnServers(@NonNull RemotePeer remotePeer) { @@ -108,7 +107,7 @@ public class WebRtcInteractor { } void stopForegroundService() { - WebRtcCallService.stop(context); + ActiveCallManager.stop(); } void insertMissedCall(@NonNull RemotePeer remotePeer, long timestamp, boolean isVideoOffer) { @@ -128,51 +127,51 @@ public class WebRtcInteractor { } void registerPowerButtonReceiver() { - WebRtcCallService.changePowerButtonReceiver(context, true); + ActiveCallManager.changePowerButtonReceiver(context, true); } void unregisterPowerButtonReceiver() { - WebRtcCallService.changePowerButtonReceiver(context, false); + ActiveCallManager.changePowerButtonReceiver(context, false); } void silenceIncomingRinger() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SilenceIncomingRinger()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SilenceIncomingRinger()); } void initializeAudioForCall() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.Initialize()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.Initialize()); } void startIncomingRinger(@Nullable Uri ringtoneUri, boolean vibrate) { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.StartIncomingRinger(ringtoneUri, vibrate)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.StartIncomingRinger(ringtoneUri, vibrate)); } void startOutgoingRinger() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.StartOutgoingRinger()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.StartOutgoingRinger()); } void stopAudio(boolean playDisconnect) { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.Stop(playDisconnect)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.Stop(playDisconnect)); } void startAudioCommunication() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.Start()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.Start()); } public void setUserAudioDevice(@Nullable RecipientId recipientId, @NonNull SignalAudioManager.ChosenAudioDeviceIdentifier userDevice) { if (userDevice.isLegacy()) { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDeviceLegacy().ordinal(), false)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDeviceLegacy().ordinal(), false)); } else { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDevice31(), true)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDevice31(), true)); } } public void setDefaultAudioDevice(@NonNull RecipientId recipientId, @NonNull SignalAudioManager.AudioDevice userDevice, boolean clearUserEarpieceSelection) { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetDefaultDevice(recipientId, userDevice, clearUserEarpieceSelection)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SetDefaultDevice(recipientId, userDevice, clearUserEarpieceSelection)); } public void playStateChangeUp() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.PlayStateChangeUp()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.PlayStateChangeUp()); } void peekGroupCallForRingingCheck(@NonNull GroupCallRingCheckInfo groupCallRingCheckInfo) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index b9e9355886..08f450dff4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -116,7 +116,6 @@ public final class FeatureFlags { private static final String CALLING_REACTIONS = "android.calling.reactions"; private static final String NOTIFICATION_THUMBNAIL_BLOCKLIST = "android.notificationThumbnailProductBlocklist"; private static final String CALLING_RAISE_HAND = "android.calling.raiseHand"; - private static final String USE_ACTIVE_CALL_MANAGER = "android.calling.useActiveCallManager.4"; private static final String GIF_SEARCH = "global.gifSearch"; private static final String AUDIO_REMUXING = "android.media.audioRemux.1"; private static final String VIDEO_RECORD_1X_ZOOM = "android.media.videoCaptureDefaultZoom"; @@ -197,7 +196,6 @@ public final class FeatureFlags { CALLING_REACTIONS, NOTIFICATION_THUMBNAIL_BLOCKLIST, CALLING_RAISE_HAND, - USE_ACTIVE_CALL_MANAGER, GIF_SEARCH, AUDIO_REMUXING, VIDEO_RECORD_1X_ZOOM, @@ -694,11 +692,6 @@ public final class FeatureFlags { return getString(NOTIFICATION_THUMBNAIL_BLOCKLIST, ""); } - /** Whether or not to use active call manager instead of WebRtcCallService. */ - public static boolean useActiveCallManager() { - return getBoolean(USE_ACTIVE_CALL_MANAGER, false); - } - /** Whether the in-app GIF search is available for use. */ public static boolean gifSearchAvailable() { return getBoolean(GIF_SEARCH, true); diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java index d2aafad338..a6cc907b87 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java @@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.WebRtcCallActivity; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.webrtc.WebRtcCallService; +import org.thoughtcrime.securesms.service.webrtc.ActiveCallManager; import org.thoughtcrime.securesms.util.ConversationUtil; /** @@ -29,8 +29,8 @@ import org.thoughtcrime.securesms.util.ConversationUtil; public class CallNotificationBuilder { - public static final int WEBRTC_NOTIFICATION = 313388; - private static final int WEBRTC_NOTIFICATION_RINGING = 313389; + public static final int WEBRTC_NOTIFICATION = 313388; + public static final int WEBRTC_NOTIFICATION_RINGING = 313389; public static final int TYPE_INCOMING_RINGING = 1; public static final int TYPE_OUTGOING_RINGING = 2; @@ -114,7 +114,7 @@ public class CallNotificationBuilder { if (deviceVersionSupportsIncomingCallStyle()) { builder.setStyle(NotificationCompat.CallStyle.forIncomingCall( person, - WebRtcCallService.denyCallIntent(context), + ActiveCallManager.denyCallIntent(context), getActivityPendingIntent(context, isVideoCall ? LaunchCallScreenIntentState.VIDEO : LaunchCallScreenIntentState.AUDIO) ).setIsVideo(isVideoCall)); } @@ -122,7 +122,7 @@ public class CallNotificationBuilder { return builder.build(); } else if (type == TYPE_OUTGOING_RINGING) { builder.setContentText(context.getString(R.string.NotificationBarManager__establishing_signal_call)); - builder.addAction(getServiceNotificationAction(context, WebRtcCallService.hangupIntent(context), R.drawable.symbol_phone_down_fill_24, R.string.NotificationBarManager__cancel_call)); + builder.addAction(getServiceNotificationAction(context, ActiveCallManager.hangupIntent(context), R.drawable.symbol_phone_down_fill_24, R.string.NotificationBarManager__cancel_call)); return builder.build(); } else { builder.setContentText(getOngoingCallContentText(context, recipient, isVideoCall)); @@ -138,7 +138,7 @@ public class CallNotificationBuilder { if (deviceVersionSupportsIncomingCallStyle()) { builder.setStyle(NotificationCompat.CallStyle.forOngoingCall( person, - WebRtcCallService.hangupIntent(context) + ActiveCallManager.hangupIntent(context) ).setIsVideo(isVideoCall)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt index c99acb7e9a..52306bad2e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt @@ -12,9 +12,8 @@ import org.thoughtcrime.securesms.util.ParcelUtil * Commands that can be issued to [SignalAudioManager] to perform various tasks. * * Additional context: The audio management is tied closely with the Android audio and thus benefits from being - * tied to the [org.thoughtcrime.securesms.service.webrtc.WebRtcCallService] lifecycle. Because of this, all - * calls have to go through an intent to the service and this allows one entry point for that but multiple - * operations. + * tied to the [org.thoughtcrime.securesms.service.webrtc.ActiveCallManager] lifecycle. Because of this, all + * calls have to go through it and this allows one entry point for that but multiple operations. */ sealed class AudioManagerCommand : Parcelable {