grpc-java Documentation

The Java gRPC implementation. HTTP/2 based RPC

12,060 stars Java Java 1 file ยท ~11,330 tokens #grpc#java#microservices#proto#rpc
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy grpc-java on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name grpc-java --restart always grpc/grpc-java:latest

File: README.md

gRPC-Java - An RPC library and framework

Homepage: grpc.io
Mailing List: [email protected]




Supported Platforms

gRPC-Java supports Java 8 and later. Android minSdkVersion 24 (Nougat) and
later are supported with Java 8 language desugaring.

TLS usage on Android typically requires Play Services Dynamic Security Provider.
Please see the Security Readme.

Older Java versions are not directly supported, but a branch remains available
for fixes and releases. See gRFC P5 JDK Version Support
Policy
.

Java version gRPC Branch
7 1.41.x

Getting Started

For a guided tour, take a look at the quick start
guide
or the more explanatory gRPC
basics
.

The examples and the
Android example
are standalone projects that showcase the usage of gRPC.

Download

Download the JARs. Or for Maven with non-Android, add to your pom.xml:

xml
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-netty-shaded</artifactId>
  <version>1.83.1</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-protobuf</artifactId>
  <version>1.83.1</version>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-stub</artifactId>
  <version>1.83.1</version>
</dependency>

Or for Gradle with non-Android, add to your dependencies:

gradle
runtimeOnly 'io.grpc:grpc-netty-shaded:1.83.1'
implementation 'io.grpc:grpc-protobuf:1.83.1'
implementation 'io.grpc:grpc-stub:1.83.1'

For Android client, use grpc-okhttp instead of grpc-netty-shaded and
grpc-protobuf-lite instead of grpc-protobuf:

gradle
implementation 'io.grpc:grpc-okhttp:1.83.1'
implementation 'io.grpc:grpc-protobuf-lite:1.83.1'
implementation 'io.grpc:grpc-stub:1.83.1'

For Bazel, you can either
use Maven
(with the GAVs from above), or use @io_grpc_grpc_java//api et al (see below).

Development snapshots are available in Sonatypes's snapshot
repository
.

Generated Code

For protobuf-based codegen, you can put your proto files in the src/main/proto
and src/test/proto directories along with an appropriate plugin.

For protobuf-based codegen integrated with the Maven build system, you can use
protobuf-maven-plugin (Eclipse and NetBeans users should also look at
os-maven-plugin's
IDE documentation):

xml
<extensions>
    <extension>
      <groupId>kr.motd.maven</groupId>
      <artifactId>os-maven-plugin</artifactId>
      <version>1.7.1</version>
    </extension>
  </extensions>
  
    
      <groupId>org.xolstice.maven.plugins</groupId>
      <artifactId>protobuf-maven-plugin</artifactId>
      <version>0.6.1</version>
      <configuration>
        com.google.protobuf:protoc:3.25.8:exe:${os.detected.classifier}
        grpc-java
        io.grpc:protoc-gen-grpc-java:1.83.1:exe:${os.detected.classifier}
      </configuration>
      <executions>
        <execution>
          <goals>
            <goal>compile</goal>
            <goal>compile-custom</goal>
          </goals>
        </execution>
      </executions>

For non-Android protobuf-based codegen integrated with the Gradle build system,
you can use protobuf-gradle-plugin:

gradle
plugins {
    id 'com.google.protobuf' version '0.9.5'
}

protobuf {
  protoc {
    artifact = "com.google.protobuf:protoc:3.25.8"
  }
  plugins {
    grpc {
      artifact = 'io.grpc:protoc-gen-grpc-java:1.83.1'
    }
  }
  generateProtoTasks {
    all()*.plugins {
      grpc {}
    }
  }
}

The prebuilt protoc-gen-grpc-java binary uses glibc on Linux. If you are
compiling on Alpine Linux, you may want to use the Alpine grpc-java package
which uses musl instead.

For Android protobuf-based codegen integrated with the Gradle build system, also
use protobuf-gradle-plugin but specify the 'lite' options:

gradle
plugins {
    id 'com.google.protobuf' version '0.9.5'
}

protobuf {
  protoc {
    artifact = "com.google.protobuf:protoc:3.25.8"
  }
  plugins {
    grpc {
      artifact = 'io.grpc:protoc-gen-grpc-java:1.83.1'
    }
  }
  generateProtoTasks {
    all().each { task ->
      task.builtins {
        java { option 'lite' }
      }
      task.plugins {
        grpc { option 'lite' }
      }
    }
  }
}

