From 3f241f8d8eb50bb2bffc747615c6c9d420d5e38b Mon Sep 17 00:00:00 2001 From: ssangamesh Date: Tue, 10 Jun 2025 09:36:28 +0000 Subject: [PATCH 1/3] Android: worked on lint and build warnings --- .../grpc/android/AndroidChannelBuilder.java | 2 -- .../grpc/binder/AndroidComponentAddress.java | 2 ++ .../java/io/grpc/binder/SecurityPolicies.java | 2 -- .../grpc/binder/internal/ServiceBinding.java | 23 +++++++++++-------- lint.xml | 4 ++++ 5 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 lint.xml diff --git a/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java b/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java index e56ce5fc405..54b38bc3bd3 100644 --- a/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java +++ b/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java @@ -217,7 +217,6 @@ private void configureNetworkMonitoring() { connectivityManager.registerDefaultNetworkCallback(defaultNetworkCallback); unregisterRunnable = new Runnable() { - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void run() { connectivityManager.unregisterNetworkCallback(defaultNetworkCallback); @@ -231,7 +230,6 @@ public void run() { context.registerReceiver(networkReceiver, networkIntentFilter); unregisterRunnable = new Runnable() { - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void run() { context.unregisterReceiver(networkReceiver); diff --git a/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java b/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java index c4c17bb2cef..ed810be60cb 100644 --- a/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java +++ b/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java @@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -165,6 +166,7 @@ public Intent asBindIntent() { * *

See {@link Intent#URI_ANDROID_APP_SCHEME} for details. */ + @SuppressLint("InlinedApi") public String asAndroidAppUri() { Intent intentForUri = bindIntent; if (intentForUri.getPackage() == null) { diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index 05e8c43da79..c0f6fe81989 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -184,7 +184,6 @@ public Status checkAuthorization(int uid) { * Creates {@link SecurityPolicy} which checks if the app is a device owner app. See {@link * DevicePolicyManager}. */ - @RequiresApi(18) public static io.grpc.binder.SecurityPolicy isDeviceOwner(Context applicationContext) { DevicePolicyManager devicePolicyManager = (DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE); @@ -199,7 +198,6 @@ public static io.grpc.binder.SecurityPolicy isDeviceOwner(Context applicationCon * Creates {@link SecurityPolicy} which checks if the app is a profile owner app. See {@link * DevicePolicyManager}. */ - @RequiresApi(21) public static SecurityPolicy isProfileOwner(Context applicationContext) { DevicePolicyManager devicePolicyManager = (DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE); diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index ee171140045..ffc79f57c11 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Build; import android.os.IBinder; import android.os.UserHandle; import androidx.annotation.AnyThread; @@ -183,18 +184,22 @@ private static Status bindInternal( bindResult = context.bindService(bindIntent, conn, flags); break; case BIND_SERVICE_AS_USER: - bindResult = context.bindServiceAsUser(bindIntent, conn, flags, targetUserHandle); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + bindResult = context.bindServiceAsUser(bindIntent, conn, flags, targetUserHandle); + } break; case DEVICE_POLICY_BIND_SEVICE_ADMIN: DevicePolicyManager devicePolicyManager = - (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); - bindResult = - devicePolicyManager.bindDeviceAdminServiceAsUser( - channelCredentials.getDevicePolicyAdminComponentName(), - bindIntent, - conn, - flags, - targetUserHandle); + (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + bindResult = + devicePolicyManager.bindDeviceAdminServiceAsUser( + channelCredentials.getDevicePolicyAdminComponentName(), + bindIntent, + conn, + flags, + targetUserHandle); + } break; } if (!bindResult) { diff --git a/lint.xml b/lint.xml new file mode 100644 index 00000000000..9dcca2a820a --- /dev/null +++ b/lint.xml @@ -0,0 +1,4 @@ + + + + From 1655cb416ee86fb3cf6cbc8df13b38440872a325 Mon Sep 17 00:00:00 2001 From: ssangamesh Date: Fri, 11 Jul 2025 04:43:19 +0000 Subject: [PATCH 2/3] Android: fixed internal review points --- .../grpc/binder/AndroidComponentAddress.java | 8 +- .../grpc/binder/internal/ServiceBinding.java | 74 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java b/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java index af300c9d7a8..327be3cdbcb 100644 --- a/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java +++ b/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java @@ -17,6 +17,7 @@ package io.grpc.binder; import static android.content.Intent.URI_ANDROID_APP_SCHEME; +import static android.content.Intent.URI_INTENT_SCHEME; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; @@ -24,6 +25,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.UserHandle; import com.google.common.base.Objects; import io.grpc.ExperimentalApi; @@ -166,7 +168,6 @@ public Intent asBindIntent() { * *

See {@link Intent#URI_ANDROID_APP_SCHEME} for details. */ - @SuppressLint("InlinedApi") public String asAndroidAppUri() { Intent intentForUri = bindIntent; if (intentForUri.getPackage() == null) { @@ -174,7 +175,10 @@ public String asAndroidAppUri() { // factory methods. Oddly, a ComponentName is not enough. intentForUri = intentForUri.cloneFilter().setPackage(getComponent().getPackageName()); } - return intentForUri.toUri(URI_ANDROID_APP_SCHEME); + return intentForUri.toUri( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 + ? URI_ANDROID_APP_SCHEME + : URI_INTENT_SCHEME); } @Override diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index ffc79f57c11..eadfb8bb02b 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -23,15 +23,22 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.os.Build; import android.os.IBinder; import android.os.UserHandle; import androidx.annotation.AnyThread; import androidx.annotation.MainThread; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.VerifyException; import com.google.errorprone.annotations.concurrent.GuardedBy; import io.grpc.Status; +import io.grpc.StatusException; import io.grpc.binder.BinderChannelCredentials; +import java.lang.reflect.Method; +import java.util.List; import java.util.concurrent.Executor; import java.util.logging.Level; import java.util.logging.Logger; @@ -86,6 +93,8 @@ public String methodName() { private final Observer observer; private final Executor mainThreadExecutor; + private static volatile Method queryIntentServicesAsUserMethod; + @GuardedBy("this") private State state; @@ -252,6 +261,71 @@ void unbindInternal(Status reason) { } } + // Sadly the PackageManager#resolveServiceAsUser() API we need isn't part of the SDK or even a + // @SystemApi as of this writing. Modern Android prevents even system apps from calling it, by any + // means (https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces). + // So instead we call queryIntentServicesAsUser(), which does more than we need but *is* a + // @SystemApi in all the SDK versions where we support cross-user Channels. + @Nullable + private static ResolveInfo resolveServiceAsUser( + PackageManager packageManager, Intent intent, int flags, UserHandle targetUserHandle) { + List results = + queryIntentServicesAsUser(packageManager, intent, flags, targetUserHandle); + // The first query result is "what would be returned by resolveService", per the javadoc. + return (results != null && !results.isEmpty()) ? results.get(0) : null; + } + + // The cross-user Channel feature requires the client to be a system app so we assume @SystemApi + // queryIntentServicesAsUser() is visible to us at runtime. It would be visible at build time too, + // if our host system app were written to call it directly. We only have to use reflection here + // because grpc-java is a library built outside the Android source tree where the compiler can't + // see the "non-SDK" @SystemApis that we need. + @Nullable + @SuppressWarnings("unchecked") // Safe by PackageManager#queryIntentServicesAsUser spec in AOSP. + private static List queryIntentServicesAsUser( + PackageManager packageManager, Intent intent, int flags, UserHandle targetUserHandle) { + try { + if (queryIntentServicesAsUserMethod == null) { + synchronized (ServiceBinding.class) { + if (queryIntentServicesAsUserMethod == null) { + queryIntentServicesAsUserMethod = + PackageManager.class.getMethod( + "queryIntentServicesAsUser", Intent.class, int.class, UserHandle.class); + } + } + } + return (List) + queryIntentServicesAsUserMethod.invoke(packageManager, intent, flags, targetUserHandle); + } catch (ReflectiveOperationException e) { + throw new VerifyException(e); + } + } + + @AnyThread + @Override + public ServiceInfo resolve() throws StatusException { + checkState(sourceContext != null); + PackageManager packageManager = sourceContext.getPackageManager(); + int flags = 0; + if (Build.VERSION.SDK_INT >= 29) { + // Filter out non-'directBootAware' s when 'targetUserHandle' is locked. Here's why: + // Callers want 'bindIntent' to #resolve() to the same thing a follow-up call to #bind() will. + // But bindService() *always* ignores services that can't presently be created for lack of + // 'directBootAware'-ness. This flag explicitly tells resolveService() to act the same way. + flags |= PackageManager.MATCH_DIRECT_BOOT_AUTO; + } + ResolveInfo resolveInfo = + targetUserHandle != null + ? resolveServiceAsUser(packageManager, bindIntent, flags, targetUserHandle) + : packageManager.resolveService(bindIntent, flags); + if (resolveInfo == null) { + throw Status.UNIMPLEMENTED // Same status code as when bindService() returns false. + .withDescription("resolveService(" + bindIntent + " / " + targetUserHandle + ") was null") + .asException(); + } + return resolveInfo.serviceInfo; + } + @MainThread private void clearReferences() { sourceContext = null; From 356f3457c0bda910c43845457bdeeb10619feab4 Mon Sep 17 00:00:00 2001 From: ssangamesh Date: Mon, 14 Jul 2025 03:42:46 +0000 Subject: [PATCH 3/3] Android: worked on lint and build warnings --- .../grpc/binder/AndroidComponentAddress.java | 6 ++--- .../grpc/binder/internal/ServiceBinding.java | 25 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java b/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java index 327be3cdbcb..cd8c62262ef 100644 --- a/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java +++ b/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java @@ -176,9 +176,9 @@ public String asAndroidAppUri() { intentForUri = intentForUri.cloneFilter().setPackage(getComponent().getPackageName()); } return intentForUri.toUri( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 - ? URI_ANDROID_APP_SCHEME - : URI_INTENT_SCHEME); + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 + ? URI_ANDROID_APP_SCHEME + : URI_INTENT_SCHEME); } @Override diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index eadfb8bb02b..46ef10a7d16 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -201,8 +201,7 @@ private static Status bindInternal( DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - bindResult = - devicePolicyManager.bindDeviceAdminServiceAsUser( + bindResult = devicePolicyManager.bindDeviceAdminServiceAsUser( channelCredentials.getDevicePolicyAdminComponentName(), bindIntent, conn, @@ -268,9 +267,9 @@ void unbindInternal(Status reason) { // @SystemApi in all the SDK versions where we support cross-user Channels. @Nullable private static ResolveInfo resolveServiceAsUser( - PackageManager packageManager, Intent intent, int flags, UserHandle targetUserHandle) { + PackageManager packageManager, Intent intent, int flags, UserHandle targetUserHandle) { List results = - queryIntentServicesAsUser(packageManager, intent, flags, targetUserHandle); + queryIntentServicesAsUser(packageManager, intent, flags, targetUserHandle); // The first query result is "what would be returned by resolveService", per the javadoc. return (results != null && !results.isEmpty()) ? results.get(0) : null; } @@ -283,19 +282,19 @@ private static ResolveInfo resolveServiceAsUser( @Nullable @SuppressWarnings("unchecked") // Safe by PackageManager#queryIntentServicesAsUser spec in AOSP. private static List queryIntentServicesAsUser( - PackageManager packageManager, Intent intent, int flags, UserHandle targetUserHandle) { + PackageManager packageManager, Intent intent, int flags, UserHandle targetUserHandle) { try { if (queryIntentServicesAsUserMethod == null) { synchronized (ServiceBinding.class) { if (queryIntentServicesAsUserMethod == null) { queryIntentServicesAsUserMethod = - PackageManager.class.getMethod( - "queryIntentServicesAsUser", Intent.class, int.class, UserHandle.class); + PackageManager.class.getMethod( + "queryIntentServicesAsUser", Intent.class, int.class, UserHandle.class); } } } return (List) - queryIntentServicesAsUserMethod.invoke(packageManager, intent, flags, targetUserHandle); + queryIntentServicesAsUserMethod.invoke(packageManager, intent, flags, targetUserHandle); } catch (ReflectiveOperationException e) { throw new VerifyException(e); } @@ -315,13 +314,13 @@ public ServiceInfo resolve() throws StatusException { flags |= PackageManager.MATCH_DIRECT_BOOT_AUTO; } ResolveInfo resolveInfo = - targetUserHandle != null - ? resolveServiceAsUser(packageManager, bindIntent, flags, targetUserHandle) - : packageManager.resolveService(bindIntent, flags); + targetUserHandle != null + ? resolveServiceAsUser(packageManager, bindIntent, flags, targetUserHandle) + : packageManager.resolveService(bindIntent, flags); if (resolveInfo == null) { throw Status.UNIMPLEMENTED // Same status code as when bindService() returns false. - .withDescription("resolveService(" + bindIntent + " / " + targetUserHandle + ") was null") - .asException(); + .withDescription("resolveService(" + bindIntent + " / " + targetUserHandle + ") was null") + .asException(); } return resolveInfo.serviceInfo; }