Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/src/dev/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<receiver
android:name=".DebugCommandReceiver"
android:exported="true"
android:permission="android.permission.DUMP" />
<activity
android:name=".gfxstream.GfxstreamPresenterProbeActivity"
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
Expand Down
30 changes: 30 additions & 0 deletions app/src/dev/java/org/randomcoder/udroid/DebugCommandReceiver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.randomcoder.udroid

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent

/** ADB-only entry point; the dev manifest requires the shell's DUMP permission. */
class DebugCommandReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.getStringExtra("command").orEmpty()
if (!DebugCommandStore.isSupported(action)) {
setResultCode(1)
setResultData("Unsupported debug command: $action")
return
}
val command =
DebugCommand(
action = action,
rootfsName = intent.getStringExtra("rootfs"),
environmentId = intent.getStringExtra("environment"),
)
if (!DebugCommandStore.enqueue(context, command)) {
setResultCode(1)
setResultData("A debug command is already pending")
return
}
setResultCode(-1)
setResultData("Queued $action")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ runtime=26.1.5-jammy
abi=arm64-v8a
source_package=mesa
mesa_commit=6a02618ccf6c5651ecb9cccbde571eb61fd73592
bin/zink_check_requirements.sha256=690177f11f92a12ab413b4548051362960c4df892ad3124a89c7ad2e6b16d438
lib/dri/zink_dri.so.sha256=a85c32422ab9c0b1b98495fc2274d8bfc7c6892f02496d60f884023ca45ccc41
lib/libEGL_mesa.so.0.sha256=b6b2e58a5e2b0b1aa6687891272625c89d727166b326cdf67cc2f040ae1cc484
lib/libEGL_mesa.so.0.sha256=013607840e63dc6cbca87e3ca3126f46734ec102b2072b5757d509615a3a1a46
lib/libGLX_mesa.so.0.sha256=36bad44d10cdb8610cbdfce7c532c4520acd4f1a9f9e92943e66606cc530b2fb
lib/libgallium-26.1.5.so.sha256=1a190093d84f64372c9ae1b001b4adc0d226b308486dc21d8565c53f2a1df75b
lib/libvulkan_virtio.so.sha256=82b57a2c7b7ea71e2cef92919a9529c5ff2dcda9bc7f53ed1bdf38bc81ca6e47
lib/libgallium-26.1.5.so.sha256=00de405a69fab61c49f0dbbe6a13c0b8332ffb664f20ed021baba0b713d2f06c
lib/libvulkan_virtio.so.sha256=9a21b21f90484960b3a3383144e7bd65bb725ede546ae4ce205f978b894f54a1
share/doc/license.rst.sha256=0d1a0472ecc81830e75c20d59b0ea02841e3db21255e0ebad97ab682c54d6615
share/drirc.d/00-mesa-defaults.conf.sha256=1406e018d5a83dacc9dceb5cecdc47410ad7f31a573390ec8c41c729e23c038a
share/glvnd/egl_vendor.d/50_mesa.json.sha256=cd3881df4227c13ca7f9cfe6c78820f21d5f7a00a41b299214f6f827e63be369
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
format=1
runtime=1.3.0-6
runtime=1.3.0-7
abi=arm64-v8a
source_package=virglrenderer-android
termux_packages_commit=978cdac5bdeb47f6061996240b65fc4e4dff11df
source_deb_sha256=847839a8cac5a349052e396f28bfe68061791e34901d4413b490e4555701399f
bin/virgl_test_server_android.sha256=c4fa02e432f2edbf974c8bf85922b6d1f7418be633bce443e5b1893c312273aa
libexec/virgl_render_server.sha256=e93c0082b7c82a8ec4c54c621e9d4c0e12fccbc7049f77b92398126199418c9e
lib/libepoxy.so.sha256=7b50fe1ef66cfea354f398523169bdb4ac8ec3b2715ac77073540bd5a1b3075f
lib/libvirglrenderer.so.sha256=15afb2811333f6947e29ebb498738bc9523708aeb05dbc9ae6630a708666798c
termux_packages_commit=d9696948261f136ccb9826a047c432f526dc99ba
source_deb_sha256=11595cd177b64c783b0ed6d3669e26b6bbd62f39e0426f1dbaf85d1a035f1c12
bin/virgl_test_server_android.sha256=62855b51d45eb8200c19ad3710bd6e426d4e19ff5dd28061e9c094b741f5b468
libexec/virgl_render_server.sha256=659544b68466d8af6ed85bdfd2f126903b9f36ebc6968dad034de0075b7de09c
lib/libepoxy.so.sha256=a50c278d336031d07a5e0ab43b7868496f71e56c7bf908daa0ada7bc32f0d5d8
lib/libvirglrenderer.so.sha256=af425d45e96916d36417f45157f78518189345ff4a5611d97f5ef3ae28a89c86
share/doc/COPYING-gl4es.sha256=69a3049c77abd88658f6ad7cf6a5b6ceb5cf03b80e60f7c4657d90322cfab7bd
share/doc/COPYING-libepoxy.sha256=8d5144666f9c4df9bbd69b8900086d5979259152a1060421cdcc0fb9061a1c12
share/doc/COPYING-virglrenderer.sha256=3b32caf16e2402c2aff2b716858713f3e1feded85c25e71bf829816ee5009402
Binary file not shown.
Binary file modified app/src/main/assets/runtime/arm64-v8a/virgl-host/lib/libepoxy.so
Binary file not shown.
Binary file not shown.
Binary file not shown.
57 changes: 57 additions & 0 deletions app/src/main/java/org/randomcoder/udroid/DebugCommandStore.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.randomcoder.udroid

