microg/GmsCore · error · SecurityException
suggested PID [
Error message
suggested PID [
What it means
Same guard as the UID check, but for the process id: if suggestedCallerPid > 0 and differs from Binder.getCallingPid(), getAndCheckCallingPackage throws this SecurityException. It prevents misattributed or spoofed caller identity during package-name verification.
Source
Thrown at play-services-base/core/src/main/java/org/microg/gms/common/PackageUtils.java:234
@Nullable
public static String getAndCheckCallingPackage(@NonNull Context context, int suggestedCallerUid) {
return getAndCheckCallingPackage(context, null, suggestedCallerUid);
}
@Nullable
public static String getAndCheckCallingPackage(@NonNull Context context, @Nullable String suggestedPackageName, int suggestedCallerUid) {
return getAndCheckCallingPackage(context, suggestedPackageName, suggestedCallerUid, 0);
}
@Nullable
public static String getAndCheckCallingPackage(@NonNull Context context, @Nullable String suggestedPackageName, int suggestedCallerUid, int suggestedCallerPid) {
int callingUid = Binder.getCallingUid(), callingPid = Binder.getCallingPid();
if (suggestedCallerUid > 0 && suggestedCallerUid != callingUid) {
throw new SecurityException("suggested UID [" + suggestedCallerUid + "] and real calling UID [" + callingUid + "] mismatch!");
}
if (suggestedCallerPid > 0 && suggestedCallerPid != callingPid) {
throw new SecurityException("suggested PID [" + suggestedCallerPid + "] and real calling PID [" + callingPid + "] mismatch!");
}
return getAndCheckPackage(context, suggestedPackageName, callingUid, callingPid);
}
@Nullable
public static String getAndCheckPackage(Context context, String suggestedPackageName, int callingUid) {
return getAndCheckPackage(context, suggestedPackageName, callingUid, 0);
}
@Nullable
public static String getAndCheckPackage(@NonNull Context context, @Nullable String suggestedPackageName, int callingUid, int callingPid) {
String packageName = packageFromProcessId(context, callingPid);
if (packageName == null) {
String[] packagesForUid = context.getPackageManager().getPackagesForUid(callingUid);
if (packagesForUid != null && packagesForUid.length != 0) {
if (packagesForUid.length == 1) {
packageName = packagesForUid[0];
} else if (Arrays.asList(packagesForUid).contains(suggestedPackageName)) {View on GitHub (pinned to 157c9d86ac)
Solutions
- Pass suggestedCallerPid = 0 to skip the PID check, or use the overload that derives identity from Binder automatically.
- Always sample Binder.getCallingPid()/getCallingUid() at call time in the same transaction; never cache or forward them across calls.
- Remove pid/uid fields from any client-provided request data used for this check.
- Log both values on mismatch to find which component is sending stale identity data.
Example fix
// before
int pid = request.getInt("callerPid"); // stale
PackageUtils.getAndCheckCallingPackage(context, pkg, uid, pid);
// after
PackageUtils.getAndCheckCallingPackage(context, pkg, uid, 0); // let Binder provide pid Defensive patterns
Strategy: validation
Validate before calling
int callingPid = Binder.getCallingPid();
if (suggestedCallerPid > 0 && suggestedCallerPid != callingPid) {
throw new IllegalArgumentException("suggestedCallerPid does not match Binder caller");
} Type guard
boolean isTrustedSuggestedPid(int suggestedPid) {
return suggestedPid <= 0 || suggestedPid == Binder.getCallingPid();
} Try / catch
try {
return PackageUtils.getAndCheckCallingPackage(context, suggestedPkg, uid, pid);
} catch (SecurityException e) {
Log.w(TAG, "Caller identity mismatch (pid)", e);
return null;
} Prevention
- Never reuse pid/uid captured in a previous transaction; PIDs are per-process and calls may hop processes.
- Prefer the overloads that do not take explicit uid/pid.
- Strip caller-pid fields from request data models.
- Log Binder.getCallingPid() on mismatch to trace stale senders.
When it happens
Trigger: Passing a caller PID captured in a previous transaction (PIDs change per process) or taken from client-controlled extras, so it no longer matches the current Binder.getCallingPid(); calling the method from a different thread/process than where the PID was sampled.
Common situations: Sticky cached caller info reused across binder calls; proxy services replaying stored uid/pid pairs; refactored code that now runs in a worker process.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- suggested UID [
- Access denied, missing google package permission for
- UID [
- No fields were found
- Too many non-synthetic fields were found
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/bd74fc8d3209fe87.
Report an issue: GitHub.