mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Feat] (Android): the render server screen also starts the on-screen display server
An On-screen window checkbox switches Start from the offscreen service to MobileGLDisplayActivity (process :mglwin), which serves in its own process and renders a client's window surface onto its SurfaceView. Stop ends either shape: stopService for the supervisor, killProcess for :mglwin, and the running check covers both. Verified end to end with an OpenRA retrace: the client dialed tcp://127.0.0.1:40613 with MOBILEGL_IPC_SURFACE=server from a freeform window while the display Activity held the screen, frames landed on the SurfaceView and the run passed with ssim=1.0.
This commit is contained in:
@@ -13,6 +13,7 @@ import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
@@ -25,12 +26,13 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Control screen for the offscreen render server ({@link MobileGLServerService}).
|
||||
* Collects the service's {@code listen}/{@code token}/{@code env} extras and starts or
|
||||
* stops it; no native code of its own. The endpoint is a tcp://host:port for remote
|
||||
* clients, or an @abstract name / filesystem path for on-device ones. One server per
|
||||
* device (P12 D8): starting this service kills the on-screen display server in :mglwin,
|
||||
* and vice versa.
|
||||
* Control screen for the render server, in either shape: the offscreen supervisor
|
||||
* ({@link MobileGLServerService}) or the on-screen display server
|
||||
* ({@link MobileGLDisplayActivity}), which renders a client's window surface onto this
|
||||
* screen. Collects the shared {@code listen}/{@code token}/{@code env} extras and starts
|
||||
* or stops the selected one; no native code of its own. The endpoint is a tcp://host:port
|
||||
* for remote clients, or an @abstract name / filesystem path for on-device ones. One
|
||||
* server per device (P12 D8): the two shapes replace each other.
|
||||
*/
|
||||
public final class ServerControlActivity extends Activity {
|
||||
private static final String TAG = "MobileGLServerCtl";
|
||||
@@ -51,6 +53,7 @@ public final class ServerControlActivity extends Activity {
|
||||
private EditText listenField;
|
||||
private EditText tokenField;
|
||||
private EditText envField;
|
||||
private CheckBox onScreenBox;
|
||||
private Button startButton;
|
||||
private Button stopButton;
|
||||
private TextView statusView;
|
||||
@@ -74,13 +77,16 @@ public final class ServerControlActivity extends Activity {
|
||||
contentLayout.addView(makeText("MobileGL Render Server", 20, COLOR_TEXT, true));
|
||||
|
||||
TextView hint = makeText(
|
||||
"Starts the offscreen render server (libMobileGLServer.so --serve) as a foreground "
|
||||
+ "service. Endpoint forms: tcp://host:port for remote clients, @name for an "
|
||||
"Starts the render server. Offscreen (default): the supervisor "
|
||||
+ "(libMobileGLServer.so --serve) runs as a foreground service. On-screen "
|
||||
+ "window: the server runs in a display Activity that renders the client's "
|
||||
+ "frames onto this screen (clients opt in with MOBILEGL_IPC_SURFACE=server). "
|
||||
+ "Endpoint forms: tcp://host:port for remote clients, @name for an "
|
||||
+ "on-device abstract socket, or a filesystem path (relative to the app's "
|
||||
+ "files dir) for on-device clients. Non-loopback TCP requires a token of at "
|
||||
+ "least " + MIN_TOKEN_BYTES + " bytes; unix endpoints need none. Extra env "
|
||||
+ "uses the KEY=VALUE;KEY grammar (e.g. MOBILEGL_BACKEND_TYPE=DirectVulkan). "
|
||||
+ "One server per device: this replaces the on-screen display server.",
|
||||
+ "One server per device: the two modes replace each other.",
|
||||
12, COLOR_INFO, false);
|
||||
LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
@@ -91,6 +97,15 @@ public final class ServerControlActivity extends Activity {
|
||||
tokenField = addField(contentLayout, "Auth token (empty = loopback only)", "", dp(8));
|
||||
envField = addField(contentLayout, "Extra env (optional)", "", dp(8));
|
||||
|
||||
onScreenBox = new CheckBox(this);
|
||||
onScreenBox.setText("On-screen window (render client frames onto this screen)");
|
||||
onScreenBox.setTextColor(COLOR_TEXT);
|
||||
onScreenBox.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
LinearLayout.LayoutParams boxParams = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
boxParams.topMargin = dp(8);
|
||||
contentLayout.addView(onScreenBox, boxParams);
|
||||
|
||||
LinearLayout buttons = new LinearLayout(this);
|
||||
buttons.setOrientation(LinearLayout.HORIZONTAL);
|
||||
LinearLayout.LayoutParams buttonsParams = new LinearLayout.LayoutParams(
|
||||
@@ -121,8 +136,8 @@ public final class ServerControlActivity extends Activity {
|
||||
statusParams.topMargin = dp(12);
|
||||
contentLayout.addView(statusView, statusParams);
|
||||
|
||||
if (isServiceRunning()) {
|
||||
setStatus("Service is running (started outside this screen).", COLOR_WARN);
|
||||
if (isServerRunning()) {
|
||||
setStatus("A server is running (started outside this screen).", COLOR_WARN);
|
||||
} else {
|
||||
setStatus("Stopped.", COLOR_INFO);
|
||||
}
|
||||
@@ -189,23 +204,28 @@ public final class ServerControlActivity extends Activity {
|
||||
}
|
||||
};
|
||||
}
|
||||
if (isServiceRunning()) {
|
||||
setStatus("Service already running; stop it before reconfiguring.", COLOR_WARN);
|
||||
if (isServerRunning()) {
|
||||
setStatus("A server is already running; stop it before reconfiguring.", COLOR_WARN);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(this, MobileGLServerService.class);
|
||||
final boolean onScreen = onScreenBox.isChecked();
|
||||
Intent intent = new Intent(this,
|
||||
onScreen ? MobileGLDisplayActivity.class : MobileGLServerService.class);
|
||||
intent.putExtra("listen", endpoint);
|
||||
intent.putExtra("token", token);
|
||||
if (!env.isEmpty()) intent.putExtra("env", env);
|
||||
try {
|
||||
startForegroundService(intent);
|
||||
// The display Activity runs the server in its own process (:mglwin) and shows the
|
||||
// client's window surface; launching it brings that window to the front.
|
||||
if (onScreen) startActivity(intent);
|
||||
else startForegroundService(intent);
|
||||
} catch (RuntimeException error) {
|
||||
Log.e(TAG, "cannot start MobileGLServerService", error);
|
||||
setStatus("startForegroundService failed: " + error, COLOR_FAIL);
|
||||
Log.e(TAG, "cannot start the render server", error);
|
||||
setStatus("start failed: " + error, COLOR_FAIL);
|
||||
return;
|
||||
}
|
||||
setStatus("Starting " + endpoint + " ...", COLOR_INFO);
|
||||
setStatus("Starting " + endpoint + (onScreen ? " (on-screen)" : "") + " ...", COLOR_INFO);
|
||||
probeUntilListening(probeDisplay, probe);
|
||||
}
|
||||
|
||||
@@ -235,8 +255,27 @@ public final class ServerControlActivity extends Activity {
|
||||
Thread probe = probeThread;
|
||||
if (probe != null) probe.interrupt();
|
||||
boolean stopped = stopService(new Intent(this, MobileGLServerService.class));
|
||||
setStatus(stopped ? "Stopped." : "Stop requested, but the service was not running.",
|
||||
stopped ? COLOR_INFO : COLOR_WARN);
|
||||
boolean displayKilled = killDisplayServer();
|
||||
setStatus(stopped || displayKilled ? "Stopped." : "Stop requested, but no server was running.",
|
||||
stopped || displayKilled ? COLOR_INFO : COLOR_WARN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills the on-screen server's process (:mglwin). Same uid, so killProcess is allowed;
|
||||
* this mirrors MobileGLServerService.stopDisplayServer for the reverse direction.
|
||||
*/
|
||||
private boolean killDisplayServer() {
|
||||
ActivityManager activities = getSystemService(ActivityManager.class);
|
||||
String displayProcess = getPackageName() + ":mglwin";
|
||||
List<ActivityManager.RunningAppProcessInfo> processes = activities.getRunningAppProcesses();
|
||||
if (processes == null) return false;
|
||||
for (ActivityManager.RunningAppProcessInfo process : processes) {
|
||||
if (displayProcess.equals(process.processName)) {
|
||||
android.os.Process.killProcess(process.pid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,11 +306,12 @@ public final class ServerControlActivity extends Activity {
|
||||
}
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
if (isServiceRunning()) {
|
||||
setStatus("Service running but " + display
|
||||
+ " did not accept a probe; see logcat tag MobileGLServer.", COLOR_WARN);
|
||||
if (isServerRunning()) {
|
||||
setStatus("Server running but " + display + " did not accept a probe; "
|
||||
+ "see logcat tags MobileGLServer/MobileGLDisplay.", COLOR_WARN);
|
||||
} else {
|
||||
setStatus("Service exited; see logcat tag MobileGLServer.", COLOR_FAIL);
|
||||
setStatus("Server exited; see logcat tags MobileGLServer/MobileGLDisplay.",
|
||||
COLOR_FAIL);
|
||||
}
|
||||
});
|
||||
}, "mgl-server-probe");
|
||||
@@ -279,14 +319,23 @@ public final class ServerControlActivity extends Activity {
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private boolean isServiceRunning() {
|
||||
/** True when either server shape is up: the supervisor service or the :mglwin process. */
|
||||
private boolean isServerRunning() {
|
||||
ActivityManager activities = getSystemService(ActivityManager.class);
|
||||
List<ActivityManager.RunningServiceInfo> services =
|
||||
activities.getRunningServices(Integer.MAX_VALUE);
|
||||
if (services == null) return false;
|
||||
for (ActivityManager.RunningServiceInfo service : services) {
|
||||
if (MobileGLServerService.class.getName().equals(service.service.getClassName())) {
|
||||
return true;
|
||||
if (services != null) {
|
||||
for (ActivityManager.RunningServiceInfo service : services) {
|
||||
if (MobileGLServerService.class.getName().equals(service.service.getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
String displayProcess = getPackageName() + ":mglwin";
|
||||
List<ActivityManager.RunningAppProcessInfo> processes = activities.getRunningAppProcesses();
|
||||
if (processes != null) {
|
||||
for (ActivityManager.RunningAppProcessInfo process : processes) {
|
||||
if (displayProcess.equals(process.processName)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user