import android.content.Context

internal data class DebugCommand(
val action: String,
val rootfsName: String? = null,
val environmentId: String? = null,
)

internal object DebugCommandStore {
private const val PREFERENCES_NAME = "adb-debug-command"
private const val MAX_AGE_MS = 30_000L
private val actions =
setOf(
"open-system",
"open-desktop",
"start-terminal",
"stop-terminal",
"start-desktop",
"stop-desktop",
)

fun isSupported(action: String): Boolean = action in actions

@Synchronized
fun enqueue(context: Context, command: DebugCommand): Boolean {
require(isSupported(command.action))
val preferences = preferences(context)
val age = System.currentTimeMillis() - preferences.getLong("created-at", 0L)
if (preferences.contains("action") && age in 0..MAX_AGE_MS) return false
return preferences.edit()
.putString("action", command.action)
.putString("rootfs", command.rootfsName)
.putString("environment", command.environmentId)
.putLong("created-at", System.currentTimeMillis())
.commit()
}

@Synchronized
fun take(context: Context): DebugCommand? {
val preferences = preferences(context)
val action = preferences.getString("action", null) ?: return null
val age = System.currentTimeMillis() - preferences.getLong("created-at", 0L)
val command =
DebugCommand(
action = action,
rootfsName = preferences.getString("rootfs", null),
environmentId = preferences.getString("environment", null),
)
check(preferences.edit().clear().commit()) { "Could not clear the debug command" }
return command.takeIf { age in 0..MAX_AGE_MS && isSupported(action) }
}

private fun preferences(context: Context) =
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
}
76 changes: 76 additions & 0 deletions app/src/main/java/org/randomcoder/udroid/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ class MainActivity : ComponentActivity() {
private var pendingDesktopStart: PendingDesktopStart? = null
private var pendingShortcutRootfsName: String? = null
private var pendingTerminalRootfsName: String? = null
private var pendingDebugDesktop: DebugCommand? = null
private var pendingDebugStopDesktop = false
private var pendingMicrophoneRootfsName: String? = null
private var linuxApplicationsLoadGeneration = 0L
private var desktopScanGeneration = 0L
Expand Down Expand Up @@ -179,6 +181,10 @@ class MainActivity : ComponentActivity() {
(binder as? RuntimeSupervisorService.RuntimeBinder)?.service()
runtimeService = connectedService
refreshFromDisk()
if (pendingDebugStopDesktop) {
pendingDebugStopDesktop = false
connectedService?.stopDesktop()
}
launchPendingLinuxApplication()
launchPendingDesktop()
if (snapshot.desiredRunning && connectedService?.currentTerminalSession() == null) {
Expand Down Expand Up @@ -319,6 +325,7 @@ class MainActivity : ComponentActivity() {
handleUpdateIntent(intent)
handleInstallerIntent(intent)
if (!handleShortcutIntent(intent)) loadLinuxApplications()
handleDebugCommand()
}

override fun onNewIntent(intent: Intent) {
Expand All @@ -327,6 +334,7 @@ class MainActivity : ComponentActivity() {
handleUpdateIntent(intent)
handleInstallerIntent(intent)
handleShortcutIntent(intent)
handleDebugCommand()
}

override fun onStart() {
Expand Down Expand Up @@ -979,6 +987,7 @@ class MainActivity : ComponentActivity() {
"${environments.size} desktop session" +
if (environments.size == 1) " detected" else "s detected"
}
launchPendingDebugDesktop(rootfsName)
},
onFailure = {
desktopEnvironments = emptyList()
Expand All @@ -989,11 +998,78 @@ class MainActivity : ComponentActivity() {
touchScaleEnabled = true,
)
desktopScanMessage = it.message ?: "Desktop detection failed"
if (pendingDebugDesktop?.rootfsName == rootfsName) {
debugCommandError(desktopScanMessage ?: "Desktop detection failed")
}
pendingDebugDesktop = null
},
)
}
}

private fun handleDebugCommand() {
if (!BuildConfig.DEBUG) return
val command = DebugCommandStore.take(this) ?: return
val rootfsName = command.rootfsName ?: installedRootfsName
app.journal.append(
component = "debug",
severity = "info",
event = "adb_command",
message = command.action,
bootId = snapshot.bootId,
fields = mapOf("rootfs" to rootfsName, "environment" to command.environmentId),
)
if (command.action in setOf("open-system", "start-terminal", "start-desktop") &&
installedRootfses.none { it.name == rootfsName }
) {
debugCommandError("Linux system ${rootfsName ?: "(none)"} is not installed")
return
}
when (command.action) {
"open-system" -> openSystemDetails(rootfsName!!)
"open-desktop" -> selectDestination(UdroidDestination.DESKTOP)
"start-terminal" -> openRootfsTerminal(rootfsName!!)
"stop-terminal" -> RuntimeSupervisorService.stop(this)
"start-desktop" -> {
pendingDebugDesktop = command.copy(rootfsName = rootfsName)
openSystemDetails(rootfsName!!)
}
"stop-desktop" -> {
if (runtimeService == null) pendingDebugStopDesktop = true
else runtimeService?.stopDesktop()
}
}
}

private fun launchPendingDebugDesktop(rootfsName: String) {
val command = pendingDebugDesktop?.takeIf { it.rootfsName == rootfsName } ?: return
pendingDebugDesktop = null
val requestedEnvironment = command.environmentId
if (requestedEnvironment != null) {
if (desktopEnvironments.none { it.id == requestedEnvironment }) {
debugCommandError("Desktop $requestedEnvironment is not installed in $rootfsName")
return
}
selectDesktopEnvironment(requestedEnvironment)
}
if (desktopEnvironments.isEmpty()) {
debugCommandError("No desktop session is installed in $rootfsName")
return
}
startSelectedDesktop()
selectDestination(UdroidDestination.DESKTOP)
}

private fun debugCommandError(message: String) {
app.journal.append(
component = "debug",
severity = "error",
event = "adb_command_rejected",
message = message,
bootId = snapshot.bootId,
)
}

private fun selectDesktopEnvironment(environmentId: String) {
val rootfsName = selectedSystemRootfsName ?: return
val environment = desktopEnvironments.firstOrNull { it.id == environmentId } ?: return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,18 @@ enum class DesktopGraphicsProfile(
VIRGL("virgl"),
VIRGL_ANGLE("virgl-angle"),
VENUS_EXPERIMENTAL("venus-experimental"),
SPHAL_EXPERIMENTAL("sphal-experimental"),
GFXSTREAM_EXPERIMENTAL("gfxstream-experimental"),
;

companion object {
fun fromStorage(value: String?): DesktopGraphicsProfile =
fun fromStorage(
value: String?,
sphalEnabled: Boolean = false,
): DesktopGraphicsProfile =
entries.firstOrNull {
it.storageValue == value &&
(it != SPHAL_EXPERIMENTAL || sphalEnabled) &&
(it != GFXSTREAM_EXPERIMENTAL || GFXSTREAM_PROFILE_ENABLED)
} ?: STANDARD
}
Expand Down Expand Up @@ -107,8 +112,9 @@ data class DesktopSessionSnapshot(
)

class DesktopConfigurationStore(context: Context) {
private val context = context.applicationContext
private val preferences =
context.applicationContext
this.context
.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)

fun load(
Expand Down Expand Up @@ -143,6 +149,7 @@ class DesktopConfigurationStore(context: Context) {
graphicsProfile =
DesktopGraphicsProfile.fromStorage(
preferences.getString(key(rootfsName, KEY_GRAPHICS_PROFILE), null),
SphalGraphicsSupport.isEnabled(context),
),
virglServerMode =
VirglServerMode.fromStorage(
Expand Down Expand Up @@ -186,6 +193,7 @@ class DesktopConfigurationStore(context: Context) {
fun loadGraphicsProfile(rootfsName: String): DesktopGraphicsProfile =
DesktopGraphicsProfile.fromStorage(
preferences.getString(key(rootfsName, KEY_GRAPHICS_PROFILE), null),
SphalGraphicsSupport.isEnabled(context),
)

fun loadVirglServerMode(rootfsName: String): VirglServerMode =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal data class GraphicsDeviceSupport(
val openGlEsVersion: Int,
val vulkanVersion: Int,
val hardwareBuffer: Boolean,
val vendorVulkanHal: Boolean,
)

internal data class GraphicsGuestSupport(
Expand All @@ -26,6 +27,7 @@ internal data class GraphicsGuestSupport(
val vulkanIcd: Boolean,
val virgl: Boolean,
val venus: Boolean,
val sphal: Boolean,
)

object GraphicsProfileCompatibilityProbe {
Expand Down Expand Up @@ -75,6 +77,14 @@ object GraphicsProfileCompatibilityProbe {
device.hardwareBuffer to "This device cannot allocate GPU hardware buffers",
guest.venus to "A compatible Mesa Venus driver is not available in Linux",
),
DesktopGraphicsProfile.SPHAL_EXPERIMENTAL to
firstFailure(
device.arm64 to "SPHAL is packaged only for arm64 devices",
isVulkan11(device.vulkanVersion) to "SPHAL requires Android Vulkan 1.1",
device.hardwareBuffer to "This device cannot allocate GPU hardware buffers",
device.vendorVulkanHal to "No unambiguous Android vendor Vulkan driver was found",
guest.sphal to "Install the SPHAL guest runtime and patched Mesa in Linux",
),
DesktopGraphicsProfile.GFXSTREAM_EXPERIMENTAL to
require(GFXSTREAM_PROFILE_ENABLED, "gfxstream is not enabled in this build"),
)
Expand Down Expand Up @@ -105,6 +115,7 @@ object GraphicsProfileCompatibilityProbe {
openGlEsVersion = openGlEsVersion,
vulkanVersion = vulkanVersion,
hardwareBuffer = hardwareBuffer,
vendorVulkanHal = SphalGraphicsSupport.findVulkanHal() != null,
)
}

Expand Down Expand Up @@ -147,6 +158,7 @@ object GraphicsProfileCompatibilityProbe {
vulkanIcd = icds.isNotEmpty(),
virgl = hasDriDriver("virtio_gpu_dri.so"),
venus = VenusGuestRuntimeInstaller.requiresBundledMesa(rootfs) || systemVenus,
sphal = SphalGraphicsSupport.isGuestRuntimeAvailable(rootfs),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ internal class EnvironmentProotLaunchProfile private constructor(
DesktopGraphicsProfile.VIRGL -> null
DesktopGraphicsProfile.VIRGL_ANGLE -> null
DesktopGraphicsProfile.VENUS_EXPERIMENTAL -> null
DesktopGraphicsProfile.SPHAL_EXPERIMENTAL -> null
DesktopGraphicsProfile.GFXSTREAM_EXPERIMENTAL -> null
}
}
Expand Down
Loading
Loading