Skip to content

Commit efcdebb

Browse files
authored
Introduce a NameResolver for Android's intent: URIs (#12248)
Let grpc-binder clients find on-device services by [implicit Intent](https://developer.android.com/guide/components/intents-filters#Types) target URI, lifting the need to hard code a server's package name.
1 parent f30964a commit efcdebb

File tree

11 files changed

+1117
-9
lines changed

11 files changed

+1117
-9
lines changed

api/src/main/java/io/grpc/NameResolverRegistry.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ static List<Class<?>> getHardCodedClasses() {
166166
} catch (ClassNotFoundException e) {
167167
logger.log(Level.FINE, "Unable to find DNS NameResolver", e);
168168
}
169+
try {
170+
list.add(Class.forName("io.grpc.binder.internal.IntentNameResolverProvider"));
171+
} catch (ClassNotFoundException e) {
172+
logger.log(Level.FINE, "Unable to find IntentNameResolverProvider", e);
173+
}
169174
return Collections.unmodifiableList(list);
170175
}
171176

binder/src/androidTest/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
<service android:name="io.grpc.binder.HostServices$HostService1" android:exported="false">
1212
<intent-filter>
1313
<action android:name="action1"/>
14+
<data android:scheme="scheme" android:host="authority" android:path="/path"/>
1415
</intent-filter>
1516
</service>
1617
<service android:name="io.grpc.binder.HostServices$HostService2" android:exported="false">
1718
<intent-filter>
1819
<action android:name="action2"/>
20+
<data android:scheme="scheme" android:host="authority" android:path="/path"/>
1921
</intent-filter>
2022
</service>
2123
</application>

binder/src/androidTest/java/io/grpc/binder/BinderChannelSmokeTest.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import android.content.Context;
2525
import android.content.Intent;
26+
import android.net.Uri;
2627
import android.os.Parcel;
2728
import android.os.Parcelable;
2829
import androidx.test.core.app.ApplicationProvider;
@@ -39,7 +40,6 @@
3940
import io.grpc.ManagedChannel;
4041
import io.grpc.Metadata;
4142
import io.grpc.MethodDescriptor;
42-
import io.grpc.NameResolverRegistry;
4343
import io.grpc.ServerCall;
4444
import io.grpc.ServerCall.Listener;
4545
import io.grpc.ServerCallHandler;
@@ -49,7 +49,6 @@
4949
import io.grpc.Status.Code;
5050
import io.grpc.StatusRuntimeException;
5151
import io.grpc.internal.GrpcUtil;
52-
import io.grpc.internal.testing.FakeNameResolverProvider;
5352
import io.grpc.stub.ClientCalls;
5453
import io.grpc.stub.MetadataUtils;
5554
import io.grpc.stub.ServerCalls;
@@ -77,7 +76,6 @@ public final class BinderChannelSmokeTest {
7776

7877
private static final int SLIGHTLY_MORE_THAN_ONE_BLOCK = 16 * 1024 + 100;
7978
private static final String MSG = "Some text which will be repeated many many times";
80-
private static final String SERVER_TARGET_URI = "fake://server";
8179
private static final Metadata.Key<PoisonParcelable> POISON_KEY =
8280
ParcelableUtils.metadataKey("poison-bin", PoisonParcelable.CREATOR);
8381

@@ -99,7 +97,6 @@ public final class BinderChannelSmokeTest {
9997
.setType(MethodDescriptor.MethodType.BIDI_STREAMING)
10098
.build();
10199

102-
FakeNameResolverProvider fakeNameResolverProvider;
103100
ManagedChannel channel;
104101
AtomicReference<Metadata> headersCapture = new AtomicReference<>();
105102
AtomicReference<PeerUid> clientUidCapture = new AtomicReference<>();
@@ -138,8 +135,6 @@ public void setUp() throws Exception {
138135
PeerUids.newPeerIdentifyingServerInterceptor());
139136

140137
AndroidComponentAddress serverAddress = HostServices.allocateService(appContext);
141-
fakeNameResolverProvider = new FakeNameResolverProvider(SERVER_TARGET_URI, serverAddress);
142-
NameResolverRegistry.getDefaultRegistry().register(fakeNameResolverProvider);
143138
HostServices.configureService(
144139
serverAddress,
145140
HostServices.serviceParamsBuilder()
@@ -166,7 +161,6 @@ public void setUp() throws Exception {
166161
@After
167162
public void tearDown() throws Exception {
168163
channel.shutdownNow();
169-
NameResolverRegistry.getDefaultRegistry().deregister(fakeNameResolverProvider);
170164
HostServices.awaitServiceShutdown();
171165
}
172166

@@ -235,7 +229,11 @@ public void testStreamingCallOptionHeaders() throws Exception {
235229

236230
@Test
237231
public void testConnectViaTargetUri() throws Exception {
238-
channel = BinderChannelBuilder.forTarget(SERVER_TARGET_URI, appContext).build();
232+
// Compare with the <intent-filter> mapping in AndroidManifest.xml.
233+
channel =
234+
BinderChannelBuilder.forTarget(
235+
"intent://authority/path#Intent;action=action1;scheme=scheme;end;", appContext)
236+
.build();
239237
assertThat(doCall("Hello").get()).isEqualTo("Hello");
240238
}
241239

@@ -245,7 +243,10 @@ public void testConnectViaIntentFilter() throws Exception {
245243
channel =
246244
BinderChannelBuilder.forAddress(
247245
AndroidComponentAddress.forBindIntent(
248-
new Intent().setAction("action1").setPackage(appContext.getPackageName())),
246+
new Intent()
247+
.setAction("action1")
248+
.setData(Uri.parse("scheme://authority/path"))
249+
.setPackage(appContext.getPackageName())),
249250
appContext)
250251
.build();
251252
assertThat(doCall("Hello").get()).isEqualTo("Hello");

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ private ApiConstants() {}
3434
*/
3535
public static final String ACTION_BIND = "grpc.io.action.BIND";
3636

37+
/**
38+
* Gives a {@link NameResolver} access to its Channel's "source" {@link android.content.Context},
39+
* the entry point to almost every other Android API.
40+
*
41+
* <p>This argument is set automatically by {@link BinderChannelBuilder}. Any value passed to
42+
* {@link io.grpc.ManagedChannelBuilder#setNameResolverArg} will be ignored.
43+
*
44+
* <p>See {@link BinderChannelBuilder#forTarget(String, android.content.Context)} for more.
45+
*/
46+
public static final NameResolver.Args.Key<android.content.Context> SOURCE_ANDROID_CONTEXT =
47+
NameResolver.Args.Key.create("source-android-context");
48+
3749
/**
3850
* Specifies the Android user in which target URIs should be resolved.
3951
*

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,8 @@ public BinderChannelBuilder idleTimeout(long value, TimeUnit unit) {
321321
public ManagedChannel build() {
322322
transportFactoryBuilder.setOffloadExecutorPool(
323323
managedChannelImplBuilder.getOffloadExecutorPool());
324+
setNameResolverArg(
325+
ApiConstants.SOURCE_ANDROID_CONTEXT, transportFactoryBuilder.getSourceContext());
324326
return super.build();
325327
}
326328
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ public Builder setSourceContext(Context sourceContext) {
142142
return this;
143143
}
144144

145+
public Context getSourceContext() {
146+
return sourceContext;
147+
}
148+
145149
public Builder setOffloadExecutorPool(ObjectPool<? extends Executor> offloadExecutorPool) {
146150
this.offloadExecutorPool = checkNotNull(offloadExecutorPool, "offloadExecutorPool");
147151
return this;

0 commit comments

Comments
 (0)