diff --git a/documentation/android-binderchannel-status-codes.md b/documentation/android-binderchannel-status-codes.md new file mode 100644 index 0000000000..dda0220bf8 --- /dev/null +++ b/documentation/android-binderchannel-status-codes.md @@ -0,0 +1,385 @@ +# 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 + | +