asLody/VirtualApp · error · java.lang.SecurityException

Must be sealed to accept permissions

Error message

Must be sealed to accept permissions

What it means

When an install requires runtime-permission confirmation, the system may ask the user; setPermissionsResult(accepted) records the answer and kicks off the next install pass, but only if the session is already sealed (committed). Calling it before commit() throws SecurityException('Must be sealed to accept permissions').

Solutions

  1. Ensure commit() was called and completed (mSealed true) before relaying the permission decision.
  2. Verify the sessionId in the permission callback matches the committed session; ignore stale callbacks.
  3. If the session was abandoned, drop the permission response instead of calling setPermissionsResult.

Example fix

// before
if (promptAccepted) session.setPermissionsResult(true); // SecurityException if unsealed

// after
if (promptAccepted && session.sealed) {
    session.setPermissionsResult(true);
} else {
    Log.w(TAG, "Ignoring permission result for unsealed/unknown session");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Only forward results for sessions you committed yourself
if (!isSessionCommitted(sessionId)) return; // drop stale permission callback

Try / catch

try {
    session.setPermissionsResult(accepted);
} catch (SecurityException e) {
    if (String.valueOf(e.getMessage()).contains("Must be sealed")) {
        Log.w(TAG, "Permission result for unsealed session ignored");
    } else throw e;
}

Prevention

When it happens

Trigger: Delivering the permission-accept/deny callback for a session that has not gone through commit(); invoking setPermissionsResult manually out of order or twice across unsealed sessions.

Common situations: Custom installer UI that responds to a permission prompt for a session which was concurrently abandoned or never committed; race between commit() and the user's permission answer; wrong sessionId passed to the callback.

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


AI-assisted analysis of asLody/VirtualApp@666fefcb5d (2026-09-09). Data as JSON: /api/errors/2577a38a39900989. Report an issue: GitHub.

Appendix: source

Thrown at VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java:458

    private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
        mFinalStatus = returnCode;
        mFinalMessage = msg;

        if (mRemoteObserver != null) {
            try {
                mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);
            } catch (RemoteException ignored) {
            }
        }

        final boolean success = (returnCode == INSTALL_SUCCEEDED);
        mCallback.onSessionFinished(this, success);
    }

    void setPermissionsResult(boolean accepted) {
        if (!mSealed) {
            throw new SecurityException("Must be sealed to accept permissions");
        }

        if (accepted) {
            // Mark and kick off another install pass
            synchronized (mLock) {
                mPermissionsAccepted = true;
            }
            mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
        } else {
            destroyInternal();
            dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
        }
    }

    public void open() throws IOException {
        if (mActiveCount.getAndIncrement() == 0) {
            mCallback.onSessionActiveChanged(this, true);
        }

View on GitHub (pinned to 666fefcb5d)