diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4351c489a5..ad3118218e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1396,6 +1396,16 @@ + + + + + + + + + + diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/SafeForegroundService.kt b/app/src/main/java/org/thoughtcrime/securesms/service/SafeForegroundService.kt index 187b9104e3..004beadc25 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/SafeForegroundService.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/SafeForegroundService.kt @@ -9,6 +9,7 @@ import android.app.Notification import android.app.Service import android.content.Context import android.content.Intent +import android.os.Bundle import android.os.IBinder import androidx.core.app.ServiceCompat import org.signal.core.util.logging.Log @@ -39,7 +40,7 @@ abstract class SafeForegroundService : Service() { * @return False if we tried to start the service but failed, otherwise true. */ @CheckReturnValue - fun start(context: Context, serviceClass: Class): Boolean { + fun start(context: Context, serviceClass: Class, extras: Bundle = Bundle.EMPTY): Boolean { stateLock.withLock { val state = currentState(serviceClass) @@ -57,7 +58,10 @@ abstract class SafeForegroundService : Service() { try { ForegroundServiceUtil.startWhenCapable( context = context, - intent = Intent(context, serviceClass).apply { action = ACTION_START } + intent = Intent(context, serviceClass).apply { + action = ACTION_START + putExtras(extras) + } ) true } catch (e: UnableToStartException) { @@ -79,14 +83,15 @@ abstract class SafeForegroundService : Service() { * Safely stops the service by starting it with an action to stop itself. * This is done to prevent scenarios where you stop the service while * a start is pending, preventing the posting of a foreground notification. + * @return true if service was running previously */ - fun stop(context: Context, serviceClass: Class) { + fun stop(context: Context, serviceClass: Class): Boolean { stateLock.withLock { val state = currentState(serviceClass) Log.d(TAG, "[stop] Current state: $state") - when (state) { + return when (state) { State.STARTING -> { Log.d(TAG, "[stop] Stopping service.") states[serviceClass] = State.STOPPING @@ -99,16 +104,19 @@ abstract class SafeForegroundService : Service() { Log.w(TAG, "Failed to start service class $serviceClass", e) states[serviceClass] = State.STOPPED } + true } State.STOPPED, State.STOPPING -> { Log.d(TAG, "[stop] No need to stop the service. Current state: $state") + false } State.NEEDS_RESTART -> { Log.i(TAG, "[stop] Clearing pending restart.") states[serviceClass] = State.STOPPING + false } } } 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 new file mode 100644 index 0000000000..de3b610a4f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ActiveCallManager.kt @@ -0,0 +1,330 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.service.webrtc + +import android.app.Notification +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.ConnectivityManager +import android.telephony.PhoneStateListener +import android.telephony.TelephonyManager +import androidx.annotation.MainThread +import androidx.core.app.NotificationManagerCompat +import androidx.core.os.bundleOf +import org.signal.core.util.ThreadUtil +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.UnableToStartException +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.service.SafeForegroundService +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.audio.SignalAudioManager.Companion.create +import org.thoughtcrime.securesms.webrtc.locks.LockManager +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]. + * + * 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. + */ +class ActiveCallManager( + private val application: Context +) : SignalAudioManager.EventListener { + + companion object { + private val TAG = Log.tag(ActiveCallManager::class.java) + } + + private val callManager = ApplicationDependencies.getSignalCallManager() + + private var networkReceiver: NetworkReceiver? = null + private var powerButtonReceiver: PowerButtonReceiver? = null + private var uncaughtExceptionHandlerManager: UncaughtExceptionHandlerManager? = null + private val webSocketKeepAliveTask: WebSocketKeepAliveTask = WebSocketKeepAliveTask() + private var signalAudioManager: SignalAudioManager? = null + private var previousNotificationId = -1 + + init { + registerUncaughtExceptionHandler() + registerNetworkReceiver() + + webSocketKeepAliveTask.start() + } + + fun stop() { + Log.v(TAG, "stop") + + uncaughtExceptionHandlerManager?.unregister() + uncaughtExceptionHandlerManager = null + + signalAudioManager?.shutdown() + signalAudioManager = null + + unregisterNetworkReceiver() + unregisterPowerButtonReceiver() + + webSocketKeepAliveTask.stop() + + if (!ActiveCallForegroundService.stop(application) && previousNotificationId != -1) { + NotificationManagerCompat.from(application).cancel(previousNotificationId) + } + } + + fun update(type: Int, recipientId: RecipientId, isVideoCall: Boolean) { + Log.i(TAG, "update $type $recipientId $isVideoCall") + + val notificationId = CallNotificationBuilder.getNotificationId(type) + + if (previousNotificationId != notificationId && previousNotificationId != -1) { + NotificationManagerCompat.from(application).cancel(previousNotificationId) + } + + previousNotificationId = notificationId + + if (type != CallNotificationBuilder.TYPE_ESTABLISHED) { + val notification = CallNotificationBuilder.getCallInProgressNotification(application, type, Recipient.resolved(recipientId), isVideoCall, false) + NotificationManagerCompat.from(application).notify(notificationId, notification) + } else { + ActiveCallForegroundService.start(application, recipientId, isVideoCall) + } + } + + fun sendAudioCommand(audioCommand: AudioManagerCommand) { + if (signalAudioManager == null) { + signalAudioManager = create(application, this) + } + + Log.i(TAG, "Sending audio command [" + audioCommand.javaClass.simpleName + "] to " + signalAudioManager?.javaClass?.simpleName) + signalAudioManager!!.handleCommand(audioCommand) + } + + fun changePowerButton(enabled: Boolean) { + if (enabled) { + registerPowerButtonReceiver() + } else { + unregisterPowerButtonReceiver() + } + } + + private fun registerUncaughtExceptionHandler() { + uncaughtExceptionHandlerManager = UncaughtExceptionHandlerManager() + uncaughtExceptionHandlerManager!!.registerHandler(ProximityLockRelease(callManager.lockManager)) + } + + private fun registerNetworkReceiver() { + if (networkReceiver == null) { + networkReceiver = NetworkReceiver() + application.registerReceiver(networkReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)) + } + } + + private fun unregisterNetworkReceiver() { + if (networkReceiver != null) { + application.unregisterReceiver(networkReceiver) + networkReceiver = null + } + } + + private fun registerPowerButtonReceiver() { + if (!AndroidTelecomUtil.telecomSupported && powerButtonReceiver == null) { + powerButtonReceiver = PowerButtonReceiver() + application.registerReceiver(powerButtonReceiver, IntentFilter(Intent.ACTION_SCREEN_OFF)) + } + } + + private fun unregisterPowerButtonReceiver() { + if (powerButtonReceiver != null) { + application.unregisterReceiver(powerButtonReceiver) + powerButtonReceiver = null + } + } + + override fun onAudioDeviceChanged(activeDevice: SignalAudioManager.AudioDevice, devices: Set) { + callManager.onAudioDeviceChanged(activeDevice, devices) + } + + override fun onBluetoothPermissionDenied() { + callManager.onBluetoothPermissionDenied() + } + + /** Foreground service started only after a call is established */ + class ActiveCallForegroundService : SafeForegroundService() { + companion object { + private const val EXTRA_RECIPIENT_ID = "RECIPIENT_ID" + private const val EXTRA_IS_VIDEO_CALL = "IS_VIDEO_CALL" + + fun start(context: Context, recipientId: RecipientId, isVideoCall: Boolean) { + val extras = bundleOf( + EXTRA_RECIPIENT_ID to recipientId, + EXTRA_IS_VIDEO_CALL to isVideoCall + ) + + if (!SafeForegroundService.start(context, ActiveCallForegroundService::class.java, extras)) { + throw UnableToStartException(Exception()) + } + } + + fun stop(context: Context): Boolean { + return SafeForegroundService.stop(context, ActiveCallForegroundService::class.java) + } + } + + override val tag: String + get() = TAG + + override val notificationId: Int + get() = CallNotificationBuilder.WEBRTC_NOTIFICATION + + private var hangUpRtcOnDeviceCallAnswered: PhoneStateListener? = null + private var notification: Notification? = null + + override fun onCreate() { + super.onCreate() + hangUpRtcOnDeviceCallAnswered = HangUpRtcOnPstnCallAnsweredListener() + + if (!AndroidTelecomUtil.telecomSupported) { + try { + TelephonyUtil.getManager(application).listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_CALL_STATE) + } catch (e: SecurityException) { + Log.w(TAG, "Failed to listen to PSTN call answers!", e) + } + } + } + + override fun onDestroy() { + super.onDestroy() + + if (!AndroidTelecomUtil.telecomSupported) { + TelephonyUtil.getManager(application).listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_NONE) + } + } + + override fun getForegroundNotification(intent: Intent): Notification { + if (notification != null) { + return notification!! + } else if (!intent.hasExtra(EXTRA_RECIPIENT_ID)) { + return CallNotificationBuilder.getStoppingNotification(this) + } + + val recipientId: RecipientId = intent.getParcelableExtra(EXTRA_RECIPIENT_ID)!! + val isVideoCall = intent.getBooleanExtra(EXTRA_IS_VIDEO_CALL, false) + + notification = CallNotificationBuilder.getCallInProgressNotification( + this, + CallNotificationBuilder.TYPE_ESTABLISHED, + Recipient.resolved(recipientId), + isVideoCall, + false + ) + + return notification!! + } + + @Suppress("deprecation") + private class HangUpRtcOnPstnCallAnsweredListener : PhoneStateListener() { + override fun onCallStateChanged(state: Int, phoneNumber: String) { + super.onCallStateChanged(state, phoneNumber) + if (state == TelephonyManager.CALL_STATE_OFFHOOK) { + hangup() + Log.i(TAG, "Device phone call ended Signal call.") + } + } + + private fun hangup() { + ApplicationDependencies.getSignalCallManager().localHangup() + } + } + } + + class ActiveCallServiceReceiver : BroadcastReceiver() { + + companion object { + const val ACTION_DENY = "org.thoughtcrime.securesms.service.webrtc.ActiveCallAction.DENY" + const val ACTION_HANGUP = "org.thoughtcrime.securesms.service.webrtc.ActiveCallAction.HANGUP" + } + + override fun onReceive(context: Context?, intent: Intent?) { + Log.d(TAG, "action: ${intent?.action}") + when (intent?.action) { + ACTION_DENY -> ApplicationDependencies.getSignalCallManager().denyCall() + ACTION_HANGUP -> ApplicationDependencies.getSignalCallManager().localHangup() + } + } + } + + /** + * Periodically request the web socket stay open if we are doing anything call related. + */ + private class WebSocketKeepAliveTask : Runnable { + + companion object { + private val REQUEST_WEBSOCKET_STAY_OPEN_DELAY: Duration = 1.minutes + private val WEBSOCKET_KEEP_ALIVE_TOKEN: String = WebSocketKeepAliveTask::class.java.simpleName + } + + private var keepRunning = false + + @MainThread + fun start() { + if (!keepRunning) { + keepRunning = true + run() + } + } + + @MainThread + fun stop() { + keepRunning = false + ThreadUtil.cancelRunnableOnMain(this) + ApplicationDependencies.getIncomingMessageObserver().removeKeepAliveToken(WEBSOCKET_KEEP_ALIVE_TOKEN) + } + + @MainThread + override fun run() { + if (keepRunning) { + ApplicationDependencies.getIncomingMessageObserver().registerKeepAliveToken(WEBSOCKET_KEEP_ALIVE_TOKEN) + ThreadUtil.runOnMainDelayed(this, REQUEST_WEBSOCKET_STAY_OPEN_DELAY.inWholeMilliseconds) + } + } + } + + private class NetworkReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val activeNetworkInfo = connectivityManager.activeNetworkInfo + + ApplicationDependencies.getSignalCallManager().apply { + networkChange(activeNetworkInfo != null && activeNetworkInfo.isConnected) + dataModeUpdate() + } + } + } + + private class PowerButtonReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (Intent.ACTION_SCREEN_OFF == intent.action) { + ApplicationDependencies.getSignalCallManager().screenOff() + } + } + } + + private class ProximityLockRelease(private val lockManager: LockManager) : Thread.UncaughtExceptionHandler { + override fun uncaughtException(thread: Thread, 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/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java index 4a7e866d99..27dfa2b93a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java @@ -1,6 +1,7 @@ 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; @@ -18,6 +19,7 @@ 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; @@ -25,6 +27,7 @@ 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; @@ -84,7 +87,18 @@ public final class WebRtcCallService extends Service implements SignalAudioManag private Disposable lastNotificationDisposable = Disposable.disposed(); private boolean stopping = false; - public static void update(@NonNull Context context, int type, @NonNull RecipientId recipientId, boolean isVideoCall) { + 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) @@ -95,36 +109,84 @@ public final class WebRtcCallService extends Service implements SignalAudioManag } public static void denyCall(@NonNull Context context) { - ForegroundServiceUtil.tryToStartWhenCapable(context, denyCallIntent(context), FOREGROUND_SERVICE_TIMEOUT); + 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) { - ForegroundServiceUtil.tryToStartWhenCapable(context, hangupIntent(context), FOREGROUND_SERVICE_TIMEOUT); + if (FeatureFlags.useActiveCallManager()) { + ApplicationDependencies.getSignalCallManager().localHangup(); + return; + } + + ForegroundServiceUtil.tryToStartWhenCapable(context, new Intent(context, WebRtcCallService.class).setAction(ACTION_LOCAL_HANGUP), FOREGROUND_SERVICE_TIMEOUT); } - public static void stop(@NonNull Context context) { + 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 static @NonNull Intent denyCallIntent(@NonNull Context context) { - return new Intent(context, WebRtcCallService.class).setAction(ACTION_DENY_CALL); + 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 static @NonNull Intent hangupIntent(@NonNull Context context) { - return new Intent(context, WebRtcCallService.class).setAction(ACTION_LOCAL_HANGUP); + 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 static void sendAudioManagerCommand(@NonNull Context context, @NonNull AudioManagerCommand command) { + 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 static void changePowerButtonReceiver(@NonNull Context context, boolean register) { + 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); @@ -337,6 +399,11 @@ public final class WebRtcCallService extends Service implements SignalAudioManag 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 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 8d7259051c..2a6debaf06 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -117,7 +117,8 @@ public final class FeatureFlags { public static final String SEPA_ENABLED_REGIONS = "global.donations.sepaEnabledRegions"; 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 CALLING_RAISE_HAND = "android.calling.raiseHand"; + private static final String USE_ACTIVE_CALL_MANAGER = "android.calling.useActiveCallManager"; /** * We will only store remote values for flags in this set. If you want a flag to be controllable @@ -188,7 +189,8 @@ public final class FeatureFlags { CALLING_REACTIONS, NOTIFICATION_THUMBNAIL_BLOCKLIST, CALLING_RAISE_HAND, - PHONE_NUMBER_PRIVACY + PHONE_NUMBER_PRIVACY, + USE_ACTIVE_CALL_MANAGER ); @VisibleForTesting @@ -679,6 +681,11 @@ 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); + } + /** Only for rendering debug info. */ public static synchronized @NonNull Map getMemoryValues() { return new TreeMap<>(REMOTE_VALUES); 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 e60417eedc..36309ed260 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java @@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.util.ConversationUtil; public class CallNotificationBuilder { - private static final int WEBRTC_NOTIFICATION = 313388; + public static final int WEBRTC_NOTIFICATION = 313388; private static final int WEBRTC_NOTIFICATION_RINGING = 313389; public static final int TYPE_INCOMING_RINGING = 1; @@ -114,7 +114,7 @@ public class CallNotificationBuilder { if (deviceVersionSupportsIncomingCallStyle()) { builder.setStyle(NotificationCompat.CallStyle.forIncomingCall( person, - getServicePendingIntent(context, WebRtcCallService.denyCallIntent(context)), + WebRtcCallService.denyCallIntent(context), getActivityPendingIntent(context, isVideoCall ? LaunchCallScreenIntentState.VIDEO : LaunchCallScreenIntentState.AUDIO) ).setIsVideo(isVideoCall)); } @@ -138,7 +138,7 @@ public class CallNotificationBuilder { if (deviceVersionSupportsIncomingCallStyle()) { builder.setStyle(NotificationCompat.CallStyle.forOngoingCall( person, - getServicePendingIntent(context, WebRtcCallService.hangupIntent(context)) + WebRtcCallService.hangupIntent(context) ).setIsVideo(isVideoCall)); } @@ -214,13 +214,8 @@ public class CallNotificationBuilder { } } - private 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()); - } - - private static NotificationCompat.Action getServiceNotificationAction(Context context, Intent intent, int iconResId, int titleResId) { - return new NotificationCompat.Action(iconResId, context.getString(titleResId), getServicePendingIntent(context, intent)); + private static NotificationCompat.Action getServiceNotificationAction(Context context, PendingIntent intent, int iconResId, int titleResId) { + return new NotificationCompat.Action(iconResId, context.getString(titleResId), intent); } private static PendingIntent getActivityPendingIntent(@NonNull Context context, @NonNull LaunchCallScreenIntentState launchCallScreenIntentState) {