asLody/VirtualApp · error · SecurityException

Invalid userId

Error message

Invalid userId 

What it means

VPackageManagerService.checkUserId() validates that the target user profile exists via VUserManagerService.get().exists(userId). Package/component lookups are per-user in VirtualApp; querying a userId that was never created throws SecurityException("Invalid userId "+userId) instead of returning null data.

Solutions

  1. Create/obtain a valid user via VUserManagerService (e.g. start/create user) and pass its id to package lookups.
  2. Before calling, guard with VUserManagerService.get().exists(userId) and fall back to a valid user id.
  3. Replace hardcoded userId (especially 0) with the id of the currently active virtual user.
  4. Handle user removal: refresh cached userIds and retry after the user is recreated.

Example fix

// before
PackageInfo pi = vPms.getPackageInfo(pkg, 0, 0); // userId 0 may not exist
// after
int userId = VUserManagerService.get().exists(0) ? 0 : VUserManagerService.get().getUsers().get(0).id;
if (!VUserManagerService.get().exists(userId)) { userId = vUserManager.createUser("user").id; }
PackageInfo pi = vPms.getPackageInfo(pkg, 0, userId);
Defensive patterns

Strategy: validation

Validate before calling

if (!VUserManagerService.get().exists(userId)) {
    // pick a valid user or create one before any package lookup
    userId = VUserManagerService.get().getUsers().get(0).id;
}

Type guard

boolean isValidVirtualUser(int userId) {
    return VUserManagerService.get() != null && VUserManagerService.get().exists(userId);
}

Try / catch

try {
    return vPms.getPackageInfo(packageName, flags, userId);
} catch (SecurityException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Invalid userId")) {
        return vPms.getPackageInfo(packageName, flags, getDefaultVirtualUserId());
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling getPackageInfo, getActivityInfo, getReceiverInfo, getServiceInfo, getProviderInfo, or resolveIntent with a flags+userId where the userId is not an installed virtual user — e.g. userId 0 in a fresh VirtualApp install (only user 10+ typically exists), a stale cached userId after the user was removed, or an hardcoded user id.

Common situations: Hardcoding userId=0 in multi-user virtual setups; calling PM APIs after VUserManagerService users were wiped (reinstall of the host app); concurrent user removal while another thread performs a package lookup; passing an OS userId (e.g. from UserManager) instead of a VirtualApp user id.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java:279

    private int updateFlagsNought(int flags) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            return flags;
        }
        if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) {
            // Caller expressed an explicit opinion about what encryption
            // aware/unaware components they want to see, so fall through and
            // give them what they want
        } else {
            // Caller expressed no opinion, so match based on user state
            flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
        }
        return flags;
    }

    private void checkUserId(int userId) {
        if (!VUserManagerService.get().exists(userId)) {
            throw new SecurityException("Invalid userId " + userId);
        }
    }

    @Override
    public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
        checkUserId(userId);
        flags = updateFlagsNought(flags);
        synchronized (mPackages) {
            VPackage p = mPackages.get(component.getPackageName());
            if (p != null) {
                PackageSetting ps = (PackageSetting) p.mExtras;
                VPackage.ActivityComponent a = mActivities.mActivities.get(component);
                if (a != null) {
                    ActivityInfo activityInfo = PackageParserEx.generateActivityInfo(a, flags, ps.readUserState(userId), userId);
                    ComponentFixer.fixComponentInfo(ps, activityInfo, userId);
                    return activityInfo;
                }
            }

View on GitHub (pinned to 666fefcb5d)