Skip to content

Commit 7b07837

Browse files
author
fangsy
committed
# Conflicts: # app/src/main/java/com/github/kr328/clash/MainApplication.kt # build.gradle.kts
2 parents 68d056c + 22f12b7 commit 7b07837

18 files changed

Lines changed: 238 additions & 48 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
From 7115c480196f4bdcbdae5e14ebaa4510540680e9 Mon Sep 17 00:00:00 2001
2+
From: Brad Fitzpatrick <bradfitz@tailscale.com>
3+
Date: Tue, 27 Jan 2026 09:52:22 -0800
4+
Subject: [PATCH] [tailscale] os: disable pidfd on Android
5+
6+
Updates tailscale/tailscale#13452
7+
Updates golang/go#70508
8+
Updates tailscale/go#99
9+
---
10+
src/os/pidfd_linux.go | 10 ++++++++++
11+
1 file changed, 10 insertions(+)
12+
13+
diff --git a/src/os/pidfd_linux.go b/src/os/pidfd_linux.go
14+
index 796d8c018c7f2a..5cdbf1175e0db5 100644
15+
--- a/src/os/pidfd_linux.go
16+
+++ b/src/os/pidfd_linux.go
17+
@@ -138,6 +138,16 @@ func (p *Process) pidfdSendSignal(s syscall.Signal) error {
18+
19+
// pidfdWorks returns whether we can use pidfd on this system.
20+
func pidfdWorks() bool {
21+
+ if runtime.GOOS == "android" {
22+
+ // Tailscale-specific workaround since https://github.com/golang/go/pull/69543/commits/aad6b3b32c81795f86bc4a9e81aad94899daf520
23+
+ // does not solve https://github.com/golang/go/issues/69065 for Android apps using Go libraries.
24+
+ //
25+
+ // See: https://github.com/tailscale/tailscale/issues/13452
26+
+ //
27+
+ // For now (2025-04-09), we'll just disable pidfd
28+
+ // on all Android releases.
29+
+ return false
30+
+ }
31+
return checkPidfdOnce() == nil
32+
}
33+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
Subject: [PATCH] remove 64bits syscall on 32bit linux
2+
---
3+
Index: src/runtime/os_linux32.go
4+
IDEA additional info:
5+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
6+
<+>UTF-8
7+
===================================================================
8+
diff --git a/src/runtime/os_linux32.go b/src/runtime/os_linux32.go
9+
--- a/src/runtime/os_linux32.go (revision 030384681641464bf71ed16500075c458363510f)
10+
+++ b/src/runtime/os_linux32.go (date 1771666707318)
11+
@@ -21,14 +21,14 @@
12+
13+
//go:nosplit
14+
func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32 {
15+
- if !isFutexTime32bitOnly.Load() {
16+
- ret := futex_time64(addr, op, val, ts, addr2, val3)
17+
- // futex_time64 is only supported on Linux 5.0+
18+
- if ret != -_ENOSYS {
19+
- return ret
20+
- }
21+
- isFutexTime32bitOnly.Store(true)
22+
- }
23+
+ //if !isFutexTime32bitOnly.Load() {
24+
+ // ret := futex_time64(addr, op, val, ts, addr2, val3)
25+
+ // // futex_time64 is only supported on Linux 5.0+
26+
+ // if ret != -_ENOSYS {
27+
+ // return ret
28+
+ // }
29+
+ // isFutexTime32bitOnly.Store(true)
30+
+ //}
31+
// Downgrade ts.
32+
var ts32 timespec32
33+
var pts32 *timespec32
34+
@@ -49,14 +49,14 @@
35+
36+
//go:nosplit
37+
func timer_settime(timerid int32, flags int32, new, old *itimerspec) int32 {
38+
- if !isSetTime32bitOnly.Load() {
39+
- ret := timer_settime64(timerid, flags, new, old)
40+
- // timer_settime64 is only supported on Linux 5.0+
41+
- if ret != -_ENOSYS {
42+
- return ret
43+
- }
44+
- isSetTime32bitOnly.Store(true)
45+
- }
46+
+ //if !isSetTime32bitOnly.Load() {
47+
+ // ret := timer_settime64(timerid, flags, new, old)
48+
+ // // timer_settime64 is only supported on Linux 5.0+
49+
+ // if ret != -_ENOSYS {
50+
+ // return ret
51+
+ // }
52+
+ // isSetTime32bitOnly.Store(true)
53+
+ //}
54+
55+
var newts, oldts itimerspec32
56+
var new32, old32 *itimerspec32

.github/workflows/build-debug.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,14 @@ jobs:
2929
- name: Setup Go
3030
uses: actions/setup-go@v6
3131
with:
32-
go-version: "1.25"
32+
go-version: "1.26"
3333
check-latest: true # Always check for the latest patch release
3434

35+
- name: Apply Patches
36+
run: |
37+
cd $(go env GOROOT)
38+
for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done
39+
3540
- uses: actions/cache@v4
3641
with:
3742
path: |

.github/workflows/build-pre-release.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,14 @@ jobs:
2727
- name: Setup Go
2828
uses: actions/setup-go@v6
2929
with:
30-
go-version: "1.25"
30+
go-version: "1.26"
3131
check-latest: true # Always check for the latest patch release
3232

33+
- name: Apply Patches
34+
run: |
35+
cd $(go env GOROOT)
36+
for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done
37+
3338
- uses: actions/cache@v4
3439
with:
3540
path: |

.github/workflows/build-release.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,14 @@ jobs:
3131
- name: Setup Go
3232
uses: actions/setup-go@v6
3333
with:
34-
go-version: "1.25"
34+
go-version: "1.26"
3535
check-latest: true # Always check for the latest patch release
3636

37+
- name: Apply Patches
38+
run: |
39+
cd $(go env GOROOT)
40+
for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done
41+
3742
- uses: actions/cache@v4
3843
with:
3944
path: |

.github/workflows/update-dependencies.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,13 @@ jobs:
2424
- name: Setup Go
2525
uses: actions/setup-go@v6
2626
with:
27-
go-version: "1.25"
27+
go-version: "1.26"
2828
check-latest: true # Always check for the latest patch release
29+
30+
- name: Apply Patches
31+
run: |
32+
cd $(go env GOROOT)
33+
for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done
2934
3035
- uses: actions/cache@v4
3136
with:

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
android:configChanges="uiMode"
4747
android:exported="true"
4848
android:label="@string/launch_name"
49-
android:launchMode="singleTop">
49+
android:launchMode="singleTask">
5050
<intent-filter>
5151
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
5252
<action android:name="android.intent.action.MAIN" />
@@ -69,8 +69,10 @@
6969

7070
<activity
7171
android:name=".ExternalControlActivity"
72+
android:excludeFromRecents="true"
7273
android:exported="true"
7374
android:label="@string/external_control_activity"
75+
android:noHistory="true"
7476
android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
7577
<intent-filter>
7678
<action android:name="android.intent.action.VIEW" />

app/src/main/java/com/github/kr328/clash/ExternalControlActivity.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import com.github.kr328.clash.design.R
2525
class ExternalControlActivity : Activity(), CoroutineScope by MainScope() {
2626
override fun onCreate(savedInstanceState: Bundle?) {
2727
super.onCreate(savedInstanceState)
28+
@Suppress("DEPRECATION")
29+
overridePendingTransition(0, 0)
2830

2931
when(intent.action) {
3032
Intent.ACTION_VIEW -> {
@@ -90,4 +92,10 @@ class ExternalControlActivity : Activity(), CoroutineScope by MainScope() {
9092
stopClashService()
9193
Toast.makeText(this, R.string.external_control_stopped, Toast.LENGTH_LONG).show()
9294
}
95+
96+
override fun finish() {
97+
super.finish()
98+
@Suppress("DEPRECATION")
99+
overridePendingTransition(0, 0)
100+
}
93101
}

app/src/main/java/com/github/kr328/clash/MainApplication.kt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@ package com.github.kr328.clash
22

33
import android.app.Application
44
import android.content.Context
5+
import android.content.Intent
6+
import androidx.core.content.pm.ShortcutInfoCompat
7+
import androidx.core.content.pm.ShortcutManagerCompat
8+
import androidx.core.graphics.drawable.IconCompat
59
import com.github.kr328.clash.common.Global
610
import com.github.kr328.clash.common.compat.currentProcessName
11+
import com.github.kr328.clash.common.constants.Intents
712
import com.github.kr328.clash.common.log.Log
813
import com.github.kr328.clash.remote.Remote
914
import com.github.kr328.clash.service.util.sendServiceRecreated
1015
import com.github.kr328.clash.util.clashDir
1116
import java.io.File
1217
import java.io.FileOutputStream
18+
import com.github.kr328.clash.design.R as DesignR
1319

1420

1521
@Suppress("unused")
@@ -32,13 +38,59 @@ class MainApplication : Application() {
3238

3339
if (processName == packageName) {
3440
Remote.launch()
41+
setupShortcuts()
3542
wifiAutomator = WifiAutomator(this)
3643
wifiAutomator?.start()
3744
} else {
3845
sendServiceRecreated()
3946
}
4047
}
4148

49+
private fun setupShortcuts() {
50+
val icon = IconCompat.createWithResource(this, R.mipmap.ic_launcher)
51+
val flags = Intent.FLAG_ACTIVITY_NEW_TASK or
52+
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS or
53+
Intent.FLAG_ACTIVITY_NO_ANIMATION
54+
55+
val toggle = ShortcutInfoCompat.Builder(this, "toggle_clash")
56+
.setShortLabel(getString(DesignR.string.shortcut_toggle_short))
57+
.setLongLabel(getString(DesignR.string.shortcut_toggle_long))
58+
.setIcon(icon)
59+
.setIntent(
60+
Intent(Intents.ACTION_TOGGLE_CLASH)
61+
.setClassName(this, ExternalControlActivity::class.java.name)
62+
.addFlags(flags)
63+
)
64+
.setRank(0)
65+
.build()
66+
67+
val start = ShortcutInfoCompat.Builder(this, "start_clash")
68+
.setShortLabel(getString(DesignR.string.shortcut_start_short))
69+
.setLongLabel(getString(DesignR.string.shortcut_start_long))
70+
.setIcon(icon)
71+
.setIntent(
72+
Intent(Intents.ACTION_START_CLASH)
73+
.setClassName(this, ExternalControlActivity::class.java.name)
74+
.addFlags(flags)
75+
)
76+
.setRank(1)
77+
.build()
78+
79+
val stop = ShortcutInfoCompat.Builder(this, "stop_clash")
80+
.setShortLabel(getString(DesignR.string.shortcut_stop_short))
81+
.setLongLabel(getString(DesignR.string.shortcut_stop_long))
82+
.setIcon(icon)
83+
.setIntent(
84+
Intent(Intents.ACTION_STOP_CLASH)
85+
.setClassName(this, ExternalControlActivity::class.java.name)
86+
.addFlags(flags)
87+
)
88+
.setRank(2)
89+
.build()
90+
91+
ShortcutManagerCompat.setDynamicShortcuts(this, listOf(toggle, start, stop))
92+
}
93+
4294
override fun onTerminate() {
4395
super.onTerminate()
4496
wifiAutomator?.stop()

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ subprojects {
5858
minSdk = 21
5959
targetSdk = 35
6060

61-
versionName = "2.11.23.1"
62-
versionCode = 211023
61+
versionName = "2.11.24"
62+
versionCode = 211024
6363

6464
resValue("string", "release_name", "v$versionName")
6565
resValue("integer", "release_code", "$versionCode")

0 commit comments

Comments
 (0)