For Bazel, use the proto_library
and the java_proto_library (no load() required)
and load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library") (from this project), as in
this example BUILD.bazel.

API Stability

APIs annotated with @Internal are for internal use by the gRPC library and
should not be used by gRPC users. APIs annotated with @ExperimentalApi are
subject to change in future releases, and library code that other projects
may depend on should not use these APIs.

We recommend using the
grpc-java-api-checker
(an Error Prone plugin)
to check for usages of @ExperimentalApi and @Internal in any library code
that depends on gRPC. It may also be used to check for @Internal usage or
unintended @ExperimentalApi consumption in non-library code.

How to Build

If you are making changes to gRPC-Java, see the compiling
instructions
.

High-level Components

At a high level there are three distinct layers to the library: Stub,
Channel, and Transport.

Stub

The Stub layer is what is exposed to most developers and provides type-safe
bindings to whatever datamodel/IDL/interface you are adapting. gRPC comes with
a plugin to the
protocol-buffers compiler that generates Stub interfaces out of .proto files,
but bindings to other datamodel/IDL are easy and encouraged.

Channel

The Channel layer is an abstraction over Transport handling that is suitable for
interception/decoration and exposes more behavior to the application than the
Stub layer. It is intended to be easy for application frameworks to use this
layer to address cross-cutting concerns such as logging, monitoring, auth, etc.

Transport

The Transport layer does the heavy lifting of putting and taking bytes off the
wire. The interfaces to it are abstract just enough to allow plugging in of
different implementations. Note the transport layer API is considered internal
to gRPC and has weaker API guarantees than the core API under package io.grpc.

gRPC comes with multiple Transport implementations:

  1. The Netty-based HTTP/2 transport is the main transport implementation based
    on Netty. It is not officially supported on Android.
    There is a "grpc-netty-shaded" version of this transport. It is generally
    preferred over using the Netty-based transport directly as it requires less
    dependency management and is easier to upgrade within many applications.
  2. The OkHttp-based HTTP/2 transport is a lightweight transport based on
    Okio and forked low-level parts of
    OkHttp. It is mainly for use on Android.
  3. The in-process transport is for when a server is in the same process as the
    client. It is used frequently for testing, while also being safe for
    production use.
  4. The Binder transport is for Android cross-process communication on a single
    device.

File: android-interop-testing/README.md

gRPC Android test App

Implements gRPC integration tests in an Android App.

In order to build this app, you need a local.properties file under this directory which specifies
the location of your android sdk:

text
sdk.dir=/somepath/somepath/sdk

Connect your Android device or start the emulator:

text
$ ./start-emulator.sh <AVD name> & ./wait-for-emulator.sh

Start test server

Start the test server by:

text
$ ../run-test-server.sh

Manually test

Install the App by:

text
$ ../gradlew installDebug

Then manually test it with the UI.

Instrumentation tests

Instrumentation tests must be run on a connected device or emulator. Run with the
following gradle command:

text
$ ../gradlew connectedAndroidTest \
    -Pandroid.testInstrumentationRunnerArguments.server_host=10.0.2.2 \
    -Pandroid.testInstrumentationRunnerArguments.server_port=8080 \
    -Pandroid.testInstrumentationRunnerArguments.use_tls=true \
    -Pandroid.testInstrumentationRunnerArguments.server_host_override=foo.test.google.fr \
    -Pandroid.testInstrumentationRunnerArguments.use_test_ca=true \
    -Pandroid.testInstrumentationRunnerArguments.test_case=all

File: benchmarks/README.md

grpc Benchmarks

QPS Benchmark

The "Queries Per Second Benchmark" allows you to get a quick overview of the throughput and latency characteristics of grpc.

To build the benchmark type

text
$ ./gradlew :grpc-benchmarks:installDist

from the grpc-java directory.

You can now find the client and the server executables in benchmarks/build/install/grpc-benchmarks/bin.

The C++ counterpart can be found at https://github.com/grpc/grpc/tree/master/test/cpp/qps

The netty benchmark directory contains
the standard benchmarks used to assess the performance of GRPC. Since these
benchmarks run on localhost over loopback the performance of the underlying network is considerably
different to real networks and their behavior. To address this issue we recommend the use of
a network emulator to make loopback behave more like a real network. To this end the benchmark
code looks for a loopback interface with 'benchmark' in its name and attempts to use the address
bound to that interface when creating the client and server. If it cannot find such an interface it
will print a warning and continue with the default localhost address.

