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
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,28 @@ private void resetExtractAndBootstrap(String downloadPath, String tarball) {
// ------------------------------------------------------------ module queue

private void runModuleQueue() {
installNextModule();
// ADFA-4842: a runrole modifies system packages/config, so it must own the rootfs
// exclusively. Unlike content (which the running server handles in-process), we cannot
// avoid the runrole's own proot — so instead stop the server's SERVICES first (pdsm stop)
// so a runrole never runs alongside a live server writing the same DBs/config (the
// data-corruption risk). The app restarts the server after the queue (LibraryActivity's
// module-queue observer). Stopping is idempotent (no-op if the server is already down).
if (cancelled) return;
updateNotification(getString(R.string.server_shutting_down));
log("[Modules] Stopping server services before runroles (exclusive rootfs)...");
if (prootEngine == null) prootEngine = new PRootEngine();
prootEngine.executeInContainer(this, debianRootfs.getAbsolutePath(),
"/usr/bin/env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin bash -lc '/usr/local/bin/pdsm stop'",
new PRootEngine.OutputListener() {
@Override public void onOutputLine(String line) { log("[Modules] pdsm stop: " + line); }
@Override public void onProcessExit(int exitCode) { installNextModule(); }
@Override public void onError(String error) {
// Proceed anyway: if the stop couldn't launch, the runroles still need to run;
// surface it in the log rather than hanging the queue.
log("[Modules] pdsm stop error (continuing): " + error);
installNextModule();
}
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class LibraryActivity extends AppCompatActivity implements ServerControll
private boolean gateDismissed = false;
private boolean closing = false;
private boolean closedDone = false;
private boolean moduleBusy = false; // ADFA-4842: a module (runrole) queue is running

// ADFA-4837: animated "…" on the boot status line so the long pre-pdsm silence doesn't look frozen.
private final Handler ellipsisHandler = new Handler(Looper.getMainLooper());
Expand Down Expand Up @@ -172,6 +173,21 @@ public android.graphics.Typeface fetchFont(String fontFamily) {
}
});

// ADFA-4842: while a module (runrole) queue runs, the server is stopped and the rootfs is
// being modified — block the WHOLE library UI (every tab + nav) behind the full-screen boot
// gate, show which module is installing, and bring the server back when the queue finishes.
org.iiab.controller.install.presentation.ModuleQueueRepository.get().state().observe(this, ms -> {
if (ms == null) return;
if (ms.isRunning()) {
if (!moduleBusy) { moduleBusy = true; showModuleBusy(); }
if (installDetail != null) installDetail.setText(ms.currentModule == null ? "" : ms.currentModule);
} else if (moduleBusy) {
moduleBusy = false;
hideModuleBusy();
if (canStartServer()) startServer(); // the server was stopped for the runroles
}
});

Handler main = new Handler(Looper.getMainLooper());
if (installing) {
// A download is in progress: keep the gate and show live progress; dismissal
Expand Down Expand Up @@ -219,6 +235,37 @@ private void hideInstallProgress() {
if (installProgress != null) installProgress.setVisibility(View.GONE);
}

// ADFA-4842: full-screen "system updating" block while a module (runrole) queue runs. The boot
// gate is match_parent + clickable and drawn over the nav content, so it blocks EVERY tab
// (Library, Connect, Clone, Settings) and the nav bar — not just the Library cards.
private void showModuleBusy() {
if (closing) return;
stopBootEllipsis();
if (installProgress != null) {
installProgress.setVisibility(View.VISIBLE);
if (installStatus != null) installStatus.setText(getString(R.string.install_busy_modules));
if (installBar != null) { installBar.setVisibility(View.VISIBLE); installBar.setIndeterminate(true); }
}
if (bootGate != null) {
bootGate.setVisibility(View.VISIBLE);
if (!reduceMotion()) {
bootGate.removeAllAnimatorListeners();
bootGate.setRepeatCount(LottieDrawable.INFINITE);
bootGate.setMinAndMaxFrame("A_ENTRY_LOOP");
bootGate.playAnimation();
}
}
}

private void hideModuleBusy() {
hideInstallProgress();
if (bootGate != null) {
bootGate.removeAllAnimatorListeners();
bootGate.cancelAnimation();
bootGate.setVisibility(View.GONE);
}
}

private void onServerReady() {
if (gateDismissed || bootGate == null) {
return;
Expand Down