Skip to content

Commit c005d72

Browse files
committed
Address comments from CR/769886275
1 parent 5517904 commit c005d72

7 files changed

Lines changed: 65 additions & 30 deletions

File tree

binder/src/androidTest/java/io/grpc/binder/internal/BinderClientTransportTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@
5858
import java.io.InputStream;
5959
import java.util.ArrayDeque;
6060
import java.util.Deque;
61+
import java.util.concurrent.BlockingQueue;
6162
import java.util.concurrent.ExecutorService;
6263
import java.util.concurrent.Executors;
64+
import java.util.concurrent.LinkedBlockingQueue;
6365
import java.util.concurrent.ScheduledExecutorService;
6466
import javax.annotation.Nullable;
6567
import org.junit.After;
@@ -185,7 +187,7 @@ public BinderTransport.BinderClientTransport build() {
185187

186188
@After
187189
public void tearDown() throws Exception {
188-
blockingSecurityPolicy.setAuthorizationResult(Status.ABORTED);
190+
blockingSecurityPolicy.provideNextCheckAuthorizationResult(Status.ABORTED);
189191
transport.shutdownNow(Status.OK);
190192
HostServices.awaitServiceShutdown();
191193
shutdownAndTerminate(executorServicePool.getObject());

binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,22 @@ public Builder setBindIntentFromComponent(ComponentName component) {
250250
return this;
251251
}
252252

253-
/** See {@link AndroidComponentAddress#getTargetUser()}. */
253+
/**
254+
* Specifies the Android user in which the built Address' bind Intent will be evaluated.
255+
*
256+
* <p>Connecting to a server in a different Android user is uncommon and requires the client app
257+
* have runtime visibility of &#064;SystemApi's and hold certain &#064;SystemApi permissions.
258+
* The device must also be running Android SDK version 30 or higher.
259+
*
260+
* <p>See https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces
261+
* for details on which apps can call the underlying &#064;SystemApi's needed to make this type
262+
* of connection.
263+
*
264+
* <p>One of the "android.permission.INTERACT_ACROSS_XXX" permissions is required. The exact one
265+
* depends on the calling user's relationship to the target user, whether client and server are
266+
* in the same or different apps, and the version of Android in use. See {@link
267+
* Context#bindServiceAsUser}, the essential underlying Android API, for details.
268+
*/
254269
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/10173")
255270
public Builder setTargetUser(@Nullable UserHandle targetUser) {
256271
this.targetUser = targetUser;

binder/src/main/java/io/grpc/binder/ApiConstants.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package io.grpc.binder;
1818

1919
import android.content.Intent;
20-
import android.content.pm.ServiceInfo;
2120
import android.os.UserHandle;
2221
import io.grpc.Attributes;
2322
import io.grpc.EquivalentAddressGroup;
@@ -38,11 +37,13 @@ private ApiConstants() {}
3837
/**
3938
* Specifies the Android user in which target URIs should be resolved.
4039
*
41-
* <p>{@link UserHandle} can't reasonably be encoded in a target URI string. Instead, all
42-
* {@link io.grpc.NameResolverProvider}s producing {@link AndroidComponentAddress}es should let
43-
* clients address servers in another Android user using this argument.
40+
* <p>{@link UserHandle} can't reasonably be encoded in a target URI string. Instead, all {@link
41+
* io.grpc.NameResolverProvider}s producing {@link AndroidComponentAddress}es should let clients
42+
* address servers in another Android user using this argument.
4443
*
45-
* <p>See also {@link AndroidComponentAddress#getTargetUser()}.
44+
* <p>Connecting to a server in a different Android user is uncommon and can only be done by a
45+
* "system app" client with special permissions. See {@link
46+
* AndroidComponentAddress.Builder#setTargetUser(UserHandle)} for details.
4647
*/
4748
public static final NameResolver.Args.Key<UserHandle> TARGET_ANDROID_USER =
4849
NameResolver.Args.Key.create("target-android-user");
@@ -57,11 +58,4 @@ private ApiConstants() {}
5758
@EquivalentAddressGroup.Attr
5859
public static final Attributes.Key<Boolean> PRE_AUTH_SERVER_OVERRIDE =
5960
Attributes.Key.create("pre-auth-server-override");
60-
61-
/**
62-
* The authentic ServiceInfo for an {@link io.grpc.EquivalentAddressGroup} of {@link
63-
* AndroidComponentAddress}es, in case a {@link NameResolver} has already looked it up.
64-
*/
65-
public static final Attributes.Key<ServiceInfo> TARGET_SERVICE_INFO =
66-
Attributes.Key.create("target-service-info");
6761
}

binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public static BinderChannelBuilder forTarget(String target) {
158158
}
159159

160160
private final ManagedChannelImplBuilder managedChannelImplBuilder;
161-
final BinderClientTransportFactory.Builder transportFactoryBuilder;
161+
private final BinderClientTransportFactory.Builder transportFactoryBuilder;
162162

163163
private boolean strictLifecycleManagement;
164164

@@ -242,9 +242,9 @@ public BinderChannelBuilder securityPolicy(SecurityPolicy securityPolicy) {
242242
* specify a {@link UserHandle}. If neither the Channel nor the {@link AndroidComponentAddress}
243243
* specifies a target user, the {@link UserHandle} of the current process will be used.
244244
*
245-
* <p>Targeting a Service in a different Android user is uncommon and requires special permissions
246-
* normally reserved for system apps. See {@link android.content.Context#bindServiceAsUser} for
247-
* details.
245+
* <p>Connecting to a server in a different Android user is uncommon and can only be done by a
246+
* "system app" client with special permissions. See {@link
247+
* AndroidComponentAddress.Builder#setTargetUser(UserHandle)} for details.
248248
*
249249
* @deprecated This method's name is misleading because it implies an impersonated client identity
250250
* when it's actually specifying part of the server's location. It's also no longer necessary

binder/src/main/java/io/grpc/binder/internal/Bindable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ interface Observer {
5050
/**
5151
* Fetches details about the remote Service from PackageManager without binding to it.
5252
*
53-
* <p>Resolving an untrusted address before binding to it lets you screen out unauthorized servers
53+
* <p>Resolving an untrusted address before binding to it lets you screen out problematic servers
5454
* before giving them a chance to run. However, note that the identity/existence of the resolved
5555
* Service can change between the time this method returns and the time you actually bind/connect
5656
* to it. For example, suppose the target package gets uninstalled right after this method

binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import io.grpc.StatusException;
3838
import io.grpc.binder.BinderChannelCredentials;
3939
import java.lang.reflect.Method;
40+
import java.util.List;
4041
import java.util.concurrent.Executor;
4142
import java.util.logging.Level;
4243
import java.util.logging.Logger;
@@ -91,6 +92,8 @@ public String methodName() {
9192
private final Observer observer;
9293
private final Executor mainThreadExecutor;
9394

95+
private static volatile Method queryIntentServicesAsUserMethod;
96+
9497
@GuardedBy("this")
9598
private State state;
9699

@@ -253,19 +256,41 @@ void unbindInternal(Status reason) {
253256
}
254257
}
255258

256-
private static int getIdentifier(UserHandle userHandle) throws ReflectiveOperationException {
257-
return (int) userHandle.getClass().getDeclaredMethod("getIdentifier").invoke(userHandle);
259+
// Sadly the PackageManager#resolveServiceAsUser() API we need isn't part of the SDK or even a
260+
// @SystemApi as of this writing. Modern Android prevents even system apps from calling it, by any
261+
// means (https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces).
262+
// So instead we call queryIntentServicesAsUser(), which does more than we need but *is* a
263+
// @SystemApi in all the SDK versions where we support cross-user Channels.
264+
@Nullable
265+
private static ResolveInfo resolveServiceAsUser(
266+
PackageManager packageManager, Intent intent, int flags, UserHandle targetUserHandle) {
267+
List<ResolveInfo> results =
268+
queryIntentServicesAsUser(packageManager, intent, flags, targetUserHandle);
269+
// The first query result is "what would be returned by resolveService", per the javadoc.
270+
return (results != null && !results.isEmpty()) ? results.get(0) : null;
258271
}
259272

260-
// Sadly we must call this system API reflectively since it isn't part of the Android SDK.
261-
private static ResolveInfo resolveServiceAsUser(
273+
// The cross-user Channel feature requires the client to be a system app so we assume @SystemApi
274+
// queryIntentServicesAsUser() is visible to us at runtime. It would be visible at build time too,
275+
// if our host system app were written to call it directly. We only have to use reflection here
276+
// because grpc-java is a library built outside the Android source tree where the compiler can't
277+
// see the "non-SDK" @SystemApis that we need.
278+
@Nullable
279+
@SuppressWarnings("unchecked") // Safe by PackageManager#queryIntentServicesAsUser spec in AOSP.
280+
private static List<ResolveInfo> queryIntentServicesAsUser(
262281
PackageManager packageManager, Intent intent, int flags, UserHandle targetUserHandle) {
263282
try {
264-
return (ResolveInfo)
265-
packageManager
266-
.getClass()
267-
.getMethod("resolveServiceAsUser", Intent.class, int.class, int.class)
268-
.invoke(packageManager, intent, flags, getIdentifier(targetUserHandle));
283+
if (queryIntentServicesAsUserMethod == null) {
284+
synchronized (ServiceBinding.class) {
285+
if (queryIntentServicesAsUserMethod == null) {
286+
queryIntentServicesAsUserMethod =
287+
PackageManager.class.getMethod(
288+
"queryIntentServicesAsUser", Intent.class, int.class, UserHandle.class);
289+
}
290+
}
291+
}
292+
return (List<ResolveInfo>)
293+
queryIntentServicesAsUserMethod.invoke(packageManager, intent, flags, targetUserHandle);
269294
} catch (ReflectiveOperationException e) {
270295
throw new VerifyException(e);
271296
}

binder/src/test/java/io/grpc/binder/internal/RobolectricBinderTransportTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import io.grpc.internal.ManagedClientTransport;
4747
import io.grpc.internal.ObjectPool;
4848
import io.grpc.internal.SharedResourcePool;
49-
import java.util.Collections;
5049
import java.util.List;
5150
import java.util.concurrent.Executor;
5251
import java.util.concurrent.ScheduledExecutorService;
@@ -208,7 +207,7 @@ public void clientAuthorizesServerUidsInOrder() throws Exception {
208207
serverPkgInfo.applicationInfo.uid = 22222; // UID of the server *app*, which can be different.
209208
shadowOf(application.getPackageManager()).installPackage(serverPkgInfo);
210209
shadowOf(application.getPackageManager()).addOrUpdateService(serviceInfo);
211-
server = newServer(Collections.emptyList());
210+
server = newServer(ImmutableList.of());
212211
server.start(serverListener);
213212

214213
SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy();

0 commit comments

Comments
 (0)