To attempt to standardize benchmark behavior across machines we attempt to emulate a 10gbit
ethernet interface with a packet delay of 0.1ms.

Linux

On Linux we can use netem to shape the traffic appropriately.

sh
# Remove all traffic shaping from loopback
sudo tc qdisc del dev lo root
# Add a priority traffic class to the root of loopback
sudo tc qdisc add dev lo root handle 1: prio
# Add a qdisc under the new class with the appropriate shaping
sudo tc qdisc add dev lo parent 1:1 handle 2: netem delay 0.1ms rate 10gbit
# Add a filter which selects the new qdisc class for traffic to 127.127.127.127
sudo tc filter add dev lo parent 1:0 protocol ip prio 1 u32 match ip dst 127.127.127.127 flowid 2:1
# Create an interface alias call 'lo:benchmark' that maps 127.127.127.127 to loopback
sudo ip addr add dev lo 127.127.127.127/32 label lo:benchmark

to remove this configuration

sh
sudo tc qdisc del dev lo root
sudo ip addr del dev lo 127.127.127.127/32 label lo:benchmark

Other Platforms

Contributions are welcome!

Visualizing the Latency Distribution

The QPS client comes with the option --save_histogram=FILE, if set it serializes the histogram to FILE which can then be used with a plotter to visualize the latency distribution. The histogram is stored in the file format of HdrHistogram. That way it can be plotted very easily using a browser based tool like https://hdrhistogram.github.io/HdrHistogram/plotFiles.html. Simply upload the generated file and it will generate a beautiful graph for you. It also allows you to plot two or more histograms on the same surface in order two easily compare latency distributions.

JVM Options

When running a benchmark it's often useful to adjust some JVM options to improve performance and to gain some insights into what's happening. Passing JVM options to the QPS server and client is as easy as setting the JAVA_OPTS environment variables. Below are some options that I find very useful:

  • -Xms gives a lower bound on the heap to allocate and -Xmx gives an upper bound. If your program uses more than what's specified in -Xmx the JVM will exit with an OutOfMemoryError. When setting those always set Xms and Xmx to the same value. The reason for this is that the young and old generation are sized according to the total available heap space. So if the total heap gets resized, they will also have to be resized and this will then trigger a full GC.
  • -verbose:gc prints some basic information about garbage collection. It will log to stdout whenever a GC happend and will tell you about the kind of GC, pause time and memory compaction.
  • -XX:+PrintGCDetails prints out very detailed GC and heap usage information before the program terminates.
  • -XX:-HeapDumpOnOutOfMemoryError and -XX:HeapDumpPath=path when you are pushing the JVM hard it sometimes happens that it will crash due to the lack of available heap space. This option will allow you to dive into the details of why it happened. The heap dump can be viewed with e.g. the Eclipse Memory Analyzer.
  • -XX:+PrintCompilation will give you a detailed overview of what gets compiled, when it gets compiled, by which HotSpot compiler it gets compiled and such. It's a lot of output. I usually just redirect it to file and look at it with less and grep.
  • -XX:+PrintInlining will give you a detailed overview of what gets inlined and why some methods didn't get inlined. The output is very verbose and like -XX:+PrintCompilation and useful to look at after some major changes or when a drop in performance occurs.
  • It sometimes happens that a benchmark just doesn't make any progress, that is no bytes are transferred over the network, there is hardly any CPU utilization and low memory usage but the benchmark is still running. In that case it's useful to get a thread dump and see what's going on. HotSpot ships with a tool called jps and jstack. jps tells you the process id of all running JVMs on the machine, which you can then pass to jstack and it will print a thread dump of this JVM.
  • Taking a heap dump of a running JVM is similarly straightforward. First get the process id with jps and then use jmap to take the heap dump. You will almost always want to run it with -dump:live in order to only dump live objects. If possible, try to size the heap of your JVM (-Xmx) as small as possible in order to also keep the heap dump small. Large heap dumps are very painful and slow to analyze.

Profiling

Newer JVMs come with a built-in profiler called Java Flight Recorder. It's an excellent profiler and it can be used to start a recording directly on the command line, from within Java Mission Control or
with jcmd.

A good introduction on how it works and how to use it are http://hirt.se/blog/?p=364 and http://hirt.se/blog/?p=370.


File: compiler/README.md

gRPC Java Codegen Plugin for Protobuf Compiler

This generates the Java interfaces out of the service definition from a
.proto file. It works with the Protobuf Compiler (protoc).

Normally you don't need to compile the codegen by yourself, since pre-compiled
binaries for common platforms are available on Maven Central:

  1. Navigate to https://mvnrepository.com/artifact/io.grpc/protoc-gen-grpc-java
  2. Click into a version
  3. Click "Files"

However, if the pre-compiled binaries are not compatible with your system,
you may want to build your own codegen.

Compiling and testing the codegen

Set up your system as described in COMPILING.md.

Then change to the compiler directory:

text
$ cd $GRPC_JAVA_ROOT/compiler

To compile the plugin:

text
$ ../gradlew java_pluginExecutable

To test the plugin with the compiler:

text
$ ../gradlew test

You will see a PASS if the test succeeds.

To compile a proto file and generate Java interfaces out of the service definitions:

text
$ protoc --plugin=protoc-gen-grpc-java=build/exe/java_plugin/protoc-gen-grpc-java \
  --grpc-java_out="$OUTPUT_FILE" --proto_path="$DIR_OF_PROTO_FILE" "$PROTO_FILE"

To generate Java interfaces with protobuf lite:

text
$ protoc --plugin=protoc-gen-grpc-java=build/exe/java_plugin/protoc-gen-grpc-java \
  --grpc-java_out=lite:"$OUTPUT_FILE" --proto_path="$DIR_OF_PROTO_FILE" "$PROTO_FILE"

Installing the codegen to Maven local repository

This will compile a codegen and put it under your ~/.m2/repository. This
will make it available to any build tool that pulls codegens from Maven
repositories.

text
$ ../gradlew publishToMavenLocal

Creating a release of gRPC Java

Please follow the instructions in RELEASING.md under the root directory for
details on how to create a new release.


File: cronet/README.md

gRPC Cronet Transport

EXPERIMENTAL: gRPC's Cronet transport is an experimental API. Its stability
depends on upstream Cronet's implementation, which involves some experimental features.

This code enables using the Chromium networking stack
(Cronet)

as the transport layer for gRPC on Android. This lets your Android app make
RPCs using the same networking stack as used in the Chrome browser.

Some advantages of using Cronet with gRPC:

  • Bundles an OpenSSL implementation, enabling TLS connections even on older
    versions of Android without additional configuration
  • Robust to Android network connectivity changes
  • Support for QUIC

Since gRPC's 1.24 release, the grpc-cronet package provides access to the
CronetChannelBuilder class. Cronet jars are available on Google's Maven repository.
See the example app at https://github.com/GoogleChrome/cronet-sample/blob/master/README.md.

Example usage:

In your app module's build.gradle file, include a dependency on both
io.grpc:grpc-cronet and the Google Play Services Client Library for Cronet,
com.google.android.gms:play-services-cronet.

In cases where Cronet cannot be loaded from Google Play services, there is a less performant
implementation of Cronet's API that can be used. Depend on org.chromium.net:cronet-fallback
to use this fall-back implementation.

You will also need permission to access the device's network state in your
AndroidManifest.xml:

text

Once the above steps are completed, you can create a gRPC Cronet channel as
follows:

text
import io.grpc.cronet.CronetChannelBuilder;
import org.chromium.net.ExperimentalCronetEngine;

...

ExperimentalCronetEngine engine =
    new ExperimentalCronetEngine.Builder(context /* Android Context */).build();
ManagedChannel channel = CronetChannelBuilder.forAddress("localhost", 8080, engine).build();

File: documentation/android-binderchannel-status-codes.md

Android gRPC/BinderChannel Status Codes

Background

BinderChannel is a gRPC transport that lets Android apps communicate across processes using familiar gRPC concepts and APIs. A BinderChannel-backed gRPC request can fail for many Android-specific reasons, both at ServiceConnection establishment and at transact() time. These transport-specific failures must be reported to clients using gRPCโ€™s standard canonical status code abstraction. This document enumerates the BinderChannel errors one can expect to encounter, specifies a canonical status code mapping for each possibility and discusses how clients should handle them.

Status Code Mapping

Consider the table that follows as an BinderChannel-specific addendum to the โ€œCodes that may be returned by the gRPC librariesโ€ document. Mappings in that table that share a status code with one of the binder-specific mappings are repeated here for comparison.

