mirror of
				https://github.com/yggdrasil-network/yggdrasil-android.git
				synced 2025-11-04 11:15:08 +03:00 
			
		
		
		
	Implemented Yggdrasil start after device boot (#39)
Implemented Yggdrasil start after device boot (even if Always-On VPN disabled).
This commit is contained in:
		
							parent
							
								
									055aab328d
								
							
						
					
					
						commit
						a07412d02d
					
				
					 7 changed files with 93 additions and 21 deletions
				
			
		| 
						 | 
				
			
			@ -5,6 +5,7 @@
 | 
			
		|||
    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 | 
			
		||||
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:name=".GlobalApplication"
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +14,8 @@
 | 
			
		|||
        android:label="@string/app_name"
 | 
			
		||||
        android:roundIcon="@drawable/ic_launcher_round"
 | 
			
		||||
        android:supportsRtl="true"
 | 
			
		||||
        android:theme="@style/Theme.Yggdrasil">
 | 
			
		||||
        android:theme="@style/Theme.Yggdrasil"
 | 
			
		||||
        android:largeHeap="true">
 | 
			
		||||
        <activity android:name=".MainActivity" android:exported="true">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.MAIN" />
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +59,12 @@
 | 
			
		|||
                <action android:name="android.service.quicksettings.action.QS_TILE" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </service>
 | 
			
		||||
 | 
			
		||||
        <receiver android:name=".BootUpReceiver" android:exported="true">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.BOOT_COMPLETED" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </receiver>
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,4 +75,5 @@
 | 
			
		|||
    <string name="location_buffalo">Баффало, США</string>
 | 
			
		||||
    <string name="channel_name">Сервис VPN</string>
 | 
			
		||||
    <string name="channel_description">Главный канал нотификаций сервиса</string>
 | 
			
		||||
    <string name="permission_notification_text">Нажмите здесь чтобы включить Yggdrasil.</string>
 | 
			
		||||
</resources>
 | 
			
		||||
| 
						 | 
				
			
			@ -75,4 +75,5 @@
 | 
			
		|||
    <string name="location_buffalo">Buffalo, US</string>
 | 
			
		||||
    <string name="channel_name">VPN Service</string>
 | 
			
		||||
    <string name="channel_description">Main channel for foreground notification</string>
 | 
			
		||||
    <string name="permission_notification_text">Tap here to enable Yggdrasil.</string>
 | 
			
		||||
</resources>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue