microg/GmsCore · error · SecurityException

suggested UID [

Error message

suggested UID [

What it means

getAndCheckCallingPackage verifies that a caller-supplied 'suggested' caller identity matches the real Binder identity. If suggestedCallerUid > 0 and differs from Binder.getCallingUid(), it throws this SecurityException. The library does this to prevent a privileged or IPC-forwarding process from spoofing another app's identity when claiming a package name.

Source

Thrown at play-services-base/core/src/main/java/org/microg/gms/common/PackageUtils.java:231

            throw e;
        }
    }

    @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) {

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Pass suggestedCallerUid = 0 (or use the overload without uid/pid) unless you obtained the value from a trusted source for this exact transaction.
  2. Compare against Binder.getCallingUid() yourself and log the mismatch before calling, to identify the spoofing client.
  3. If you intentionally forward on behalf of another process, do not forward its UID; perform the package check with the real calling UID.
  4. Fix client code to stop sending uid/pid fields in the request bundle.

Example fix

// before
String pkg = PackageUtils.getAndCheckCallingPackage(context, suggestedPkg, extra.getInt("uid"), extra.getInt("pid"));

// after
String pkg = PackageUtils.getAndCheckCallingPackage(context, suggestedPkg); // uid/pid taken from Binder
Defensive patterns

Strategy: validation

Validate before calling

int callingUid = Binder.getCallingUid();
if (suggestedCallerUid > 0 && suggestedCallerUid != callingUid) {
    throw new IllegalArgumentException("suggestedCallerUid does not match Binder caller");
}

Type guard

boolean isTrustedSuggestedUid(int suggestedUid) {
    return suggestedUid <= 0 || suggestedUid == Binder.getCallingUid();
}

Try / catch

try {
    return PackageUtils.getAndCheckCallingPackage(context, suggestedPkg, uid, pid);
} catch (SecurityException e) {
    Log.w(TAG, "Caller identity mismatch (uid)", e);
    return null;
}

Prevention

When it happens

Trigger: An IPC endpoint (e.g. an AIDL service method receiving a Bundle) passes suggestedCallerUid from untrusted client input that does not equal Binder.getCallingUid(); forwarding a request on behalf of another process while passing that process's UID.

Common situations: Hand-written AIDL glue code that copies a UID/PID out of the request extras instead of leaving it 0; proxy/dispatcher apps forwarding calls to microG services; stale code written for non-Binder call paths.

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/d1f8e58d2270c684. Report an issue: GitHub.