Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public final class BinderChannelSmokeTest {
.setType(MethodDescriptor.MethodType.BIDI_STREAMING)
.build();

AndroidComponentAddress serverAddress;
ManagedChannel channel;
AtomicReference<Metadata> headersCapture = new AtomicReference<>();
AtomicReference<PeerUid> clientUidCapture = new AtomicReference<>();
Expand Down Expand Up @@ -134,7 +135,7 @@ public void setUp() throws Exception {
TestUtils.recordRequestHeadersInterceptor(headersCapture),
PeerUids.newPeerIdentifyingServerInterceptor());

AndroidComponentAddress serverAddress = HostServices.allocateService(appContext);
serverAddress = HostServices.allocateService(appContext);
HostServices.configureService(
serverAddress,
HostServices.serviceParamsBuilder()
Expand All @@ -149,13 +150,15 @@ public void setUp() throws Exception {
.build())
.build());

channel =
BinderChannelBuilder.forAddress(serverAddress, appContext)
channel = newBinderChannelBuilder().build();
}

BinderChannelBuilder newBinderChannelBuilder() {
return BinderChannelBuilder.forAddress(serverAddress, appContext)
.inboundParcelablePolicy(
InboundParcelablePolicy.newBuilder()
.setAcceptParcelableMetadataValues(true)
.build())
.build();
InboundParcelablePolicy.newBuilder()
.setAcceptParcelableMetadataValues(true)
.build());
}

@After
Expand Down Expand Up @@ -185,6 +188,18 @@ public void testBasicCall() throws Exception {
assertThat(doCall("Hello").get()).isEqualTo("Hello");
}

@Test
public void testBasicCallWithLegacyAuthStrategy() throws Exception {
channel = newBinderChannelBuilder().useLegacyAuthStrategy().build();
assertThat(doCall("Hello").get()).isEqualTo("Hello");
}

@Test
public void testBasicCallWithV2AuthStrategy() throws Exception {
channel = newBinderChannelBuilder().useV2AuthStrategy().build();
assertThat(doCall("Hello").get()).isEqualTo("Hello");
}

@Test
public void testPeerUidIsRecorded() throws Exception {
assertThat(doCall("Hello").get()).isEqualTo("Hello");
Expand Down
56 changes: 56 additions & 0 deletions binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,62 @@ public BinderChannelBuilder preAuthorizeServers(boolean preAuthorize) {
return this;
}

/**
* Specifies how and when to authorize a server against this Channel's {@link SecurityPolicy}.
*
* <p>This method selects the original "legacy" authorization strategy, which is no longer
* preferred for two reasons. First, the legacy strategy considers the UID of the server *process*
* we connect to. This is problematic for services using the `android:isolatedProcess` attribute,
* which runs them under a different UID and without any of the privileges of the hosting app.
* Second, the legacy authorization strategy performs SecurityPolicy checks later in the
* handshake, which means the calling UID must be rechecked on every subsequent transaction. For
* these reasons, prefer {@link #useV2AuthStrategy()} instead.
*
* <p>The server does not know which authorization strategy a client is using. Both strategies
* work with all versions of the grpc-binder server.
*
* <p>The default authorization strategy is unspecified. Clients that require the legacy strategy
* should configure it explicitly using this method. Eventually support for the legacy strategy
* will be removed.
*
* @return this
*/
public BinderChannelBuilder useLegacyAuthStrategy() {
transportFactoryBuilder.setUseLegacyAuthStrategy(true);
return this;
}

/**
* Specifies how and when to authorize a server against this Channel's {@link SecurityPolicy}.
*
* <p>This method selects the v2 authorization strategy. It improves on {@link
* #useLegacyAuthStrategy()}, by considering the UID of the server *app* we connect to, rather
* than the server *process*. This allows clients to connect to services using the
* `android:isolatedProcess` attribute, which runs them under a different ephemeral UID and
* without any of the privileges of the hosting app.
*
* <p>Furthermore, the v2 authorization strategy performs SecurityPolicy checks earlier the
* handshake, which allows subsequent transactions over the connection to proceed securely without
* further UID checks. For these reasons, clients should prefer the v2 strategy.
*
* <p>The server does not know which authorization strategy a client is using. Both strategies
* work with all versions of the grpc-binder server.
*
* <p>The default authorization strategy is unspecified. Clients that require the v2 strategy
* should configure it explicitly using this method. Eventually support for the legacy strategy
* will be removed.
*
* <p>If moving to the new authorization strategy causes a robolectric test to fail, ensure your
* fake Service component is registered with `ShadowPackageManager` using `addOrUpdateService()`.
*
* @return this
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/12397")
public BinderChannelBuilder useV2AuthStrategy() {
transportFactoryBuilder.setUseLegacyAuthStrategy(false);
return this;
}

@Override
public BinderChannelBuilder idleTimeout(long value, TimeUnit unit) {
checkState(
Expand Down
22 changes: 20 additions & 2 deletions binder/src/main/java/io/grpc/binder/internal/Bindable.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,11 @@ interface Observer {
* before giving them a chance to run. However, note that the identity/existence of the resolved
* Service can change between the time this method returns and the time you actually bind/connect
* to it. For example, suppose the target package gets uninstalled or upgraded right after this
* method returns. In {@link Observer#onBound}, you should verify that the server you resolved is
* the same one you connected to.
* method returns.
*
* <p>Compare with {@link #getConnectedServiceInfo()}, which can only be called after {@link
* Observer#onBound(IBinder)} but can be used to learn about the service you actually connected
* to.
*/
@AnyThread
ServiceInfo resolve() throws StatusException;
Expand All @@ -68,6 +71,21 @@ interface Observer {
@AnyThread
void bind();

/**
* Asks PackageManager for details about the remote Service we *actually* connected to.
*
* <p>Can only be called after {@link Observer#onBound}.
*
* <p>Compare with {@link #resolve()}, which reports which service would be selected as of now but
* *without* connecting.
*
* @throws StatusException UNIMPLEMENTED if the connected service isn't found (an {@link
* Observer#onUnbound} callback has likely already happened or is on its way!)
* @throws IllegalStateException if {@link Observer#onBound} has not "happened-before" this call
*/
@AnyThread
ServiceInfo getConnectedServiceInfo() throws StatusException;

/**
* Unbind from the remote service if connected.
*
Expand Down
Loading