-
Notifications
You must be signed in to change notification settings - Fork 658
Expand file tree
/
Copy pathUseExitNodeWorker.kt
More file actions
143 lines (124 loc) · 5.63 KB
/
Copy pathUseExitNodeWorker.kt
File metadata and controls
143 lines (124 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package com.tailscale.ipn
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import com.tailscale.ipn.UninitializedApp.Companion.STATUS_CHANNEL_ID
import com.tailscale.ipn.UninitializedApp.Companion.STATUS_NOTIFICATION_ID
import com.tailscale.ipn.ui.localapi.Client
import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.notifier.Notifier
import kotlinx.coroutines.CoroutineScope
import kotlin.coroutines.resume
class UseExitNodeWorker(appContext: Context, workerParams: WorkerParameters) :
CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
val app = UninitializedApp.get()
val exitNodeName = inputData.getString(EXIT_NODE_NAME)
val exitNodeId =
if (exitNodeName.isNullOrEmpty()) {
null
} else {
if (!app.isAbleToStartVPN()) {
return Result.failure(
Data.Builder().putString(
ERROR_KEY, app.getString(R.string.vpn_is_not_ready_to_start)
).build()
)
}
val netmap = Notifier.netmap.value ?: return Result.failure(
Data.Builder().putString(
ERROR_KEY, app.getString(R.string.tailscale_is_not_setup)
).build()
)
val peers = netmap.Peers ?: return Result.failure(
Data.Builder().putString(
ERROR_KEY, app.getString(R.string.no_peers_found)
).build()
)
val filteredPeers = peers.filter { it.displayName == exitNodeName }.toList()
when {
filteredPeers.isEmpty() -> {
return Result.failure(
Data.Builder().putString(ERROR_KEY, app.getString(R.string.no_peers_with_name_found, exitNodeName)).build()
)
}
filteredPeers.size > 1 -> {
return Result.failure(
Data.Builder().putString(ERROR_KEY, app.getString(R.string.multiple_peers_with_name_found, exitNodeName)).build()
)
}
!filteredPeers[0].isExitNode -> {
return Result.failure(
Data.Builder().putString(ERROR_KEY, app.getString(R.string.peer_with_name_is_not_an_exit_node, exitNodeName)).build()
)
}
}
filteredPeers[0].StableID
}
val allowLanAccess = inputData.getBoolean(ALLOW_LAN_ACCESS, false)
val prefsOut = Ipn.MaskedPrefs()
prefsOut.ExitNodeID = exitNodeId
prefsOut.ExitNodeAllowLANAccess = allowLanAccess
val scope = CoroutineScope(kotlinx.coroutines.currentCoroutineContext())
val result: String? =
kotlinx.coroutines.suspendCancellableCoroutine { cont ->
Client(scope).editPrefs(prefsOut) { editResult ->
val err =
if (editResult.isFailure) {
editResult.exceptionOrNull()?.message
} else {
null
}
if (cont.isActive) {
cont.resume(err)
}
}
}
return if (result != null) {
val intent =
Intent(app, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent =
PendingIntent.getActivity(
app, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val notification =
NotificationCompat.Builder(app, STATUS_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(app.getString(R.string.use_exit_node_intent_failed))
.setContentText(result)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.build()
app.notifyStatus(notification)
Result.failure(Data.Builder().putString(ERROR_KEY, result).build())
} else {
Result.success()
}
}
override suspend fun getForegroundInfo(): ForegroundInfo {
// notification just so that there is no exception on android 11 and older (api 30 and older)
// it will be only briefly visible in the real world because the intent finishes almost instantly
// https://developer.android.com/develop/background-work/background-tasks/persistent/getting-started/define-work#backwards-compat
val app = UninitializedApp.get()
val notification =
NotificationCompat.Builder(app, STATUS_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(app.getString(R.string.changing_exit_node_notification))
.setPriority(NotificationCompat.PRIORITY_MIN)
.build()
return ForegroundInfo(STATUS_NOTIFICATION_ID, notification)
}
companion object {
const val EXIT_NODE_NAME = "EXIT_NODE_NAME"
const val ALLOW_LAN_ACCESS = "ALLOW_LAN_ACCESS"
const val ERROR_KEY = "error"
}
}