👻 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> </intent-filter>
</activity> </activity>
<service
android:name=".service.BitmessageService"
android:description="@string/bitmessage_service_description"
android:exported="false" />
<service <service
android:name=".service.ProofOfWorkService" android:name=".service.ProofOfWorkService"
android:exported="false" /> android:exported="false" />
@ -154,6 +150,11 @@
android:exported="true" android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" /> android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".service.CleanupService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service <service
android:name=".service.BatchProcessorService" android:name=".service.BatchProcessorService"
android:exported="false" /> 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.MainActivity
import ch.dissem.apps.abit.R import ch.dissem.apps.abit.R
import ch.dissem.apps.abit.service.BitmessageIntentService 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 java.util.*
import kotlin.concurrent.fixedRateTimer import kotlin.concurrent.fixedRateTimer
@ -51,9 +51,9 @@ class NetworkNotification(ctx: Context) : AbstractNotification(ctx) {
@SuppressLint("StringFormatMatches") @SuppressLint("StringFormatMatches")
private fun update(): Boolean { private fun update(): Boolean {
val running = BitmessageService.isRunning val running = NodeStartupService.isRunning
builder.setOngoing(running) builder.setOngoing(running)
val connections = BitmessageService.status.getProperty("network", "connections") val connections = NodeStartupService.status.getProperty("network", "connections")
if (!running) { if (!running) {
builder.setSmallIcon(R.drawable.ic_notification_full_node_disconnected) builder.setSmallIcon(R.drawable.ic_notification_full_node_disconnected)
builder.setContentText(ctx.getString(R.string.connection_info_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) { timer = fixedRateTimer(initialDelay = 10000, period = 10000) {
if (!update()) { if (!update()) {
cancel() cancel()
ctx.stopService(Intent(ctx, BitmessageService::class.java))
} }
super@NetworkNotification.show() 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.JobParameters
import android.app.job.JobService 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.network
import ch.dissem.apps.abit.util.preferences 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 * 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. * And stops it when the preconditions for the job (unmetered network) aren't met anymore.
*/ */
class NodeStartupService : JobService() { 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 { override fun onStartJob(params: JobParameters?): Boolean {
if (preferences.online) { 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 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 * Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes
* depending on Android version. * depending on Android version.
@ -30,4 +80,14 @@ class NodeStartupService : JobService() {
return false 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.content.Intent
import android.os.Build import android.os.Build
import ch.dissem.apps.abit.dialog.FullNodeDialogActivity 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 ch.dissem.apps.abit.service.NodeStartupService
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.concurrent.TimeUnit
val Context.network get() = NetworkUtils.getInstance(this) val Context.network get() = NetworkUtils.getInstance(this)
class NetworkUtils internal constructor(private val ctx: Context) { 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) { fun enableNode(ask: Boolean = true) {
if (ask && !ctx.preferences.connectionAllowed) { if (ask && !ctx.preferences.connectionAllowed) {
// Ask for connection // Ask for connection
@ -31,24 +34,32 @@ class NetworkUtils internal constructor(private val ctx: Context) {
} }
fun disableNode() { fun disableNode() {
ctx.stopService(Intent(ctx, BitmessageService::class.java)) jobScheduler.cancelAll()
} }
fun scheduleNodeStart() { fun scheduleNodeStart() {
val jobScheduler = ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler JobInfo.Builder(0, ComponentName(ctx, NodeStartupService::class.java)).let { builder ->
val serviceComponent = ComponentName(ctx, NodeStartupService::class.java) when {
val builder = JobInfo.Builder(0, serviceComponent) ctx.preferences.wifiOnly ->
when { builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
ctx.preferences.wifiOnly -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ->
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> else ->
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING) builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
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 { companion object {