mirror of
				https://github.com/yggdrasil-network/yggdrasil-android.git
				synced 2025-11-01 01:35:06 +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.CHANGE_WIFI_MULTICAST_STATE" /> | ||||||
|     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||||||
|     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> |     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> | ||||||
|  |     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | ||||||
| 
 | 
 | ||||||
|     <application |     <application | ||||||
|         android:name=".GlobalApplication" |         android:name=".GlobalApplication" | ||||||
|  | @ -13,7 +14,8 @@ | ||||||
|         android:label="@string/app_name" |         android:label="@string/app_name" | ||||||
|         android:roundIcon="@drawable/ic_launcher_round" |         android:roundIcon="@drawable/ic_launcher_round" | ||||||
|         android:supportsRtl="true" |         android:supportsRtl="true" | ||||||
|         android:theme="@style/Theme.Yggdrasil"> |         android:theme="@style/Theme.Yggdrasil" | ||||||
|  |         android:largeHeap="true"> | ||||||
|         <activity android:name=".MainActivity" android:exported="true"> |         <activity android:name=".MainActivity" android:exported="true"> | ||||||
|             <intent-filter> |             <intent-filter> | ||||||
|                 <action android:name="android.intent.action.MAIN" /> |                 <action android:name="android.intent.action.MAIN" /> | ||||||
|  | @ -57,6 +59,12 @@ | ||||||
|                 <action android:name="android.service.quicksettings.action.QS_TILE" /> |                 <action android:name="android.service.quicksettings.action.QS_TILE" /> | ||||||
|             </intent-filter> |             </intent-filter> | ||||||
|         </service> |         </service> | ||||||
|  | 
 | ||||||
|  |         <receiver android:name=".BootUpReceiver" android:exported="true"> | ||||||
|  |             <intent-filter> | ||||||
|  |                 <action android:name="android.intent.action.BOOT_COMPLETED" /> | ||||||
|  |             </intent-filter> | ||||||
|  |         </receiver> | ||||||
|     </application> |     </application> | ||||||
| 
 | 
 | ||||||
| </manifest> | </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)) |             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.setTitle(getString(R.string.dns_add_server_dialog_title)) | ||||||
|             builder.setView(view) |             builder.setView(view) | ||||||
|             builder.setPositiveButton(getString(R.string.add)) { dialog, _ -> |             builder.setPositiveButton(getString(R.string.add)) { _, _ -> | ||||||
|                 val server = input.text.toString() |                 val server = input.text.toString() | ||||||
|                 if (!servers.contains(server)) { |                 if (!servers.contains(server)) { | ||||||
|                     servers.add(server) |                     servers.add(server) | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ import androidx.annotation.RequiresApi | ||||||
| import androidx.core.app.NotificationCompat | import androidx.core.app.NotificationCompat | ||||||
| 
 | 
 | ||||||
| const val PREF_KEY_ENABLED = "enabled" | const val PREF_KEY_ENABLED = "enabled" | ||||||
|  | const val MAIN_CHANNEL_ID = "Yggdrasil Service" | ||||||
| 
 | 
 | ||||||
| class GlobalApplication: Application(), YggStateReceiver.StateReceiver { | class GlobalApplication: Application(), YggStateReceiver.StateReceiver { | ||||||
|     private lateinit var config: ConfigurationProxy |     private lateinit var config: ConfigurationProxy | ||||||
|  | @ -62,21 +63,7 @@ class GlobalApplication: Application(), YggStateReceiver.StateReceiver { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun createServiceNotification(context: Context, state: State): Notification { | fun createServiceNotification(context: Context, state: State): Notification { | ||||||
|     // Create the NotificationChannel, but only on API 26+ because |     createNotificationChannels(context) | ||||||
|     // 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) |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     val intent = Intent(context, MainActivity::class.java).apply { |     val intent = Intent(context, MainActivity::class.java).apply { | ||||||
|         this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK |         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) |         else -> context.getText(R.string.tile_disabled) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return NotificationCompat.Builder(context, channelId) |     return NotificationCompat.Builder(context, MAIN_CHANNEL_ID) | ||||||
|         .setShowWhen(false) |         .setShowWhen(false) | ||||||
|         .setContentTitle(text) |         .setContentTitle(text) | ||||||
|         .setSmallIcon(R.drawable.ic_tile_icon) |         .setSmallIcon(R.drawable.ic_tile_icon) | ||||||
|         .setContentIntent(pendingIntent) |         .setContentIntent(pendingIntent) | ||||||
|         .setPriority(NotificationCompat.PRIORITY_MIN) |         .setPriority(NotificationCompat.PRIORITY_MIN) | ||||||
|         .build() |         .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" | private const val TAG = "PacketTunnelProvider" | ||||||
| const val SERVICE_NOTIFICATION_ID = 1000 | const val SERVICE_NOTIFICATION_ID = 1000 | ||||||
| 
 | 
 | ||||||
| class PacketTunnelProvider: VpnService() { | open class PacketTunnelProvider: VpnService() { | ||||||
|     companion object { |     companion object { | ||||||
|         const val STATE_INTENT = "eu.neilalexander.yggdrasil.PacketTunnelProvider.STATE_MESSAGE" |         const val STATE_INTENT = "eu.neilalexander.yggdrasil.PacketTunnelProvider.STATE_MESSAGE" | ||||||
| 
 | 
 | ||||||
|  | @ -68,9 +68,9 @@ class PacketTunnelProvider: VpnService() { | ||||||
|             ACTION_CONNECT -> { |             ACTION_CONNECT -> { | ||||||
|                 Log.d(TAG, "Connecting...") |                 Log.d(TAG, "Connecting...") | ||||||
|                 if (started.get()) { |                 if (started.get()) { | ||||||
|                     connect(); |                     connect() | ||||||
|                 } else { |                 } else { | ||||||
|                     start(); |                     start() | ||||||
|                 } |                 } | ||||||
|                 START_STICKY |                 START_STICKY | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -75,4 +75,5 @@ | ||||||
|     <string name="location_buffalo">Баффало, США</string> |     <string name="location_buffalo">Баффало, США</string> | ||||||
|     <string name="channel_name">Сервис VPN</string> |     <string name="channel_name">Сервис VPN</string> | ||||||
|     <string name="channel_description">Главный канал нотификаций сервиса</string> |     <string name="channel_description">Главный канал нотификаций сервиса</string> | ||||||
|  |     <string name="permission_notification_text">Нажмите здесь чтобы включить Yggdrasil.</string> | ||||||
| </resources> | </resources> | ||||||
|  | @ -75,4 +75,5 @@ | ||||||
|     <string name="location_buffalo">Buffalo, US</string> |     <string name="location_buffalo">Buffalo, US</string> | ||||||
|     <string name="channel_name">VPN Service</string> |     <string name="channel_name">VPN Service</string> | ||||||
|     <string name="channel_description">Main channel for foreground notification</string> |     <string name="channel_description">Main channel for foreground notification</string> | ||||||
|  |     <string name="permission_notification_text">Tap here to enable Yggdrasil.</string> | ||||||
| </resources> | </resources> | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Revertron
						Revertron