I'm trying to logout user at midnight. This consists of erasing local data and calling remote endpoint. If app is running, it must inform user with dialog and status bar notification. If app is not running, it only leaves notification in status bar that user has been logged out.
Currently I managed to run service manually by clicking a menu item - it starts and runs as expected, receiving local broadcast (so, it only works when app is running):
R.id.menu_option_test -> {
println("menu_option_test")
Intent(this, LogoutService::class.java).also { intent ->
startService(intent)
}
return true
}
...
private fun watchForServiceBroadcasts() {
mBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Constants.ACTION_RESTART_APP -> {
showDialog(getString(R.string.you_being_auto_logout), "OK", "")
}
}
}
}
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this)
val restartIntentFilter = IntentFilter(Constants.ACTION_RESTART_APP)
mLocalBroadcastManager!!.registerReceiver(mBroadcastReceiver!!, restartIntentFilter)
}
Unfortunately, when I schedule this service to start at midnight, it doesn't run. When I restart device to receive BOOT_COMPLETED intent, I see Toast, that this system intent was received and service was scheduled, but it doesn't run at scheduled time. I'm not sure how to debug these system pending intents. I think there are errors happening inside a service which are hidden from my eye.
Here's my AndroidManifest part where these service/receivers are written. Seems OK (receiver is enabled manually to run even during restarts at first activity onCreate()):
...<service android:name=".services.LogoutService" />
<receiver
android:name=".services.BootReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
And finally, the LogoutService with BootReceiver:
class LogoutService : Service(), CoroutineScope {
private var coroutineJob: Job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + coroutineJob
private var mRedelivery: Boolean = false
private var mXApi: XApi? = null
private var localDatabase: LocalDatabase? = null
private var localDataSource: LocalDataSource? = null
private var remoteDataSource: RemoteDataSource? = null
private var sharedDataSource: SharedDataSource? = null
private var mainRepository: MainRepository? = null
fun setIntentRedelivery(enabled: Boolean) {
mRedelivery = enabled
}
override fun onBind(intent: Intent?): IBinder? = null
private suspend fun logoutUser() {
Logger.d("Running LogoutService logoutUser()")
withContext(coroutineContext) {
try {
val UserPin = mainRepository?.getUser()?.value?.pinCode
Logger.d("Logging out User with pin $UserPin")
if (UserPin != null) {
mainRepository?.logoutAndDeleteLocalData(UserPin)
showLogoutStatusBarNotification()
receiveRestartBroadcastIfAppRunning()
} else {
println("Cancelling coroutineJob")
coroutineJob.cancel()
}
} catch (e: Throwable) {
Logger.e(e.message!!)
coroutineJob.cancel()
}
}
if (coroutineJob.isCompleted) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
StringUtils.scheduleNextLogoutService(this)
}
onDestroy()
}
}
override fun onCreate() {
ToastUtils.updateWarning(this, getString(R.string.you_being_auto_logout))
mXApi = XApi
localDatabase = LocalDatabase.getInstance(this)
localDataSource = LocalDataSource(localDatabase!!, Dispatchers.IO)
remoteDataSource = RemoteDataSource(mXApi!!, Dispatchers.IO)
sharedDataSource = SharedDataSource(localDataSource!!, remoteDataSource!!, Dispatchers.IO, this)
this.launch {
logoutUser()
}
}
// only when started with startService() ex. from activity
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return if (mRedelivery) START_REDELIVER_INTENT else START_NOT_STICKY
}
private fun showLogoutStatusBarNotification() {
val logoutNotification = NotificationCompat.Builder(this, Constants.NOTIFICATION_AUTH_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_logo)
.setContentTitle(getString(R.string.auto_logout))
.setContentText(getString(R.string.you_being_auto_logout))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
with(NotificationManagerCompat.from(this)) {
notify(555, logoutNotification.build())
}
}
// broadcast only reaches application if it's running and has registered receiver
private fun receiveRestartBroadcastIfAppRunning() {
// this broadcast invokes dialog which restarts application on any answer
LocalBroadcastManager.getInstance(this)
.sendBroadcast(Intent(Constants.ACTION_RESTART_APP))
}
}
BootReceiver
class BootReceiver : BroadcastReceiver() {
private var alarmMgr: AlarmManager? = null
private lateinit var logoutIntent: PendingIntent
override fun onReceive(context: Context, intent: Intent) {
if (intent.action.equals(Intent.ACTION_BOOT_COMPLETED)) {
Toast.makeText(context, "Setting up logout service to run at midnight", Toast.LENGTH_LONG).show()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
StringUtils.scheduleNextLogoutService(context)
} else {
alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
logoutIntent = Intent(context, LogoutService::class.java).let {
PendingIntent.getService(context, 0, it, 0)
}
// Set the alarm to start at approximately 0:00 p.m.
val calendar: Calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
set(Calendar.HOUR_OF_DAY, 0)
}
// With setInexactRepeating(), you have to use one of the AlarmManager interval
// constants--in this case, AlarmManager.INTERVAL_DAY.
alarmMgr?.setInexactRepeating(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
AlarmManager.INTERVAL_DAY,
logoutIntent)
}
} else {
Logger.e("LogoutReceiver: not allowed intent filter called")
}
}
}
I've also tried to call BOOT_COMPLETED manually through adb shell - it shows Toast "Setting up logout service to run at midnight", so alarm is scheduled. Also, dumpsys alarm output shows that alarm is scheduled. When it comes time to run it at midnight - nothing happens. What's the solution here?
question from:
https://stackoverflow.com/questions/65940977/logout-service-at-midnight-with-both-system-and-local-broadcast-receiver