# Error Case Android API Manifestation Status Code Expected Client Handling
0 Server app not visible. bindService() returns false UNIMPLEMENTEDโ€œThe operation is not implemented or is not supported / enabled in this service.โ€ Give up - This is an error in the client manifest.
1 Safer Intents violation. Direct the user to install/reinstall the server app.
2 Server app not installed
3 Old version of the server app doesnโ€™t declare the target android.app.Service in its manifest.
4 Target android.app.Service is disabled
5 The whole server app is disabled
6 Server app predates the Android M permissions model and the user must review and approve some newly requested permissions before it can run.
7 Target android.app.Service doesnโ€™t recognize grpc binding Intent (old version of server app?) onNullBinding() ServiceConnection callback
8 Method not found on the io.grpc.Server (old version of server app?) N/A
9 Request cardinality violation (old version of server app expects unary rather than streaming, say)
10 Old version of the server app exposes target android.app.Service but doesnโ€™t android:export it. bindService() throws SecurityException PERMISSION_DENIED
โ€œThe caller does not have permission to execute the specified operation โ€ฆโ€ Direct the user to update the server app in the hopes that a newer version fixes this error in its manifest.
11 Target android.app.Service requires an <android:permission> that client doesnโ€™t hold. Prompt the user to grant the needed Android permission
12 Violations of the security policy for miscellaneous Android features like android:isolatedProcess, android:externalService, android:singleUser, instant apps, BIND_TREAT_LIKE_ACTIVITY, etc, Give up - This is a programming or packaging error that only the app developer can fix.
13 Calling Android UID not allowed by ServerSecurityPolicy N/A
14 Server Android UID not allowed by clientโ€™s SecurityPolicy
15 Server process crashed or killed with request in flight. onDisconnected() ServiceConnection callback UNAVAILABLE

