local: MaliBridge rc4, auto local DNS, KWin render node

This commit is contained in:
openMali committed 2026-11-01 15:17:41 +08:00
1 parent 69ffc0966b
commit 9419a19c66
11 files changed
+86 -14

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
# Copy to .signing.env, fill in, chmod 600 .signing.env
RELEASE_KEYSTORE=/path/to/release.p12
RELEASE_STORE_PASSWORD=change-me
RELEASE_KEY_ALIAS=droiddeck
+1 -2
View File
@@ -219,8 +219,7 @@ android {
jniLibs {
useLegacyPackaging = true
pickFirsts += ['**/libtermux.so']
// Shipped in the proot and tracking packs (tools/packs) and run or loaded from there.
excludes += ['**/libproot.so', '**/libproot-loader.so', '**/libopencv_java4.so', '**/libmediapipe_tasks_jni.so']
excludes += ['**/libopencv_java4.so', '**/libmediapipe_tasks_jni.so']
}
}
@@ -65,7 +65,7 @@ internal class DriverMenus(private val activity: Activity, private val ui: Handl
}
fun state() = GpuDriversState(
gpuName = gpu.displayName(activity), gpuFamily = gpu.family.label(activity), soc = gpu.soc, supportText = gpu.supportText(activity),
gpuName = gpu.displayName(activity), gpuFamily = gpu.family.label(activity), soc = gpu.soc, supportText = gpu.supportText(activity, com.droiddeck.launcher.core.DeviceSupport.vkBridge(activity)),
supported = gpu.support == GpuInfo.Support.TESTED, unsupported = gpu.support == GpuInfo.Support.UNSUPPORTED,
auto = mode == SessionPrefs.GPU_DRIVERS_AUTO, pairs = pairRows, busy = pairBusy, percent = pairPercent,
autoStatus = autoStatus, releaseStatus = releaseStatus.ifEmpty { activity.getString(R.string.drivers_not_checked) }, checking = releaseChecking,
@@ -264,7 +264,7 @@ internal class DriverMenus(private val activity: Activity, private val ui: Handl
return
}
if (DriverPairs.recommendedKey(gpu, emptyList()) == null) {
autoStatus = activity.getString(R.string.drivers_none_to_set, gpu.supportText(activity).lowercase())
autoStatus = activity.getString(R.string.drivers_none_to_set, gpu.supportText(activity, com.droiddeck.launcher.core.DeviceSupport.vkBridge(activity)).lowercase())
return
}
releaseChecking = true
@@ -209,7 +209,7 @@ object DeviceReport {
h("Drivers")
val gpu = com.droiddeck.launcher.gpu.GpuInfo.detect()
k("GPU", "${gpu.name} · ${gpu.family.label} · ${gpu.supportText}" + if (gpu.oneUi8Gen2) " · One UI 8 Gen 2" else "")
k("GPU", "${gpu.name} · ${gpu.family.label} · ${gpu.supportText(SessionPrefs.vkBridge(context))}" + if (gpu.oneUi8Gen2) " · One UI 8 Gen 2" else "")
if (gpu.model == 740) {
val hint = com.droiddeck.launcher.gpu.TpUbwcHint
k("TP UBWC hint", hint.forThisDevice(context) ?: "driver default")
@@ -16,7 +16,7 @@ object DeviceInfo {
val vk = VulkanInfo.query()
val gpu = GpuInfo.detect()
return listOf(
chip(), gpu(gpu), cpu(), memory(context), system(), vulkan(context, vk), drivers(context, gpu),
chip(), gpu(context, gpu), cpu(), memory(context), system(), vulkan(context, vk), drivers(context, gpu),
).filter { it.rows.isNotEmpty() }
}
@@ -51,7 +51,7 @@ object DeviceInfo {
add("Family", read("/sys/devices/soc0/family"))
})
private fun gpu(gpu: GpuInfo) = Section("GPU", buildList<Pair<String, String>> {
private fun gpu(context: Context, gpu: GpuInfo) = Section("GPU", buildList<Pair<String, String>> {
add("GPU", gpu.name)
add("Family", gpu.family.label)
add("Model found from", when (gpu.modelSource) {
@@ -66,7 +66,7 @@ object DeviceInfo {
add("Clock range", gpuRange())
add("Load", read("$kgsl/gpu_busy_percentage")?.let { if (it.endsWith("%")) it else "$it %" })
add("Temperature", read("$kgsl/temp")?.toLongOrNull()?.let { if (it > 1000) "${it / 1000} °C" else "$it °C" })
add("Support", gpu.supportText)
add("Support", gpu.supportText(SessionPrefs.vkBridge(context)))
})
private fun cpu() = Section("CPU", buildList<Pair<String, String>> {
@@ -53,6 +53,18 @@ data class GpuInfo(
Support.UNSUPPORTED -> "Not supported: DroidDeck needs an Adreno (Snapdragon) GPU"
}
fun supportText(bridge: Boolean): String = when {
support != Support.UNSUPPORTED -> supportText
bridge -> "Runs through the Mali bridge, on the phone's own Vulkan driver"
else -> "Not supported with the Mali bridge off: Turnip needs an Adreno (Snapdragon) GPU"
}
fun supportText(context: Context, bridge: Boolean): String = when {
support != Support.UNSUPPORTED -> supportText(context)
bridge -> context.getString(R.string.gpuinfo_bridge)
else -> context.getString(R.string.gpuinfo_bridge_off)
}
fun supportText(context: Context): String = context.getString(when (support) {
Support.TESTED -> R.string.gpuinfo_supported
Support.UNTESTED -> when {
@@ -251,12 +251,36 @@ class LinuxNetworkLinkComponent(
}
}
private fun localDnsServers(): List<String> {
val servers = LinkedHashSet<String>()
for (network in connectivity.allNetworks) {
val capabilities = connectivity.getNetworkCapabilities(network) ?: continue
if (!capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) continue
if (!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) continue
val link = connectivity.getLinkProperties(network) ?: continue
for (address in link.dnsServers) {
if (address.isLinkLocalAddress || address.isLoopbackAddress || address.isAnyLocalAddress) continue
address.hostAddress?.substringBefore('%')?.let { servers.add(it) }
}
link.routes.firstOrNull { it.isDefaultRoute }?.gateway?.let { gateway ->
if (gateway is Inet4Address && !gateway.isLoopbackAddress && !gateway.isAnyLocalAddress) {
gateway.hostAddress?.substringBefore('%')?.let { servers.add(it) }
}
}
}
return servers.sortedWith(compareBy<String>(
{ if (it.startsWith("192.168.") || it.startsWith("10.")) 0 else 1 },
{ if (it.contains(':')) 1 else 0 },
)).take(6)
}
private fun writeResolver(properties: LinkProperties?) {
val own = properties?.dnsServers.orEmpty()
.filter { !it.isLinkLocalAddress && !it.isLoopbackAddress && !it.isAnyLocalAddress }
.sortedBy { if (it is Inet4Address) 0 else 1 }
.mapNotNull { it.hostAddress?.substringBefore('%') }
val servers = (own + listOf("8.8.8.8", "1.1.1.1", "2001:4860:4860::8888")).distinct().take(6)
val servers = (localDnsServers() + own + listOf("8.8.8.8", "1.1.1.1", "2001:4860:4860::8888")).distinct().take(6)
val text = buildString {
append("# Written by the app from the device's active network; edits are overwritten.\n")
for (server in servers) append("nameserver $server\n")
@@ -65,6 +65,7 @@ public final class LinuxRuntime {
private static final String HOST_DIR = "opt/android-host";
/** The downloaded add-on pack. The APK does not ship this binary. */
public static final String PROOT_PACK = "add-on pack";
public static final String PROOT_PACKAGED = "packaged";
/** Termux's unpatched proot inside the rootfs. Developer switch only. */
public static final String PROOT_ROOTFS = "rootfs fallback";
public static final String PROOT_MISSING = "missing";
@@ -81,18 +82,32 @@ public final class LinuxRuntime {
return rootfsProot(context).isFile() && rootfsLoader(context).isFile();
}
private static File packagedProot(Context context) {
return new File(context.getApplicationInfo().nativeLibraryDir, "libproot.so");
}
private static File packagedLoader(Context context) {
return new File(context.getApplicationInfo().nativeLibraryDir, "libproot-loader.so");
}
private static boolean packagedProotReady(Context context) {
return packagedProot(context).isFile() && packagedLoader(context).isFile();
}
/** Patched proot is the add-on pack. The rootfs copy is not that binary. */
public static boolean patchedProot(Context context) {
return AddonPack.PROOT.installed(context);
}
public static File prootBinary(Context context) {
if (packagedProotReady(context)) return packagedProot(context);
if (AddonPack.PROOT.installed(context)) return AddonPack.PROOT.file(context, "libproot.so");
if (SessionPrefs.allowStockProot(context)) return rootfsProot(context);
return AddonPack.PROOT.file(context, "libproot.so");
}
public static File prootLoader(Context context) {
if (packagedProotReady(context)) return packagedLoader(context);
if (AddonPack.PROOT.installed(context)) return AddonPack.PROOT.file(context, "libproot-loader.so");
if (SessionPrefs.allowStockProot(context)) return rootfsLoader(context);
return AddonPack.PROOT.file(context, "libproot-loader.so");
@@ -102,6 +117,8 @@ public final class LinuxRuntime {
public static String prootSource(Context context) {
File bin = prootBinary(context);
if (!bin.isFile()) return PROOT_MISSING;
if (packagedProotReady(context)
&& bin.getPath().equals(packagedProot(context).getPath())) return PROOT_PACKAGED;
if (AddonPack.PROOT.installed(context)
&& bin.getPath().equals(AddonPack.PROOT.file(context, "libproot.so").getPath())) return PROOT_PACK;
return PROOT_ROOTFS;
@@ -113,7 +130,7 @@ public final class LinuxRuntime {
}
public static boolean isInstalled(Context context) {
return hasRootfs(context) && (patchedProot(context) || rootfsProotReady(context));
return hasRootfs(context) && (packagedProotReady(context) || patchedProot(context) || rootfsProotReady(context));
}
/** The rootfs alone: enough to read the game library, while the proot pack can still be downloading. */
@@ -284,11 +301,21 @@ public final class LinuxRuntime {
return false;
}
private static final String MALI_DEVICE = "/dev/mali0";
static final String MALI_DEVICE = "/dev/mali0";
static final String[] DRM_RENDER_NODES = {
"/dev/dri/renderD128", "/dev/dri/renderD129", "/dev/dri/renderD130", "/dev/dri/renderD131",
};
public static String gpuStandIn(Context context) {
if (new File(KGSL_DEVICE).exists()) return KGSL_DEVICE;
if (com.droiddeck.launcher.core.DeviceSupport.INSTANCE.vkBridge(context) && new File(MALI_DEVICE).exists()) return MALI_DEVICE;
if (!com.droiddeck.launcher.core.DeviceSupport.INSTANCE.vkBridge(context)) return null;
return bridgeStandIn(path -> new File(path).exists());
}
static String bridgeStandIn(java.util.function.Predicate<String> exists) {
if (exists.test(MALI_DEVICE)) return MALI_DEVICE;
for (String node : DRM_RENDER_NODES) if (exists.test(node)) return node;
return null;
}
@@ -2032,6 +2032,8 @@
<string name="gpuinfo_a850_experimental">Adreno 850 实验支持:需要配套的实验版显示驱动和游戏驱动;尚未在此设备上验证</string>
<string name="gpuinfo_below_tested">低于已测试的硬件(Adreno 725 及更新):可能无法运行</string>
<string name="gpuinfo_unsupported">不受支持:DroidDeck 需要 Adreno(骁龙)GPU</string>
<string name="gpuinfo_bridge">通过 Mali 桥接运行,使用手机自带的 Vulkan 驱动</string>
<string name="gpuinfo_bridge_off">关闭 Mali 桥接时不受支持:Turnip 需要 Adreno(骁龙)GPU</string>
<string name="turnip_rate_limited">已达到 GitHub 的请求频率限制 — 请稍后重试</string>
<string name="turnip_github_http">GitHub 返回 HTTP %1$d</string>
+2
View File
@@ -2111,6 +2111,8 @@
<string name="gpuinfo_a850_experimental">Experimental Adreno 850 support: requires compatible experimental display and game drivers; not verified on this device</string>
<string name="gpuinfo_below_tested">Below tested hardware (Adreno 725 and newer): it may not run</string>
<string name="gpuinfo_unsupported">Not supported: DroidDeck needs an Adreno (Snapdragon) GPU</string>
<string name="gpuinfo_bridge">Runs through the Mali bridge, on the phone\'s own Vulkan driver</string>
<string name="gpuinfo_bridge_off">Not supported with the Mali bridge off: Turnip needs an Adreno (Snapdragon) GPU</string>
<string name="turnip_rate_limited">GitHub\'s rate limit was hit - try again later</string>
<string name="turnip_github_http">GitHub answered HTTP %1$d</string>
+4 -2
View File
@@ -107,8 +107,10 @@ fi
if [ "$(cat /usr/share/droiddeck/kwin-compositor 2>/dev/null)" = opengl ]; then
unset LIBGL_ALWAYS_SOFTWARE QT_QUICK_BACKEND
export KWIN_COMPOSE=O2 MESA_LOADER_DRIVER_OVERRIDE=zink GALLIUM_DRIVER=zink LIBGL_KOPPER_DRI2=true
if [ -z "${KWIN_RENDER_NODES:-}" ] && [ -c /dev/dri/renderD0 ]; then
export KWIN_RENDER_NODES=/dev/dri/renderD0
if [ -z "${KWIN_RENDER_NODES:-}" ]; then
for node in /dev/dri/renderD0 /dev/dri/renderD307 /dev/dri/renderD*; do
if [ -c "$node" ]; then export KWIN_RENDER_NODES="$node"; break; fi
done
fi
mkdir -p /run/droiddeck/kwin
printf '#!/bin/sh\nGALLIUM_THREAD=0 exec /usr/bin/kwin_wayland "$@"\n' > /run/droiddeck/kwin/kwin_wayland