Skip to content

Commit

Permalink
Issue 1588 restrictions on background starts (#1670)
Browse files Browse the repository at this point in the history
* Resolved ForegroundServiceStartNotAllowedException for MessagesSenderWorker.| #1588

* Modified PassPhrasesInRAMService to support "restrictions on background starts".| #1588

* Modified MessagesSenderWorker to support "restrictions on background starts".| #1588

* Modified logic in MessagesSenderWorker.kt.| #1588
  • Loading branch information
DenBond7 authored Jan 28, 2022
1 parent 7e8b560 commit a0fcefe
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
package com.flowcrypt.email.jetpack.workmanager

import android.accounts.AuthenticatorException
import android.app.ForegroundServiceStartNotAllowedException
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.work.Constraints
import androidx.work.ExistingWorkPolicy
import androidx.work.ForegroundInfo
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.flowcrypt.email.Constants
Expand Down Expand Up @@ -102,8 +105,20 @@ class MessagesSenderWorker(context: Context, params: WorkerParameters) :
)
)

if (!CollectionUtils.isEmpty(queuedMsgs) || !CollectionUtils.isEmpty(sentButNotSavedMsgs)) {
setForeground(genForegroundInfo(account))
if (queuedMsgs.isNotEmpty() || sentButNotSavedMsgs.isNotEmpty()) {
try {
setForeground(genForegroundInfoInternal(account.email, false))
} catch (e: IllegalStateException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (e is ForegroundServiceStartNotAllowedException) {
//see for details https://developer.android.com/topic/libraries/architecture/workmanager/how-to/define-work#coroutineworker
LogsUtil.d(
TAG, "It seems the app started this worker while running in the background." +
" We can't show a notification in that case."
)
}
}
}

if (account.useAPI) {
when (account.accountType) {
Expand Down Expand Up @@ -157,19 +172,37 @@ class MessagesSenderWorker(context: Context, params: WorkerParameters) :
}
}

private fun genForegroundInfo(account: AccountEntity): ForegroundInfo {
val title = applicationContext.getString(R.string.sending_email)
override suspend fun getForegroundInfo(): ForegroundInfo {
/*we should add implementation of this method if we use
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).
Android 11 and less uses it to show a notification*/
return genForegroundInfoInternal()
}

private fun genForegroundInfoInternal(
email: String? = null,
isDefault: Boolean = true
): ForegroundInfo {
val notification = NotificationCompat.Builder(
applicationContext,
NotificationChannelManager.CHANNEL_ID_SYNC
)
.setContentTitle(title)
.setTicker(title)
.setSmallIcon(R.drawable.ic_sending_email_grey_24dp)
.setOngoing(true)
.setSubText(account.email)
.setProgress(0, 0, true)
.build()
.setOngoing(true)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.apply {
val title = applicationContext.getString(
if (isDefault) R.string.synchronization else R.string.sending_email
)
setContentTitle(title)
if (isDefault) {
setSmallIcon(R.drawable.ic_synchronization_grey_24dp)
} else {
setTicker(title)
setSmallIcon(R.drawable.ic_sending_email_grey_24dp)
setSubText(email)
}
}.build()

return ForegroundInfo(NOTIFICATION_ID, notification)
}
Expand Down Expand Up @@ -513,7 +546,7 @@ class MessagesSenderWorker(context: Context, params: WorkerParameters) :

companion object {
private val TAG = MessagesSenderWorker::class.java.simpleName
private const val NOTIFICATION_ID = -10000
private const val NOTIFICATION_ID = R.id.notification_id_sending_msgs_worker
val NAME = MessagesSenderWorker::class.java.simpleName

fun enqueue(context: Context, forceSending: Boolean = false) {
Expand All @@ -528,6 +561,16 @@ class MessagesSenderWorker(context: Context, params: WorkerParameters) :
if (forceSending) ExistingWorkPolicy.REPLACE else ExistingWorkPolicy.KEEP,
OneTimeWorkRequestBuilder<MessagesSenderWorker>()
.setConstraints(constraints)
.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
/*we have to use it due to
https://developer.android.com/guide/components/foreground-services#background-start-restrictions
We don't use it by default to prevent displaying a notification every time
when this job will be started. We should try to show a notification only if we have
at least one outgoing message that is actively sending*/
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
}
}
.build()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@

package com.flowcrypt.email.service

import android.app.ForegroundServiceStartNotAllowedException
import android.app.Notification
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.lifecycle.lifecycleScope
import com.flowcrypt.email.R
import com.flowcrypt.email.model.KeysStorage
Expand All @@ -18,7 +21,6 @@ import com.flowcrypt.email.ui.notifications.NotificationChannelManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.isActive
Expand All @@ -38,26 +40,13 @@ class PassPhrasesInRAMService : BaseLifecycleService() {
override fun onCreate() {
super.onCreate()
keysStorage = KeysStorageImpl.getInstance(applicationContext)
runAsForeground()
runChecking()
}

private fun runAsForeground() {
val pendingIntent: PendingIntent =
Intent(this, EmailManagerActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
}

val notification: Notification = Notification.Builder(
this,
NotificationChannelManager.CHANNEL_ID_SILENT
)
.setContentTitle(getString(R.string.active_passphrase_session))
.setSmallIcon(R.drawable.ic_baseline_password_24dp)
.setContentIntent(pendingIntent)
.build()

startForeground(R.id.notification_id_passphrase_service, notification)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val superStartedState = super.onStartCommand(intent, flags, startId)
startForeground(R.id.notification_id_passphrase_service, prepareNotification())
return superStartedState
}

private fun runChecking() {
Expand All @@ -79,6 +68,22 @@ class PassPhrasesInRAMService : BaseLifecycleService() {
}.flowOn(Dispatchers.Default)
}

private fun prepareNotification(): Notification {
return NotificationCompat.Builder(this, NotificationChannelManager.CHANNEL_ID_SILENT)
.setContentTitle(getString(R.string.active_passphrase_session))
.setSmallIcon(R.drawable.ic_baseline_password_24dp)
.setContentIntent(
PendingIntent.getActivity(
this,
0,
Intent(this, EmailManagerActivity::class.java),
PendingIntent.FLAG_IMMUTABLE
)
)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.build()
}

companion object {
/**
* We will run checking every minute.
Expand All @@ -92,7 +97,17 @@ class PassPhrasesInRAMService : BaseLifecycleService() {
*/
fun start(context: Context) {
val startEmailServiceIntent = Intent(context, PassPhrasesInRAMService::class.java)
context.startForegroundService(startEmailServiceIntent)
try {
context.startForegroundService(startEmailServiceIntent)
} catch (e: Exception) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (e is ForegroundServiceStartNotAllowedException) {
/*Because this service should be restarted by the system we can skip this exception.
It seems this service was started manually after the app crash via the trigger.*/
e.printStackTrace()
}
} else throw e
}
}

/**
Expand Down
15 changes: 15 additions & 0 deletions FlowCrypt/src/main/res/drawable/ic_synchronization_grey_24dp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!--
~ © 2016-present FlowCrypt a.s. Limitations apply. Contact [email protected]
~ Contributors: denbond7
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#B6B6B6"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z" />
</vector>
1 change: 1 addition & 0 deletions FlowCrypt/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -487,4 +487,5 @@
<string name="one_special_character_eg">один из специальных символов: &amp;"#-'_%-@,;:!*()</string>
<string name="warning_use_subject_as_password">Пожалуйста, не прописывайте пароль в тему сообщения. Передача пароля по электронной почте далает бессмысленным шифрование на основе пароля.\n\nВы можете попросить получателя также установить %1$s, сообщения между пользователями %1$s не нуждаются в пароле.</string>
<string name="warning_use_private_key_pass_phrase_as_password">Пожалуйста, не используйте Вашу ключевую фразу в качестве пароля для даного сообщения.\n\nВы должны придумать какой-нибудь другой уникальный пароль, которым Вы можете поделиться с получателем.</string>
<string name="synchronization">Синхронизация&#8230;</string>
</resources>
1 change: 1 addition & 0 deletions FlowCrypt/src/main/res/values/ids.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@
<item name="notification_id_has_failed_outgoing_msgs" type="id" />
<item name="notification_id_auth_failure" type="id" />
<item name="notification_id_passphrase_service" type="id" />
<item name="notification_id_sending_msgs_worker" type="id" />
</resources>
1 change: 1 addition & 0 deletions FlowCrypt/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -573,4 +573,5 @@
<string name="one_special_character_eg">one special character eg &amp;"#-'_%-@,;:!*()</string>
<string name="warning_use_subject_as_password">Please do not include the password in the email subject. Sharing password over email undermines password based encryption.\n\nYou can ask the recipient to also install %1$s, messages between %1$s users don\'t need a password.</string>
<string name="warning_use_private_key_pass_phrase_as_password">Please do not use your private key pass phrase as a password for this message.\n\nYou should come up with some other unique password that you can share with a recipient.</string>
<string name="synchronization">Syncing&#8230;</string>
</resources>

0 comments on commit a0fcefe

Please sign in to comment.