[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
@@ -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" />