โ€œThe service is currently unavailable. This is most likely a transient condition, which can be corrected by retrying with a backoff ...โ€ | Retry with exponential backoff and deadline (see ManagedChannelBuilder#enableRetry() |
| onBinderDied() IBinder.DeathRecipient callback | | | | |
| IBinder.transact() throws DeadObjectException | | | | |
| 16 | Server app is currently being upgraded to a new version | onBindingDied() ServiceConnection callback | | |
| 17 | The whole server app or the target android.app.Service was disabled | | | |
| 18 | Binder transaction buffer overflow | IBinder.transact() throws TransactionTooLargeException | | |
| 19 | Source Context for bindService() is destroyed with a request in flight | onDestroy() | CANCELLED

โ€œThe operation was cancelled, typically by the caller.โ€ | Give up for now.

(Re. 19: The caller can try again later when the user opens the source Activity or restarts the source Service) |
| 20 | Client application cancelled the request | N/A | | |
| 21 | Bug in Android itself or the way the io.grpc.binder transport uses it. | IBinder.transact() returns false | INTERNAL

โ€œThis means that some invariants expected by the underlying system have been broken. โ€ฆ Reserved for serious errors.โ€ | Give up - This is a programming error that only the app or grpc developers can fix |
| bindService() throws IllegalArgumentException | | | | |
| 22 | Flow-control protocol violation | N/A | | |
| 23 | Canโ€™t parse request/response proto | | | |

Ambiguity

We say a status code is ambiguous if it maps to two error cases that reasonable clients want to handle differently. For instance, a client may have good reasons to handle error cases 10 and 11 above differently. But they canโ€™t do so based on status code alone because those error cases map to the same one.

In contrast, for example, even though error case 19 and 20 both map to the status code (CANCELLED), they are not ambiguous because we see no reason that clients would want to distinguish them. In both cases, clients will simply give up on the request.

Ambiguity of PERMISSION_DENIED and Mitigations

The mapping above has only one apparently ambiguous status code: PERMISSION_DENIED. However, this isnโ€™t so bad because of the following:

The use of <android:permission>s for inter-app IPC access control (error case 11) is uncommon. Instead, we recommend that server apps only allow IPC from a limited set of client apps known in advance and identified by signature.

However, there may be gRPC server apps that want to use custom <android:permission>โ€™s to let the end user decide which arbitrary other apps can make use of its gRPC services. In that case, clients should preempt error case 11 simply by checking whether they hold the required permissions before sending a request.

Server apps can avoid error case 10 by never reusing an android.app.Service as a gRPC host if it has ever been android:exported=false in some previous app version. Instead they should simply create a new android.app.Service for this purpose.

Only error cases 12 - 14 remain, making PERMISSION_DENIED unambiguous for the purpose of error handling. Reasonable client apps can handle it in a generic way by displaying an error message and/or proceeding with degraded functionality.

Non-Ambiguity of UNIMPLEMENTED

The UNIMPLEMENTED status code corresponds to quite a few different problems with the server app: Itโ€™s either not installed, too old, misconfigured, or disabled in whole or in part. Despite the diversity of underlying error cases, we believe most client apps will and should handle UNIMPLEMENTED in the same way: by sending the user to the app store to (re)install the server app. Reinstalling might be overkill for the disabled cases but most end users don't know what it means to enable/disable an app and thereโ€™s neither enough space in a UI dialog nor enough reader attention to explain it. Reinstalling is something users likely already understand and likely to cure problems 0-9 (once a fixed version of the server is available).

Detailed Discussion of Binder Failure Modes

IBinder.transact() returns false

According to the docs, false โ€œgenerally means the transaction code was not understood.โ€ This is true for synchronous transactions but all gRPC/BinderChannel transactions are FLAG_ONEWAY meaning the calling thread doesnโ€™t wait around for the server to return from onTransact(). Examination of the AOSP source code shows several additional undocumented reasons transact() could return false but all of these cases should be impossible and arenโ€™t things that reasonable apps want to handle.

IBinder.transact() Throws an Exception

According to the docs, transact() can throw RemoteException but the significance of this exception isnโ€™t documented. By inspection of the AOSP source, we see there are several cases:

  1. The remote process is no longer alive (android.os.DeadObjectException)
  2. The IPC buffer is full (android.os.TransactionTooLargeException)
  3. Certain internal errors from the underlying ioctl() system call.

Status code mappings:

Exception Status Code Rationale
android.os.DeadObjectException UNAVAILABLE So the caller can retry against a new incarnation of the server process
android.os.TransactionTooLargeException UNAVAILABLE These are usually transient. A retry is likely to succeed later when demand for the IPC buffer subsides.
Some other RemoteException INTERNAL So the caller doesnโ€™t bother retrying
Some other RuntimeException INTERNAL

bindService() returns false

According to the docs, this bindService() returns false when โ€œthe system couldn't find the service or if your client doesn't have permission to bind to it.โ€ However, the part about permission is somewhat misleading.

According to a review of the AOSP source code, there are in fact several cases:

  1. The target package is not installed
  2. The target package is installed but does not declare the target Service in its manifest.
  3. The target package requests dangerous permissions but targets sdk <= M and therefore requires a permissions review, but the caller is not running in the foreground and so it would be inappropriate to launch the review UI.
  4. The target package is not visible to the client due to Android 11 package visibility rules.
  5. One of the new Safer Intents rules is violated. Most commonly, the bind Intent specifies a ComponentName explicitly but doesn't match any of its <intent-filter>s.

Status code mapping: UNIMPLEMENTED

(1) and (2) are interesting new possibilities unique to on-device RPC. (1) is straightforward and the most likely cause of (2) is that the user has an old version of the server app installed that predates its gRPC integration. Many clients will want to handle these cases, likely by directing the user to the app store in order to install/upgrade the server.

Unfortunately UNIMPLEMENTED doesnโ€™t capture (3) but none of the other canonical status codes do either and we expect this case to be extremely rare.

(4) and (5) are intentially indistinguishable from (1) by Android design so we can't handle them differently. However, as an error in its own manifest, (4) isn't something a reasonable client would handle at runtime anyway. (5) is an error in the server manifest and so, just like the other cases, the best practice for handling it is to send the user to the app store in the hope that the server can be updated with a fix.

bindService() throws SecurityException

According to the docs, SecurityException is thrown โ€œif the calling app does not have permission to bind to the given serviceโ€. There are quite a few specific cases:

  1. The target Service sets android:exported = โ€œfalseโ€ in its manifest but the caller is in a different app.
  2. The target Service declares a required permission in its manifest but the calling package doesnโ€™t have it.
  3. The target Service is marked as android:singleton in the manifest but doesnโ€™t hold the INTERACT_ACROSS_USERS permission.
  4. The caller is an android:isolatedProcess.
  5. The caller requested certain security-related flags on the binding without the necessary permission (gRPC/BinderChannel doesnโ€™t do this)
  6. โ€ฆ according to the source code, a long tail of unlikely security-related internal errors.

Status code mapping: PERMISSION_DENIED.

bindService() throws IllegalArgumentException

There are a couple cases:

  1. The binding Intent is not explicit.
  2. The binding Intent contains file descriptors.

Status Code mapping: INTERNAL. These cases should be impossible.

onBindingDied() ServiceConnection callback

According to the docs: โ€œ... This means the interface will never receive another connection. The application will need to unbind and rebind the connection to activate it again. This may happen, for example, if the application hosting the service it is bound to has been updated.โ€

Status code mapping: UNAVAILABLE

UNAVAILABLE is the best mapping since a retry is likely to succeed in the near future once the server application finishes updating.

onNullBinding() ServiceConnection callback

According to the docs: โ€œCalled when the service being bound has returned null from its onBind() method. This indicates that the attempting service binding represented by this ServiceConnection will never become usable.โ€

Status code mapping: UNIMPLEMENTED

UNIMPLEMENTED is used here because a retry is likely to fail for the same reason. The most likely root cause for a null binding is an older version of the server app where the android.app.Service exists but either doesnโ€™t implement onBind() or doesnโ€™t recognize the grpc.io.action.BIND Intent action.

onServiceDisconnected() ServiceConnection callback

According to the docs: โ€œCalled when a connection to the Service has been lost. This typically happens when the process hosting the service has crashed or been killed ...โ€

Status code mapping: UNAVAILABLE

UNAVAILABLE is used here since a retry is likely to succeed against a newly restarted instance of the server.

Response Parcel Contains an Exception

Androidโ€™s Parcel class exposes a mechanism for marshalling certain types of RuntimeExceptions between traditional Binder IPC peers. However we wonโ€™t consider this case because gRPC/BinderChannel doesnโ€™t use this mechanism. In fact, all BinderChannel transactions are FLAG_ONE_WAY so there is no response Parcel.

Source Context for bindService() is Destroyed

The calling Activity or Service Context might be destroyed with a gRPC request in flight. Apps should cease operations when the Context hosting it goes away and this includes cancelling any outstanding RPCs.

Status code mapping: CANCELLED


File: documentation/android-channel-builder.md

AndroidChannelBuilder

The grpc-android package provides access to the
AndroidChannelBuilder class. Given an Android Context, this builder
registers a network event listener upon channel construction. The listener
automatically responds to changes in the device's network state, avoiding
delays and interrupted RPCs that may otherwise occur.

By default, gRPC uses exponential backoff to recover from connection failures.
Depending on the scheduled backoff delay when the device regains connectivity,
this could result in a one minute or longer delay before gRPC re-establishes
the connection. This delay is removed when AndroidChannelBuilder is provided
with the app's Android Context. Notifications from the network listener
cause the channel to immediately reconnect upon network recovery.

On Android API levels 24+, AndroidChannelBuilder's network listener mechanism
allows graceful switching from cellular to wifi connections. When an Android
device on a cellular network connects to a wifi network, there is a brief
(typically 30 second) interval when both cellular and wifi networks remain
available, then any connections on the cellular network are terminated. By
listening for changes in the device's default network, AndroidChannelBuilder
sends new RPCs via wifi rather than using an already-established cellular
connection. Without listening for pending network changes, new RPCs sent on an
already established cellular connection would fail when the device terminates
cellular connections.

Note: Currently, AndroidChannelBuilder is only compatible with gRPC
OkHttp. We plan to offer additional Android-specific features compatible with
both the OkHttp and Cronet transports in the future, but the network listener
mechanism is only necessary with OkHttp; the Cronet library internally handles
connection management on Android devices.

Example usage:

In your build.gradle file, include a dependency on both io.grpc:grpc-android and
io.grpc:grpc-okhttp.

You also need permission to access the device's network state in your
AndroidManifest.xml:

text

When constructing your channel, use AndroidChannelBuilder and provide it with
your app's Context:

text
import io.grpc.android.AndroidChannelBuilder;
...
ManagedChannel channel = AndroidChannelBuilder.forAddress("localhost", 8080)
    .context(getApplicationContext())
    .build();

You continue to use the constructed channel exactly as you would any other
channel. gRPC will now monitor and respond to the device's network state
automatically. When you shutdown the managed channel, the network listener
registered by AndroidChannelBuilder will be unregistered.


File: documentation/server-reflection-tutorial.md

gRPC Server Reflection Tutorial

gRPC Server Reflection provides information about publicly-accessible gRPC
services on a server, and assists clients at runtime with constructing RPC
requests and responses without precompiled service information. It is used by
the gRPC command line tool (gRPC CLI), which can be used to introspect server
protos and send/receive test RPCs. Reflection is only supported for
proto-based services.

Enable Server Reflection

gRPC-Java Server Reflection is implemented by
io.grpc.protobuf.services.ProtoReflectionServiceV1 in the grpc-services
package. To enable server reflection, you need to add the
ProtoReflectionServiceV1 to your gRPC server.

For example, to enable server reflection in
examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java, we
need to make the following changes:

diff
--- a/examples/build.gradle
+++ b/examples/build.gradle
@@ -27,6 +27,7 @@
 dependencies {
   compile "io.grpc:grpc-netty-shaded:${grpcVersion}"
   compile "io.grpc:grpc-protobuf:${grpcVersion}"
+  compile "io.grpc:grpc-services:${grpcVersion}"
   compile "io.grpc:grpc-stub:${grpcVersion}"
 
   testCompile "junit:junit:4.13.2"
--- a/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java
+++ b/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java
@@ -33,6 +33,7 @@ package io.grpc.examples.helloworld;
 
 import io.grpc.Server;
 import io.grpc.ServerBuilder;
+import io.grpc.protobuf.services.ProtoReflectionServiceV1;
 import io.grpc.stub.StreamObserver;
 import java.io.IOException;
 import java.util.logging.Logger;
@@ -50,6 +51,7 @@ public class HelloWorldServer {
     int port = 50051;
     server = ServerBuilder.forPort(port)
         .addService(new GreeterImpl())
+        .addService(ProtoReflectionServiceV1.newInstance())
         .build()
         .start();
     logger.info("Server started, listening on " + port);

In the following examples, we assume you have made these changes to
enable reflection in HelloWorldServer.java.

gRPC CLI

After enabling server reflection in a server application, you can use gRPC
CLI to get information about its available services. gRPC CLI is written
in C++. Instructions on how to build gRPC CLI can be found at
command_line_tool.md.

Use gRPC CLI to check services

First, build and start the hello-world-server:

sh
$ cd examples/
$ ./gradlew installDist
$ build/install/examples/bin/hello-world-server

Open a new terminal and make sure you are in the directory where the grpc_cli
binary is located:

sh
$ cd <grpc-cpp-directory>/bins/opt

List services

grpc_cli ls command lists services and methods exposed at a given port:

  • List all the services exposed at a given port

    sh
    $ ./grpc_cli ls localhost:50051

    output:

    sh
    helloworld.Greeter
    grpc.reflection.v1alpha.ServerReflection
  • List one service with details

    grpc_cli ls command inspects a service given its full name (in the format of
    .<service>). It can print information with a long listing format
    when -l flag is set. This flag can be used to get more details about a
    service.

    sh
    $ ./grpc_cli ls localhost:50051 helloworld.Greeter -l

    output:

    sh
    filename: helloworld.proto
    package: helloworld;
    service Greeter {
      rpc SayHello(helloworld.HelloRequest) returns (helloworld.HelloReply) {}
    }

List methods

  • List one method with details

    grpc_cli ls command also inspects a method given its full name (in the
    format of .<service>.<method>).

    sh
    $ ./grpc_cli ls localhost:50051 helloworld.Greeter.SayHello -l

    output:

    sh
    rpc SayHello(helloworld.HelloRequest) returns (helloworld.HelloReply) {}

Inspect message types

We can usegrpc_cli type command to inspect request/response types given the
full name of the type (in the format of .<type>).

  • Get information about the request type

    sh
    $ ./grpc_cli type localhost:50051 helloworld.HelloRequest

    output:

    sh
    message HelloRequest {
      optional string name = 1[json_name = "name"];
    }

Call a remote method

We can send RPCs to a server and get responses using grpc_cli call command.

  • Call a unary method

    sh
    $ ./grpc_cli call localhost:50051 SayHello "name: 'gRPC CLI'"

    output:

    sh
    message: "Hello gRPC CLI"

File: examples/android/strictmode/README.md

gRPC Android StrictMode Example

  • This example intends to show the compatibility of gRPC with Android StrictMode.
  • Android SDK version 28 is required for StrictMode.VmPolicy.Builder.penaltyListener used in the example.
  • This example does the same thing as HelloWorld example except popping up a dialog for detected StrictMode policy violation (shown below).

File: examples/android/README.md

gRPC Hello World Example (Android Java)

PREREQUISITES

INSTALL

  1. Install gRPC Java library SNAPSHOT locally, including code generation plugin (Only need this step for non-released versions, e.g. master HEAD).

  2. Install the app

sh
$ cd helloworld  # or "cd routeguide"
$ ../../gradlew installDebug

Please refer to the
tutorial on
how to use gRPC in Android programs.