asLody/VirtualApp · error · java.lang.SecurityException
Files still open
Error message
Files still open
What it means
commit() seals the session, but first requires that every active FileBridge (the write stream bridge for openWrite) is closed. If any writer still holds an open bridge, it throws SecurityException('Files still open') and refuses to seal, protecting against installing a partially written APK.
Solutions
- Close every OutputStream returned by openWrite() (use try-with-resources) before commit().
- On failure paths, close streams in a finally block or call session.abandon() and start over.
- Await/join all background copy tasks before invoking commit().
Example fix
// before
OutputStream out = session.openWrite("base.apk", 0, -1);
out.write(apkBytes);
session.commit(callback); // SecurityException: Files still open
// after
try (OutputStream out = session.openWrite("base.apk", 0, -1)) {
out.write(apkBytes);
out.flush();
}
session.commit(callback); Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure all openWrite streams closed before commit
Set<OutputStream> openStreams; // maintained by your wrapper
if (!openStreams.isEmpty()) {
openStreams.forEach(IOUtils::closeQuietly);
} Try / catch
try {
session.commit(statusReceiver);
} catch (SecurityException e) {
if (String.valueOf(e.getMessage()).contains("Files still open")) {
closeAllStreams();
session.abandon(); // or re-commit after closing if still valid
} else throw e;
} Prevention
- Use try-with-resources for every openWrite() stream.
- Join background copy threads before commit().
- Wrap writes so exceptions always close streams (finally).
When it happens
Trigger: Calling session.commit() while an OutputStream from openWrite() is still open (not closed/flushed); leaking a stream on an exception path; another thread still mid-write.
Common situations: Exception during APK copy leaves stream unclosed, later commit fails; writing multiple splits and forgetting to close one; asynchronous copy tasks not joined before commit.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- not allowed after commit
- before prepared
- Invalid name:
- Must be sealed to accept permissions
- Exactly one of stageDir or stageCid stage must be set
AI-assisted analysis of asLody/VirtualApp@666fefcb5d (2026-09-09).
Data as JSON: /api/errors/33998c11abadc8a2.
Report an issue: GitHub.
Appendix: source
Thrown at VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java:392
}
@Override
public void close() throws RemoteException {
if (mActiveCount.decrementAndGet() == 0) {
mCallback.onSessionActiveChanged(this, false);
}
}
@Override
public void commit(IntentSender statusReceiver) throws RemoteException {
final boolean wasSealed;
synchronized (mLock) {
wasSealed = mSealed;
if (!mSealed) {
// Verify that all writers are hands-off
for (FileBridge bridge : mBridges) {
if (!bridge.isClosed()) {
throw new SecurityException("Files still open");
}
}
mSealed = true;
}
// Client staging is fully done at this point
mClientProgress = 1f;
computeProgressLocked(true);
}
if (!wasSealed) {
// Persist the fact that we've sealed ourselves to prevent
// mutations of any hard links we create. We do this without holding
// the session lock, since otherwise it's a lock inversion.
mCallback.onSessionSealedBlocking(this);
}
// This ongoing commit should keep session active, even though clientView on GitHub (pinned to 666fefcb5d)