[Feat] (Android): the render server screen and binary ship in the plugin APK too, not just trace

This commit is contained in:
2026-09-28 22:10:56 -04:00
parent 2fa58940e4
commit 1987bdff5c
16 changed files with 128 additions and 74 deletions
+10 -1
View File
@@ -35,7 +35,10 @@ fun Project.mobileGlCmakeCompilerLauncher(): String =
// Match the native module's two switches, including INPROC implying the parent
// option. A D/P plugin must ask its launcher to select the transport at runtime;
// compiling MG_Remote alone leaves ConfigLoader's default on monolith.
// compiling MG_Remote alone leaves ConfigLoader's default on monolith. The native module
// builds the split shape by default in a standalone build (it carries the render server),
// so this is only the opt-in for the launcher-selected in-process TRANSPORT: unset, the
// plugin still runs monolith.
fun Project.mobileGlDisaggregatedEnabled(): Boolean {
fun enabled(value: Any?): Boolean = value?.toString()?.trim()?.uppercase().orEmpty() in setOf("1", "ON", "YES", "TRUE", "Y")
return enabled(findProperty("mobilegl.buildDisaggregated") ?: System.getenv("MOBILEGL_BUILD_DISAGGREGATED")) ||
@@ -172,11 +175,17 @@ android {
productFlavors {
create("plugin") {
dimension = "profile"
// The shipped renderer plugin keeps its render-server components private; the
// screen and NavBar start them from inside the app.
manifestPlaceholders["serverComponentsExported"] = "false"
}
create("trace") {
dimension = "profile"
applicationIdSuffix = ".trace"
versionNameSuffix = "-trace"
// Debuggable dev APK: adb (tcp_device_server.py, the CI TCP lane) starts the
// server components directly by explicit component name.
manifestPlaceholders["serverComponentsExported"] = "true"
}
}
@@ -1,6 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The render server (ServerControlActivity / MobileGLServerService /
MobileGLDisplayActivity): a TCP listener kept alive by a wake lock in a data-sync
foreground service. -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<application
android:hasCode="true"
android:label="${appLabel}"
@@ -22,6 +30,41 @@
android:exported="true"
android:theme="@style/Theme.MobileGLPlugin.NoDisplay" />
<!-- Render server control screen: start/stop the offscreen TCP supervisor
(MobileGLServerService) or the on-screen display server. Not a launcher: the
bottom navigation bar (NavBar) switches between it and PostActivity. Both flavors
ship the server (libMobileGLServer.so + the display server's JNI in libMobileGL.so).
serverComponentsExported: the trace flavor is a debuggable dev APK that adb drives by
explicit component (listen/token/env/onscreen/start/stop extras), so it exports
them. The plugin flavor ships to users, and an exported component would let any app
on the device start a server with an env of its own choosing. singleTop delivers the
adb extras to a running screen through onNewIntent. -->
<activity
android:name=".ServerControlActivity"
android:exported="${serverComponentsExported}"
android:launchMode="singleTop"
android:theme="@style/Theme.MobileGLPlugin" />
<service
android:name=".MobileGLServerService"
android:exported="${serverComponentsExported}"
android:foregroundServiceType="dataSync"
android:process=":mglsrv" />
<!-- P12 (on-screen server window): the ON-SCREEN display server. Its own process (:mglwin)
runs the TCP server on a thread and renders a headless client's window surface
(MOBILEGL_IPC_SURFACE=server) onto its SurfaceView. One server at a time: starting it
stops MobileGLServerService, starting the service kills :mglwin. Software window
rendering keeps HWUI off EGL here (Espryt's teardown terminates the default display);
configChanges keeps the Surface through rotation and overlay changes. -->
<activity
android:name=".MobileGLDisplayActivity"
android:configChanges="@integer/trace_replay_config_changes"
android:exported="${serverComponentsExported}"
android:hardwareAccelerated="false"
android:launchMode="singleTop"
android:process=":mglwin"
android:taskAffinity="${applicationId}.mglwin"
android:theme="@style/Theme.MobileGLPlugin" />
<!-- One benchmark run per process: the backend is latched at MobileGL
init and Espryt teardown kills the process-default EGL display, so
the bench must never share a process with the POST UI. The service
@@ -18,7 +18,7 @@ import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
/** Trace-only entry point for a TCP supervisor; EGL belongs to its session children. */
/** Entry point for the offscreen TCP supervisor; EGL belongs to its session children. */
public final class MobileGLServerService extends Service {
private static final String TAG = "MobileGLServer";
private static final String CHANNEL = "mobilegl-server";
@@ -10,11 +10,9 @@ import android.widget.LinearLayout;
import android.widget.TextView;
/**
* Bottom navigation bar switching between the dev screens (POST and the render
* server control). The bar only appears in flavors that ship both screens: the
* plugin flavor has no ServerControlActivity, so wrap() returns the content
* unchanged there. Tabs switch by reordering the other activity to the front,
* so each screen keeps its state while the other is open.
* Bottom navigation bar switching between the POST screen and the render server
* control screen. Tabs switch by reordering the other activity to the front, so each
* screen keeps its state while the other is open.
*/
public final class NavBar {
public static final int TAB_POST = 0;
@@ -25,19 +23,10 @@ public final class NavBar {
private static final int COLOR_TAB_INACTIVE = 0xFF9E9E9E;
private static final int BAR_HEIGHT_DP = 56;
private static final String SERVER_ACTIVITY = "top.mobilegl.plugin.ServerControlActivity";
private NavBar() {}
/** Returns content wrapped with the bottom bar, or content unchanged in plugin builds. */
/** Returns content wrapped with the bottom bar. */
public static View wrap(Activity activity, View content, int currentTab) {
final Class<?> serverClass;
try {
serverClass = Class.forName(SERVER_ACTIVITY);
} catch (ClassNotFoundException notInThisFlavor) {
return content;
}
LinearLayout root = new LinearLayout(activity);
root.setOrientation(LinearLayout.VERTICAL);
root.addView(content, new LinearLayout.LayoutParams(
@@ -47,7 +36,7 @@ public final class NavBar {
bar.setOrientation(LinearLayout.HORIZONTAL);
bar.setBackgroundColor(COLOR_BAR);
addTab(activity, bar, "POST", PostActivity.class, currentTab == TAB_POST);
addTab(activity, bar, "Render Server", serverClass, currentTab == TAB_SERVER);
addTab(activity, bar, "Render Server", ServerControlActivity.class, currentTab == TAB_SERVER);
root.addView(bar, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, dp(activity, BAR_HEIGHT_DP)));
return root;
@@ -4,7 +4,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
/**
* The server role's environment, shared by the two servers the trace APK can run.
* The server role's environment, shared by the two servers the APK can run.
*
* <p>{@link MobileGLServerService} exec's the offscreen supervisor with it (a ProcessBuilder
* environment); {@link MobileGLDisplayActivity} applies it to its OWN process with Os.setenv /
@@ -2,42 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<application>
<!-- Render server control screen: start/stop the offscreen TCP supervisor
(MobileGLServerService) or the on-screen display server. Not the launcher:
PostActivity is the entry icon and the bottom navigation bar switches between
the two screens; exported so adb can drive it by explicit component with
listen/token/env/onscreen/start/stop extras. -->
<activity
android:name=".ServerControlActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/Theme.MobileGLPlugin" />
<service
android:name=".MobileGLServerService"
android:exported="true"
android:foregroundServiceType="dataSync"
android:process=":mglsrv" />
<!-- P12 (on-screen server window): the ON-SCREEN display server. Its own process (:mglwin)
runs the TCP server on a thread and renders a headless client's window surface
(MOBILEGL_IPC_SURFACE=server) onto its SurfaceView. One server at a time: starting it
stops MobileGLServerService, starting the service kills :mglwin. Software window
rendering keeps HWUI off EGL here (Espryt's teardown terminates the default display);
configChanges keeps the Surface through rotation and overlay changes. -->
<activity
android:name=".MobileGLDisplayActivity"
android:configChanges="@integer/trace_replay_config_changes"
android:exported="true"
android:hardwareAccelerated="false"
android:launchMode="singleTop"
android:process=":mglwin"
android:taskAffinity="${applicationId}.mglwin"
android:theme="@style/Theme.MobileGLPlugin" />
<activity
android:name=".PluginActivity"
tools:node="remove" />