Implemented Yggdrasil start after device boot (#39)

Implemented Yggdrasil start after device boot (even if Always-On VPN disabled).
This commit is contained in:
Revertron 2022-12-14 11:41:23 +01:00 committed by GitHub
parent 055aab328d
commit a07412d02d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 21 deletions

View file

@ -0,0 +1,40 @@
package eu.neilalexander.yggdrasil
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.util.Log
import androidx.preference.PreferenceManager
class BootUpReceiver : BroadcastReceiver() {
companion object {
const val TAG = "BootUpReceiver"
}
override fun onReceive(context: Context, intent: Intent) {
if (intent.action != Intent.ACTION_BOOT_COMPLETED) {
Log.w(TAG, "Wrong action: ${intent.action}")
}
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
if (!preferences.getBoolean(PREF_KEY_ENABLED, false)) {
Log.i(TAG, "Yggdrasil disabled, not starting service")
return
}
Log.i(TAG, "Yggdrasil enabled, starting service")
val serviceIntent = Intent(context, PacketTunnelProvider::class.java)
serviceIntent.action = PacketTunnelProvider.ACTION_START
val vpnIntent = VpnService.prepare(context)
if (vpnIntent != null) {
Log.i(TAG, "Need to ask for VPN permission")
val notification = createPermissionMissingNotification(context)
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.notify(444, notification)
} else {
context.startService(serviceIntent)
}
}
}

View file

@ -59,7 +59,7 @@ class DnsActivity : AppCompatActivity() {
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
builder.setTitle(getString(R.string.dns_add_server_dialog_title))
builder.setView(view)
builder.setPositiveButton(getString(R.string.add)) { dialog, _ ->
builder.setPositiveButton(getString(R.string.add)) { _, _ ->
val server = input.text.toString()
if (!servers.contains(server)) {
servers.add(server)

View file

@ -10,6 +10,7 @@ import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
const val PREF_KEY_ENABLED = "enabled"
const val MAIN_CHANNEL_ID = "Yggdrasil Service"
class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
private lateinit var config: ConfigurationProxy
@ -62,21 +63,7 @@ class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
}
fun createServiceNotification(context: Context, state: State): Notification {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
val channelId = "Foreground Service"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = context.getString(R.string.channel_name)
val descriptionText = context.getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_MIN
val channel = NotificationChannel(channelId, name, importance).apply {
description = descriptionText
}
// Register the channel with the system
val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
createNotificationChannels(context)
val intent = Intent(context, MainActivity::class.java).apply {
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
@ -90,11 +77,46 @@ fun createServiceNotification(context: Context, state: State): Notification {
else -> context.getText(R.string.tile_disabled)
}
return NotificationCompat.Builder(context, channelId)
return NotificationCompat.Builder(context, MAIN_CHANNEL_ID)
.setShowWhen(false)
.setContentTitle(text)
.setSmallIcon(R.drawable.ic_tile_icon)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_MIN)
.build()
}
fun createPermissionMissingNotification(context: Context): Notification {
createNotificationChannels(context)
val intent = Intent(context, MainActivity::class.java).apply {
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
return NotificationCompat.Builder(context, MAIN_CHANNEL_ID)
.setShowWhen(false)
.setContentTitle(context.getText(R.string.app_name))
.setContentText(context.getText(R.string.permission_notification_text))
.setSmallIcon(R.drawable.ic_tile_icon)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build()
}
private fun createNotificationChannels(context: Context) {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = context.getString(R.string.channel_name)
val descriptionText = context.getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_MIN
val channel = NotificationChannel(MAIN_CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// Register the channel with the system
val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}

View file

@ -20,7 +20,7 @@ import kotlin.concurrent.thread
private const val TAG = "PacketTunnelProvider"
const val SERVICE_NOTIFICATION_ID = 1000
class PacketTunnelProvider: VpnService() {
open class PacketTunnelProvider: VpnService() {
companion object {
const val STATE_INTENT = "eu.neilalexander.yggdrasil.PacketTunnelProvider.STATE_MESSAGE"
@ -68,9 +68,9 @@ class PacketTunnelProvider: VpnService() {
ACTION_CONNECT -> {
Log.d(TAG, "Connecting...")
if (started.get()) {
connect();
connect()
} else {
start();
start()
}
START_STICKY
}