# Android gRPC/BinderChannel Status Codes ## Background [BinderChannel](https://github.com/grpc/proposal/blob/master/L73-java-binderchannel.md) 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](https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc). 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](https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc)” 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 |
1 | Server app not installed | bindService() returns false | UNIMPLEMENTED “The operation is not implemented or is not supported / enabled in this service.” |
Direct the user to install/reinstall the server app. |
2 | Old version of the server app doesn’t declare the target android.app.Service in its manifest. | |||
3 | Target android.app.Service is disabled | |||
4 | The whole server app is disabled | |||
5 | Server app predates the Android M permissions model and the user must review and approve some newly requested permissions before it can run. | |||
6 | Target android.app.Service doesn’t recognize grpc binding Intent (old version of server app?) | onNullBinding() ServiceConnection callback | ||
7 | Method not found on the io.grpc.Server (old version of server app?) | N/A | ||
8 | Request cardinality violation (old version of server app expects unary rather than streaming, say) | |||
9 | 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 …” |
|
10 | Target android.app.Service requires an <android:permission> that client doesn’t hold. | Prompt the user to grant the needed Android permission | ||
11 | 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. | ||
12 | Calling Android UID not allowed by ServerSecurityPolicy | N/A | ||
13 | Server Android UID not allowed by client’s SecurityPolicy | |||
14 | 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 | ||||
15 | Server app is currently being upgraded to a new version | onBindingDied() ServiceConnection callback | ||
16 | The whole server app or the target android.app.Service was disabled | |||
17 | Binder transaction buffer overflow | IBinder.transact() throws TransactionTooLargeException | ||
18 | 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. 18: The caller can try again later when the user opens the source Activity or restarts the source Service) |
19 | Client application cancelled the request | N/A | ||
19 | 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 | ||||
20 | Flow-control protocol violation | N/A | ||
21 | Can’t parse request/response proto |
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 |