asLody/VirtualApp · error · SecurityException
You need MANAGE_USERS permission to:
Error message
You need MANAGE_USERS permission to:
What it means
checkManageUsersPermission in VUserManagerService guards user-management operations (rename, icon, guest mode, wipe, create user). It throws SecurityException unless the caller's UID equals VirtualCore's own UID. In other words, only the virtual engine process itself (system/root-equivalent inside the virtualized environment) may manage users; any outside binder caller is rejected.
Solutions
- Invoke user management through the official VirtualCore facade from within the engine process, not via direct binder calls from a client process.
- If you must call remotely, run the call inside the process that hosts VirtualCore so the calling uid matches myUid().
- Wrap the call in try-catch for SecurityException and fall back to a request-passing mechanism (e.g. an intent/IPC to the engine) that executes the operation in-process.
- Verify the client was properly installed/initialized by VirtualApp so its identity resolves to the engine uid.
Example fix
// before
VUserManagerService.get().createUser("name", 0);
// after
try {
VUserManagerService.get().createUser("name", 0);
} catch (SecurityException e) {
// route through the engine process instead
VirtualCore.get().createUser("name", 0);
} Defensive patterns
Strategy: try-catch
Validate before calling
boolean canManageUsers = VBinder.getCallingUid() == VirtualCore.get().myUid();
if (!canManageUsers) { /* route request through the engine process instead */ } Type guard
static boolean isEngineProcess() {
return VBinder.getCallingUid() == VirtualCore.get().myUid();
} Try / catch
try {
userManager.createUser(name, flags);
} catch (SecurityException e) {
Log.w(TAG, "MANAGE_USERS required, routing through engine", e);
engineProxy.createUser(name, flags);
} Prevention
- Only call VUserManagerService from within the VirtualApp engine process.
- Expose user management through a single facade that already runs in-process.
- Log the calling uid in dev builds to catch identity mismatches early.
When it happens
Trigger: Calling setUserName, setUserIcon, setGuestEnabled, wipeUser, makeInitialized, or createUser via binder from a process whose VBinder.getCallingUid() != VirtualCore.get().myUid(). Any cross-process caller other than the engine's own process triggers it.
Common situations: Calling VUserManagerService directly from an app inside the virtual container instead of going through VirtualCore/IPackageManager on the engine side; re-implementing binder stubs that pass a foreign calling uid; testing user management from an external shell or another app.
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
- Who are you?
- Unable to create application
- Unable to start receiver
- VirtualCore.startup() must called in main thread.
- Initializer = NULL
AI-assisted analysis of asLody/VirtualApp@666fefcb5d (2026-09-09).
Data as JSON: /api/errors/678102ad07af152e.
Report an issue: GitHub.
Appendix: source
Thrown at VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VUserManagerService.java:168
public static VUserManagerService get() {
synchronized (VUserManagerService.class) {
return sInstance;
}
}
/**
* Enforces that only the system UID or root's UID or apps that have the
* {android.Manifest.permission.MANAGE_USERS MANAGE_USERS}
* permission can make certain calls to the VUserManager.
*
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or root
*/
private static void checkManageUsersPermission(String message) {
final int uid = VBinder.getCallingUid();
if (uid != VirtualCore.get().myUid()) {
throw new SecurityException("You need MANAGE_USERS permission to: " + message);
}
}
@Override
public List<VUserInfo> getUsers(boolean excludeDying) {
//checkManageUsersPermission("query users");
synchronized (mPackagesLock) {
ArrayList<VUserInfo> users = new ArrayList<VUserInfo>(mUsers.size());
for (int i = 0; i < mUsers.size(); i++) {
VUserInfo ui = mUsers.valueAt(i);
if (ui.partial) {
continue;
}
if (!excludeDying || !mRemovingUserIds.contains(ui.id)) {
users.add(ui);
}
}
return users;View on GitHub (pinned to 666fefcb5d)