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

  1. Pass suggestedCallerPid = 0 to skip the PID check, or use the overload that derives identity from Binder automatically.
  2. Always sample Binder.getCallingPid()/getCallingUid() at call time in the same transaction; never cache or forward them across calls.
  3. Remove pid/uid fields from any client-provided request data used for this check.
  4. 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

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


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/bd74fc8d3209fe87. Report an issue: GitHub.