2022-05-10 19:11:47 +00:00
|
|
|
package org.thoughtcrime.securesms.gcm
|
|
|
|
|
|
|
|
import android.content.Context
|
|
|
|
import android.content.Intent
|
|
|
|
import android.os.Build
|
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
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.PushNotificationReceiveJob
|
|
|
|
import org.thoughtcrime.securesms.messages.RestStrategy
|
|
|
|
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Our goals with FCM processing are as follows:
|
|
|
|
* (1) Ensure some service is active for the duration of the fetch and processing stages.
|
|
|
|
* (2) Do not make unnecessary network requests.
|
|
|
|
*
|
|
|
|
* To fulfill goal 1, this class will not stop the services until there is no more running
|
|
|
|
* requests.
|
|
|
|
*
|
|
|
|
* To fulfill goal 2, this class will not enqueue a fetch if there are already 2 active fetches
|
|
|
|
* (or rather, 1 active and 1 waiting, since we use a single thread executor).
|
|
|
|
*
|
|
|
|
* Unfortunately we can't do this all in [FcmReceiveService] because it won't let us process
|
|
|
|
* the next FCM message until [FcmReceiveService.onMessageReceived] returns,
|
|
|
|
* but as soon as that method returns, it could also destroy the service. By not letting us control
|
|
|
|
* when the service is destroyed, we can't accomplish both goals within that service.
|
|
|
|
*/
|
|
|
|
object FcmFetchManager {
|
|
|
|
|
|
|
|
private val TAG = Log.tag(FcmFetchManager::class.java)
|
|
|
|
|
|
|
|
private val EXECUTOR = SerialMonoLifoExecutor(SignalExecutors.UNBOUNDED)
|
|
|
|
|
|
|
|
@Volatile
|
|
|
|
private var activeCount = 0
|
|
|
|
|
2022-05-13 13:12:02 +00:00
|
|
|
@Volatile
|
|
|
|
private var startedForeground = false
|
|
|
|
|
2022-07-05 15:32:42 +00:00
|
|
|
/**
|
|
|
|
* @return True if a service was successfully started, otherwise false.
|
|
|
|
*/
|
2022-05-10 19:11:47 +00:00
|
|
|
@JvmStatic
|
2022-07-05 15:32:42 +00:00
|
|
|
fun enqueue(context: Context, foreground: Boolean): Boolean {
|
2022-05-10 19:11:47 +00:00
|
|
|
synchronized(this) {
|
2022-06-13 14:17:43 +00:00
|
|
|
try {
|
|
|
|
if (foreground) {
|
|
|
|
Log.i(TAG, "Starting in the foreground.")
|
|
|
|
ContextCompat.startForegroundService(context, Intent(context, FcmFetchForegroundService::class.java))
|
|
|
|
startedForeground = true
|
|
|
|
} else {
|
|
|
|
Log.i(TAG, "Starting in the background.")
|
|
|
|
context.startService(Intent(context, FcmFetchBackgroundService::class.java))
|
|
|
|
}
|
2022-05-10 19:11:47 +00:00
|
|
|
|
2022-06-13 14:17:43 +00:00
|
|
|
val performedReplace = EXECUTOR.enqueue { fetch(context) }
|
2022-05-10 19:11:47 +00:00
|
|
|
|
2022-06-13 14:17:43 +00:00
|
|
|
if (performedReplace) {
|
|
|
|
Log.i(TAG, "Already have one running and one enqueued. Ignoring.")
|
|
|
|
} else {
|
|
|
|
activeCount++
|
|
|
|
Log.i(TAG, "Incrementing active count to $activeCount")
|
|
|
|
}
|
2022-08-02 18:17:16 +00:00
|
|
|
} catch (e: Exception) {
|
2022-06-13 14:17:43 +00:00
|
|
|
Log.w(TAG, "Failed to start service!", e)
|
2022-07-05 15:32:42 +00:00
|
|
|
return false
|
2022-05-10 19:11:47 +00:00
|
|
|
}
|
|
|
|
}
|
2022-07-05 15:32:42 +00:00
|
|
|
|
|
|
|
return true
|
2022-05-10 19:11:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun fetch(context: Context) {
|
|
|
|
retrieveMessages(context)
|
|
|
|
|
|
|
|
synchronized(this) {
|
|
|
|
activeCount--
|
|
|
|
|
|
|
|
if (activeCount <= 0) {
|
|
|
|
Log.i(TAG, "No more active. Stopping.")
|
|
|
|
context.stopService(Intent(context, FcmFetchBackgroundService::class.java))
|
2022-05-13 13:12:02 +00:00
|
|
|
|
|
|
|
if (startedForeground) {
|
2022-10-07 13:52:23 +00:00
|
|
|
try {
|
|
|
|
context.startService(FcmFetchForegroundService.buildStopIntent(context))
|
|
|
|
} catch (e: IllegalStateException) {
|
|
|
|
Log.w(TAG, "Failed to stop the foreground notification!", e)
|
|
|
|
}
|
|
|
|
|
2022-05-13 13:12:02 +00:00
|
|
|
startedForeground = false
|
|
|
|
}
|
2022-05-10 19:11:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
fun retrieveMessages(context: Context) {
|
|
|
|
val success = ApplicationDependencies.getBackgroundMessageRetriever().retrieveMessages(context, RestStrategy(), RestStrategy())
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
Log.i(TAG, "Successfully retrieved messages.")
|
|
|
|
} else {
|
|
|
|
if (Build.VERSION.SDK_INT >= 26) {
|
|
|
|
Log.w(TAG, "[API ${Build.VERSION.SDK_INT}] Failed to retrieve messages. Scheduling on the system JobScheduler (API " + Build.VERSION.SDK_INT + ").")
|
|
|
|
FcmJobService.schedule(context)
|
|
|
|
} else {
|
|
|
|
Log.w(TAG, "[API ${Build.VERSION.SDK_INT}] Failed to retrieve messages. Scheduling on JobManager (API " + Build.VERSION.SDK_INT + ").")
|
|
|
|
ApplicationDependencies.getJobManager().add(PushNotificationReceiveJob())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|