👻 Clean up services

This commit is contained in:
Christian Basler 2018-08-21 07:39:03 +02:00
parent ccfdff7fd8
commit 6128fd32f9
6 changed files with 113 additions and 135 deletions

View File

@ -118,10 +118,6 @@
</intent-filter>
</activity>
<service
android:name=".service.BitmessageService"
android:description="@string/bitmessage_service_description"
android:exported="false" />
<service
android:name=".service.ProofOfWorkService"
android:exported="false" />
@ -154,6 +150,11 @@
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".service.CleanupService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".service.BatchProcessorService"
android:exported="false" />

View File

@ -25,7 +25,7 @@ import android.support.v4.app.NotificationCompat
import ch.dissem.apps.abit.MainActivity
import ch.dissem.apps.abit.R
import ch.dissem.apps.abit.service.BitmessageIntentService
import ch.dissem.apps.abit.service.BitmessageService
import ch.dissem.apps.abit.service.NodeStartupService
import java.util.*
import kotlin.concurrent.fixedRateTimer
@ -51,9 +51,9 @@ class NetworkNotification(ctx: Context) : AbstractNotification(ctx) {
@SuppressLint("StringFormatMatches")
private fun update(): Boolean {
val running = BitmessageService.isRunning
val running = NodeStartupService.isRunning
builder.setOngoing(running)
val connections = BitmessageService.status.getProperty("network", "connections")
val connections = NodeStartupService.status.getProperty("network", "connections")
if (!running) {
builder.setSmallIcon(R.drawable.ic_notification_full_node_disconnected)
builder.setContentText(ctx.getString(R.string.connection_info_disconnected))
@ -112,7 +112,6 @@ class NetworkNotification(ctx: Context) : AbstractNotification(ctx) {
timer = fixedRateTimer(initialDelay = 10000, period = 10000) {
if (!update()) {
cancel()
ctx.stopService(Intent(ctx, BitmessageService::class.java))
}
super@NetworkNotification.show()
}

View File

@ -1,111 +0,0 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.service
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.os.Handler
import ch.dissem.apps.abit.notification.NetworkNotification
import ch.dissem.apps.abit.util.preferences
import ch.dissem.bitmessage.BitmessageContext
import ch.dissem.bitmessage.utils.Property
import org.jetbrains.anko.doAsync
/**
* Define a Service that returns an IBinder for the
* sync adapter class, allowing the sync adapter framework to call
* onPerformSync().
*/
class BitmessageService : Service() {
private val bmc: BitmessageContext by lazy { Singleton.getBitmessageContext(this) }
private lateinit var notification: NetworkNotification
private val connectivityReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
if (bmc.isRunning() && !preferences.connectionAllowed) {
bmc.shutdown()
}
}
}
private val cleanupHandler = Handler()
private val cleanupTask: Runnable = object : Runnable {
override fun run() {
bmc.cleanup()
if (isRunning) {
cleanupHandler.postDelayed(this, 24 * 60 * 60 * 1000L) // once a day
}
}
}
override fun onCreate() {
registerReceiver(
connectivityReceiver,
IntentFilter().apply {
addAction(ConnectivityManager.CONNECTIVITY_ACTION)
addAction(Intent.ACTION_BATTERY_CHANGED)
}
)
notification = NetworkNotification(this)
running = false
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (!isRunning) {
running = true
notification.connecting()
if (!bmc.isRunning()) {
bmc.startup()
}
notification.show()
cleanupHandler.postDelayed(cleanupTask, 24 * 60 * 60 * 1000L)
}
return Service.START_STICKY
}
override fun onDestroy() {
if (bmc.isRunning()) {
bmc.shutdown()
}
running = false
notification.showShutdown()
cleanupHandler.removeCallbacks(cleanupTask)
doAsync {
bmc.cleanup()
}
unregisterReceiver(connectivityReceiver)
stopSelf()
}
override fun onBind(intent: Intent) = null
companion object {
@Volatile
private var running = false
val isRunning: Boolean
get() = running && Singleton.bitmessageContext?.isRunning() == true
val status: Property
get() = Singleton.bitmessageContext?.status() ?: Property("bitmessage context")
}
}

View File

@ -0,0 +1,18 @@
package ch.dissem.apps.abit.service
import android.app.job.JobParameters
import android.app.job.JobService
import org.jetbrains.anko.doAsync
class CleanupService : JobService() {
override fun onStartJob(params: JobParameters?): Boolean {
doAsync {
Singleton.getBitmessageContext(this@CleanupService).cleanup()
jobFinished(params, false)
}
return true
}
override fun onStopJob(params: JobParameters?) = false
}

View File

@ -2,8 +2,17 @@ package ch.dissem.apps.abit.service
import android.app.job.JobParameters
import android.app.job.JobService
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import ch.dissem.apps.abit.notification.NetworkNotification
import ch.dissem.apps.abit.util.network
import ch.dissem.apps.abit.util.preferences
import ch.dissem.bitmessage.BitmessageContext
import ch.dissem.bitmessage.utils.Property
import org.jetbrains.anko.doAsync
/**
* Starts the full node if
@ -13,14 +22,55 @@ import ch.dissem.apps.abit.util.preferences
* And stops it when the preconditions for the job (unmetered network) aren't met anymore.
*/
class NodeStartupService : JobService() {
private val bmc: BitmessageContext by lazy { Singleton.getBitmessageContext(this) }
private lateinit var notification: NetworkNotification
private val connectivityReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
if (bmc.isRunning() && !preferences.connectionAllowed) {
bmc.shutdown()
}
}
}
override fun onStartJob(params: JobParameters?): Boolean {
if (preferences.online) {
network.scheduleNodeStart()
registerReceiver(
connectivityReceiver,
IntentFilter().apply {
addAction(ConnectivityManager.CONNECTIVITY_ACTION)
addAction(Intent.ACTION_BATTERY_CHANGED)
}
)
notification = NetworkNotification(this)
NodeStartupService.running = false
if (!isRunning) {
running = true
notification.connecting()
if (!bmc.isRunning()) {
bmc.startup()
}
notification.show()
}
}
return true
}
override fun onDestroy() {
if (bmc.isRunning()) {
bmc.shutdown()
}
running = false
notification.showShutdown()
doAsync {
bmc.cleanup()
}
unregisterReceiver(connectivityReceiver)
stopSelf()
}
/**
* Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes
* depending on Android version.
@ -30,4 +80,14 @@ class NodeStartupService : JobService() {
return false
}
companion object {
@Volatile
private var running = false
val isRunning: Boolean
get() = running && Singleton.bitmessageContext?.isRunning() == true
val status: Property
get() = Singleton.bitmessageContext?.status() ?: Property("bitmessage context")
}
}

View File

@ -8,14 +8,17 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import ch.dissem.apps.abit.dialog.FullNodeDialogActivity
import ch.dissem.apps.abit.service.BitmessageService
import ch.dissem.apps.abit.service.CleanupService
import ch.dissem.apps.abit.service.NodeStartupService
import java.lang.ref.WeakReference
import java.util.concurrent.TimeUnit
val Context.network get() = NetworkUtils.getInstance(this)
class NetworkUtils internal constructor(private val ctx: Context) {
private val jobScheduler by lazy { ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler }
fun enableNode(ask: Boolean = true) {
if (ask && !ctx.preferences.connectionAllowed) {
// Ask for connection
@ -31,24 +34,32 @@ class NetworkUtils internal constructor(private val ctx: Context) {
}
fun disableNode() {
ctx.stopService(Intent(ctx, BitmessageService::class.java))
jobScheduler.cancelAll()
}
fun scheduleNodeStart() {
val jobScheduler = ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val serviceComponent = ComponentName(ctx, NodeStartupService::class.java)
val builder = JobInfo.Builder(0, serviceComponent)
when {
ctx.preferences.wifiOnly ->
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ->
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING)
else ->
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
JobInfo.Builder(0, ComponentName(ctx, NodeStartupService::class.java)).let { builder ->
when {
ctx.preferences.wifiOnly ->
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ->
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING)
else ->
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
}
builder.setRequiresCharging(ctx.preferences.requireCharging)
builder.setPersisted(true)
jobScheduler.schedule(builder.build())
}
JobInfo.Builder(1, ComponentName(ctx, CleanupService::class.java)).let { builder ->
builder.setPeriodic(TimeUnit.DAYS.toMillis(1))
builder.setRequiresDeviceIdle(true)
builder.setRequiresCharging(true)
jobScheduler.schedule(builder.build())
}
builder.setRequiresCharging(ctx.preferences.requireCharging)
builder.setPersisted(true)
jobScheduler.schedule(builder.build())
}
companion object {