From 8c8b4f8ef853f1e5a159625aa0c38ec83bb9ed66 Mon Sep 17 00:00:00 2001 From: inthewaves Date: Sun, 15 Feb 2026 00:38:06 -0800 Subject: [PATCH 1/3] fixup! PackageExt: add Bugle (Google Messages) ids Remove hide API so that PermissionController can access them. Needed to run `m system-api-stubs-docs-non-updatable-update-current-api` --- core/api/system-current.txt | 2 ++ core/java/android/ext/PackageId.java | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 880e7d4c90dcf..442db8aa56983 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5300,6 +5300,8 @@ package android.ext { public interface PackageId { field public static final int ANDROID_AUTO = 10; // 0xa field public static final String ANDROID_AUTO_NAME = "com.google.android.projection.gearhead"; + field public static final int BUGLE = 14; // 0xe + field public static final String BUGLE_NAME = "com.google.android.apps.messaging"; field public static final int EUICC_SUPPORT_PIXEL = 5; // 0x5 field public static final String EUICC_SUPPORT_PIXEL_NAME = "com.google.euiccpixel"; field public static final int GMS_CORE = 2; // 0x2 diff --git a/core/java/android/ext/PackageId.java b/core/java/android/ext/PackageId.java index 2784ba86b84e0..99579bed50c59 100644 --- a/core/java/android/ext/PackageId.java +++ b/core/java/android/ext/PackageId.java @@ -52,8 +52,8 @@ public interface PackageId { String PIXEL_HEALTH_NAME = "com.google.android.apps.pixel.health"; int PIXEL_HEALTH = 13; - /** @hide */ String BUGLE_NAME = "com.google.android.apps.messaging"; - /** @hide */ int BUGLE = 14; + String BUGLE_NAME = "com.google.android.apps.messaging"; + int BUGLE = 14; /** @hide */ String SYSTEM_KEYBOARD = "com.android.inputmethod.latin"; } From d75486faab307fd6d6132557e3e6b82078a09b1c Mon Sep 17 00:00:00 2001 From: inthewaves Date: Tue, 17 Feb 2026 03:49:24 -0800 Subject: [PATCH 2/3] gmscompat: notify for RCS permissions if Bugle tries to verifies number Adds a potential notification for missing RCS permissions when Bugle calls `IConstellationApiService#verifyPhoneNumber` for RCS configuration and activation. --- core/java/android/os/Binder.java | 4 + .../android/internal/gmscompat/GmsHooks.java | 127 ++++++++++++++++++ .../android/internal/gmscompat/IGms2Gca.aidl | 2 + 3 files changed, 133 insertions(+) diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 92e4b8f3e209a..615364d2e66ff 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -769,11 +769,13 @@ public void attachInterface(@Nullable IInterface owner, @Nullable String descrip if (GmsCompat.isGmsCore()) { mIsGmsServiceBroker = GmsHooks.GMS_SERVICE_BROKER_INTERFACE_DESCRIPTOR.equals(descriptor); + mIsGmsConstellationService = GmsHooks.GMS_CONSTELLATION_SERVICE_INTERFACE_DESCRIPTOR.equals(descriptor); } } private boolean mIsIGmsCallbacks; private boolean mIsGmsServiceBroker; + private boolean mIsGmsConstellationService; /** * Default implementation returns an empty interface name. @@ -1496,6 +1498,8 @@ private boolean execTransactInternal(int code, Parcel data, Parcel reply, int fl try { if (mIsGmsServiceBroker) { onBeginGmsServiceBrokerCallRet = GmsHooks.onBeginGmsServiceBrokerCall(code, data); + } else if (mIsGmsConstellationService) { + GmsHooks.onBeginGmsConstellationServiceCall(code, data); } // TODO(b/299356201) - this logic should not be in Java - it should be in native // code in libbinder so that it works for all binder users. diff --git a/core/java/com/android/internal/gmscompat/GmsHooks.java b/core/java/com/android/internal/gmscompat/GmsHooks.java index 98681d0299e64..ca2f5a88d5238 100644 --- a/core/java/com/android/internal/gmscompat/GmsHooks.java +++ b/core/java/com/android/internal/gmscompat/GmsHooks.java @@ -17,6 +17,7 @@ package com.android.internal.gmscompat; import android.Manifest; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.Activity; @@ -37,7 +38,9 @@ import android.database.Cursor; import android.database.MatrixCursor; import android.database.sqlite.SQLiteOpenHelper; +import android.ext.PackageId; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.DeadSystemRuntimeException; import android.os.IBinder; @@ -62,6 +65,7 @@ import com.android.internal.gmscompat.gcarriersettings.TestCarrierConfigService; import com.android.internal.gmscompat.sysservice.GmcPackageManager; +import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -672,6 +676,129 @@ public static void onEndGmsServiceBrokerCall() { GmcPackageManager.notifyPermissionsChangeListeners(); } + public static final String GMS_CONSTELLATION_SERVICE_INTERFACE_DESCRIPTOR = + "com.google.android.gms.constellation.internal.IConstellationApiService"; + + /** Imported from android.app.appsearch.safeparcel.SafeParcelReader */ + private static class SafeParcelReader { + private static void throwParseException( + @NonNull String message, @NonNull Parcel p) throws ParseException { + throw new ParseException(message + + " Parcel: pos=" + p.dataPosition() + " size=" + p.dataSize(), p.dataPosition()); + } + + private static final int OBJECT_HEADER = 0x00004f45; + + public static int readHeader(@NonNull Parcel p) { + return p.readInt(); + } + + public static int getFieldId(int header) { + return header & 0x0000ffff; + } + + public static int readSize(@NonNull Parcel p, int header) { + if ((header & 0xffff0000) != 0xffff0000) { + return (header >> 16) & 0x0000ffff; + } else { + return p.readInt(); + } + } + + /** Skips the unknown field. */ + public static void skipUnknownField(@NonNull Parcel p, int header) { + int size = readSize(p, header); + p.setDataPosition(p.dataPosition() + size); + } + + /** + * Returns the end position of the object in the parcel. + */ + public static int validateObjectHeader(@NonNull Parcel p) throws ParseException { + final int header = readHeader(p); + final int size = readSize(p, header); + final int start = p.dataPosition(); + if (getFieldId(header) != OBJECT_HEADER) { + throwParseException( + "Expected object header. Got 0x" + Integer.toHexString(header), p); + } + final int end = start + size; + if (end < start || end > p.dataSize()) { + throwParseException("Size read is invalid start=" + start + " end=" + end, p); + } + return end; + } + + @Nullable + public static String createString(@NonNull Parcel p, int header) { + final int size = readSize(p, header); + final int pos = p.dataPosition(); + if (size == 0) { + return null; + } + final String result = p.readString(); + p.setDataPosition(pos + size); + return result; + } + } + + public static void onBeginGmsConstellationServiceCall(int transactionCode, Parcel data) { + if (transactionCode != 3) { // verifyPhoneNumber V2 method + return; + } + + try { + final var ctx = GmsCompat.appContext(); + if (ctx == null) return; + final var callingPkg = ctx.getPackageManager().getNameForUid(Binder.getCallingUid()); + // Ensure we're only sending RCS permission notifications for Bugle phone number + // verification attempts. + // + // GmsServiceBroker also does validation of allowed packages, but it doesn't seem it's + // restricted to only Bugle. For the Constellation service (155), the + // VerifyPhoneNumberApi__packages_allowed_to_call flag (proto list) also includes other + // apps like com.google.android.dialer, etc. in its default value. + if (!PackageId.BUGLE_NAME.equals(callingPkg)) { + Log.d(TAG, "onBeginGmsConstellationServiceCall code " + transactionCode + ", unexpected callingPkg " + callingPkg); + return; + } + + data.enforceInterface(GMS_CONSTELLATION_SERVICE_INTERFACE_DESCRIPTOR); + // IConstellationCallbacks binder + data.readStrongBinder(); + + if (data.readInt() == 1) { // VerifyPhoneNumberRequest is present + // Note: VerifyPhoneNumberRequest may contain sensitive information (IMSIs), but + // we're only reading the policy ID. + final int end = SafeParcelReader.validateObjectHeader(data); + while (data.dataPosition() < end) { + final int fieldHeader = SafeParcelReader.readHeader(data); + if (SafeParcelReader.getFieldId(fieldHeader) == 0x1) { // policyId field + // Known TS43 UPI policies as of 20250119: + // upi-carrier-tos-ts43 and upi-ts43-only + final String policyId = SafeParcelReader.createString(data, fieldHeader); + final boolean isTs43Verification = policyId != null && + policyId.startsWith("upi-") && + policyId.contains("ts43"); + Log.d(TAG, "onBeginGmsConstellationServiceCall: policyId " + policyId); + // We could also decode the Bundle field in VerifyPhoneNumberRequest which + // stores the key-value "required_consumer_consent" -> "RCS", and check + // for this. + GmsCompatApp.iGms2Gca() + .maybeShowRcsRequirementsNotification(isTs43Verification); + return; + } else { + SafeParcelReader.skipUnknownField(data, fieldHeader); + } + } + } + } catch (Exception e) { + Log.e(TAG, "onBeginGmsConstellationServiceCall: failed", e); + } finally { + data.setDataPosition(0); + } + } + public static IBinder maybeOverrideBinder(IBinder binder) { boolean proceed = GmsCompat.isEnabled() || GmsCompat.isClientOfGmsCore(); if (!proceed) { diff --git a/core/java/com/android/internal/gmscompat/IGms2Gca.aidl b/core/java/com/android/internal/gmscompat/IGms2Gca.aidl index a6aa9f35aea3b..c4a30840c1f48 100644 --- a/core/java/com/android/internal/gmscompat/IGms2Gca.aidl +++ b/core/java/com/android/internal/gmscompat/IGms2Gca.aidl @@ -47,4 +47,6 @@ interface IGms2Gca { Notification getMediaProjectionNotification(); void raisePackageToForeground(String targetPkg, long durationMs, @nullable String reason, int reasonCode); + + oneway void maybeShowRcsRequirementsNotification(boolean isTs43Verification); } From 932b523f56a6b962f0edd794117d0aafabe5f970 Mon Sep 17 00:00:00 2001 From: inthewaves Date: Wed, 18 Feb 2026 21:02:52 -0800 Subject: [PATCH 3/3] [temporary] gmscompat: add TODO for USE_ICC_AUTH for RCS in Android 17 --- core/java/android/app/ContextImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 395fe57904ac1..af1dc485695a6 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2610,6 +2610,11 @@ public int checkPermission(String permission, int pid, int uid) { throw new IllegalArgumentException("permission is null"); } + // TODO: In Android 17, READ_PRIVILEGED_PHONE_STATE will no longer be accepted for + // TelephonyManager#getIccAuthentication. However, Manifest.permission.USE_ICC_AUTH was + // introduced. getIccAuthentication can accept this new permission along with preexisting + // Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER. Need to pay attention to how + // GmsCore uses USE_ICC_AUTH or USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER. if (GmsCompat.isGmsCore() && android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE.equals(permission)) { if (Log.isLoggable(TAG_SPOOF, Log.VERBOSE)) {