mirror of https://github.com/grpc/grpc-java.git
binder: oneway txns cannot arrive out of order (#10754)
See https://developer.android.com/reference/android/os/IBinder#FLAG_ONEWAY
This commit is contained in:
parent
91d15ce4e6
commit
5e07310d06
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.grpc.binder.internal;
|
package io.grpc.binder.internal;
|
||||||
|
|
||||||
|
import static android.os.IBinder.FLAG_ONEWAY;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
@ -46,18 +47,28 @@ public final class LeakSafeOneWayBinderTest {
|
||||||
@Test
|
@Test
|
||||||
public void testTransaction() {
|
public void testTransaction() {
|
||||||
Parcel p = Parcel.obtain();
|
Parcel p = Parcel.obtain();
|
||||||
assertThat(binder.onTransact(123, p, null, 0)).isTrue();
|
assertThat(binder.onTransact(123, p, null, FLAG_ONEWAY)).isTrue();
|
||||||
assertThat(transactionsHandled).isEqualTo(1);
|
assertThat(transactionsHandled).isEqualTo(1);
|
||||||
assertThat(lastCode).isEqualTo(123);
|
assertThat(lastCode).isEqualTo(123);
|
||||||
assertThat(lastParcel).isSameInstanceAs(p);
|
assertThat(lastParcel).isSameInstanceAs(p);
|
||||||
p.recycle();
|
p.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDropsTwoWayTransactions() {
|
||||||
|
Parcel p = Parcel.obtain();
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
assertThat(binder.onTransact(123, p, reply, 0)).isFalse();
|
||||||
|
assertThat(transactionsHandled).isEqualTo(0);
|
||||||
|
p.recycle();
|
||||||
|
reply.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDetach() {
|
public void testDetach() {
|
||||||
Parcel p = Parcel.obtain();
|
Parcel p = Parcel.obtain();
|
||||||
binder.detach();
|
binder.detach();
|
||||||
assertThat(binder.onTransact(456, p, null, 0)).isFalse();
|
assertThat(binder.onTransact(456, p, null, FLAG_ONEWAY)).isFalse();
|
||||||
|
|
||||||
// The transaction shouldn't have been processed.
|
// The transaction shouldn't have been processed.
|
||||||
assertThat(transactionsHandled).isEqualTo(0);
|
assertThat(transactionsHandled).isEqualTo(0);
|
||||||
|
|
@ -68,8 +79,8 @@ public final class LeakSafeOneWayBinderTest {
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleTransactions() {
|
public void testMultipleTransactions() {
|
||||||
Parcel p = Parcel.obtain();
|
Parcel p = Parcel.obtain();
|
||||||
assertThat(binder.onTransact(123, p, null, 0)).isTrue();
|
assertThat(binder.onTransact(123, p, null, FLAG_ONEWAY)).isTrue();
|
||||||
assertThat(binder.onTransact(456, p, null, 0)).isTrue();
|
assertThat(binder.onTransact(456, p, null, FLAG_ONEWAY)).isTrue();
|
||||||
assertThat(transactionsHandled).isEqualTo(2);
|
assertThat(transactionsHandled).isEqualTo(2);
|
||||||
assertThat(lastCode).isEqualTo(456);
|
assertThat(lastCode).isEqualTo(456);
|
||||||
assertThat(lastParcel).isSameInstanceAs(p);
|
assertThat(lastParcel).isSameInstanceAs(p);
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import android.os.TransactionTooLargeException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Ticker;
|
import com.google.common.base.Ticker;
|
||||||
|
import com.google.common.base.Verify;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import io.grpc.Attributes;
|
import io.grpc.Attributes;
|
||||||
import io.grpc.CallOptions;
|
import io.grpc.CallOptions;
|
||||||
|
|
@ -463,14 +464,11 @@ public abstract class BinderTransport
|
||||||
if (inbound == null) {
|
if (inbound == null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!isShutdown()) {
|
if (!isShutdown()) {
|
||||||
// Create a new inbound. Strictly speaking we could end up doing this twice on
|
|
||||||
// two threads, hence the need to use putIfAbsent, and check its result.
|
|
||||||
inbound = createInbound(code);
|
inbound = createInbound(code);
|
||||||
if (inbound != null) {
|
if (inbound != null) {
|
||||||
Inbound<?> inbound2 = ongoingCalls.putIfAbsent(code, inbound);
|
Inbound<?> existing = ongoingCalls.put(code, inbound);
|
||||||
if (inbound2 != null) {
|
// Can't happen as only one invocation of handleTransaction() is running at a time.
|
||||||
inbound = inbound2;
|
Verify.verify(existing == null, "impossible appearance of %s", existing);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package io.grpc.binder.internal;
|
package io.grpc.binder.internal;
|
||||||
|
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import io.grpc.Internal;
|
import io.grpc.Internal;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
@ -42,6 +43,21 @@ public final class LeakSafeOneWayBinder extends Binder {
|
||||||
|
|
||||||
@Internal
|
@Internal
|
||||||
public interface TransactionHandler {
|
public interface TransactionHandler {
|
||||||
|
/**
|
||||||
|
* Delivers a binder transaction to this handler.
|
||||||
|
*
|
||||||
|
* <p>Implementations need not be thread-safe. Each invocation "happens-before" the next in the
|
||||||
|
* same order that transactions were sent ("oneway" semantics). However implementations must not
|
||||||
|
* be thread-hostile as different calls can come in on different threads.
|
||||||
|
*
|
||||||
|
* <p>{@code parcel} is only valid for the duration of this call. Ownership is retained by the
|
||||||
|
* caller.
|
||||||
|
*
|
||||||
|
* @param code the transaction code originally passed to {@link IBinder#transact}
|
||||||
|
* @param code a copy of the parcel originally passed to {@link IBinder#transact}.
|
||||||
|
* @return the value to return from {@link Binder#onTransact}. NB: "oneway" semantics mean this
|
||||||
|
* result will not delivered to the caller of {@link IBinder#transact}
|
||||||
|
*/
|
||||||
boolean handleTransaction(int code, Parcel data);
|
boolean handleTransaction(int code, Parcel data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,6 +76,10 @@ public final class LeakSafeOneWayBinder extends Binder {
|
||||||
TransactionHandler handler = this.handler;
|
TransactionHandler handler = this.handler;
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
try {
|
try {
|
||||||
|
if ((flags & IBinder.FLAG_ONEWAY) == 0) {
|
||||||
|
logger.log(Level.WARNING, "ignoring non-oneway transaction. flags=" + flags);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return handler.handleTransaction(code, parcel);
|
return handler.handleTransaction(code, parcel);
|
||||||
} catch (RuntimeException re) {
|
} catch (RuntimeException re) {
|
||||||
logger.log(Level.WARNING, "failure sending transaction " + code, re);
|
logger.log(Level.WARNING, "failure sending transaction " + code, re);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue