1*e07d83d3SAndroid Build Coastguard Worker# Android gRPC/BinderChannel Status Codes 2*e07d83d3SAndroid Build Coastguard Worker 3*e07d83d3SAndroid Build Coastguard Worker## Background 4*e07d83d3SAndroid Build Coastguard Worker 5*e07d83d3SAndroid Build Coastguard Worker[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. 6*e07d83d3SAndroid Build Coastguard Worker 7*e07d83d3SAndroid Build Coastguard Worker 8*e07d83d3SAndroid Build Coastguard Worker## Status Code Mapping 9*e07d83d3SAndroid Build Coastguard Worker 10*e07d83d3SAndroid Build Coastguard WorkerConsider 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. 11*e07d83d3SAndroid Build Coastguard Worker 12*e07d83d3SAndroid Build Coastguard Worker<table> 13*e07d83d3SAndroid Build Coastguard Worker <tr> 14*e07d83d3SAndroid Build Coastguard Worker <td><strong>#</strong> 15*e07d83d3SAndroid Build Coastguard Worker </td> 16*e07d83d3SAndroid Build Coastguard Worker <td><strong>Error Case</strong> 17*e07d83d3SAndroid Build Coastguard Worker </td> 18*e07d83d3SAndroid Build Coastguard Worker <td><strong>Android API Manifestation</strong> 19*e07d83d3SAndroid Build Coastguard Worker </td> 20*e07d83d3SAndroid Build Coastguard Worker <td><strong>Status Code</strong> 21*e07d83d3SAndroid Build Coastguard Worker </td> 22*e07d83d3SAndroid Build Coastguard Worker <td><strong>Expected Client Handling</strong> 23*e07d83d3SAndroid Build Coastguard Worker </td> 24*e07d83d3SAndroid Build Coastguard Worker </tr> 25*e07d83d3SAndroid Build Coastguard Worker <tr> 26*e07d83d3SAndroid Build Coastguard Worker <td>1 27*e07d83d3SAndroid Build Coastguard Worker </td> 28*e07d83d3SAndroid Build Coastguard Worker <td>Server app not installed 29*e07d83d3SAndroid Build Coastguard Worker </td> 30*e07d83d3SAndroid Build Coastguard Worker <td rowspan="5" >bindService() returns false 31*e07d83d3SAndroid Build Coastguard Worker </td> 32*e07d83d3SAndroid Build Coastguard Worker <td rowspan="8" ><p>UNIMPLEMENTED<p>“The operation is not implemented or is not supported / enabled in this service.” 33*e07d83d3SAndroid Build Coastguard Worker </td> 34*e07d83d3SAndroid Build Coastguard Worker <td rowspan="9" >Direct the user to install/reinstall the server app. 35*e07d83d3SAndroid Build Coastguard Worker </td> 36*e07d83d3SAndroid Build Coastguard Worker </tr> 37*e07d83d3SAndroid Build Coastguard Worker <tr> 38*e07d83d3SAndroid Build Coastguard Worker <td>2 39*e07d83d3SAndroid Build Coastguard Worker </td> 40*e07d83d3SAndroid Build Coastguard Worker <td>Old version of the server app doesn’t declare the target android.app.Service in its manifest. 41*e07d83d3SAndroid Build Coastguard Worker </td> 42*e07d83d3SAndroid Build Coastguard Worker </tr> 43*e07d83d3SAndroid Build Coastguard Worker <tr> 44*e07d83d3SAndroid Build Coastguard Worker <td>3 45*e07d83d3SAndroid Build Coastguard Worker </td> 46*e07d83d3SAndroid Build Coastguard Worker <td>Target android.app.Service is disabled 47*e07d83d3SAndroid Build Coastguard Worker </td> 48*e07d83d3SAndroid Build Coastguard Worker </tr> 49*e07d83d3SAndroid Build Coastguard Worker <tr> 50*e07d83d3SAndroid Build Coastguard Worker <td>4 51*e07d83d3SAndroid Build Coastguard Worker </td> 52*e07d83d3SAndroid Build Coastguard Worker <td>The whole server app is disabled 53*e07d83d3SAndroid Build Coastguard Worker </td> 54*e07d83d3SAndroid Build Coastguard Worker </tr> 55*e07d83d3SAndroid Build Coastguard Worker <tr> 56*e07d83d3SAndroid Build Coastguard Worker <td>5 57*e07d83d3SAndroid Build Coastguard Worker </td> 58*e07d83d3SAndroid Build Coastguard Worker <td>Server app predates <a href="https://developer.android.com/guide/topics/permissions/overview">the Android M permissions model</a> and the user must review and approve some newly requested permissions before it can run. 59*e07d83d3SAndroid Build Coastguard Worker </td> 60*e07d83d3SAndroid Build Coastguard Worker </tr> 61*e07d83d3SAndroid Build Coastguard Worker <tr> 62*e07d83d3SAndroid Build Coastguard Worker <td>6 63*e07d83d3SAndroid Build Coastguard Worker </td> 64*e07d83d3SAndroid Build Coastguard Worker <td>Target android.app.Service doesn’t recognize grpc binding Intent (old version of server app?) 65*e07d83d3SAndroid Build Coastguard Worker </td> 66*e07d83d3SAndroid Build Coastguard Worker <td>onNullBinding() ServiceConnection callback 67*e07d83d3SAndroid Build Coastguard Worker </td> 68*e07d83d3SAndroid Build Coastguard Worker </tr> 69*e07d83d3SAndroid Build Coastguard Worker <tr> 70*e07d83d3SAndroid Build Coastguard Worker <td>7 71*e07d83d3SAndroid Build Coastguard Worker </td> 72*e07d83d3SAndroid Build Coastguard Worker <td>Method not found on the io.grpc.Server (old version of server app?) 73*e07d83d3SAndroid Build Coastguard Worker </td> 74*e07d83d3SAndroid Build Coastguard Worker <td rowspan="2" >N/A 75*e07d83d3SAndroid Build Coastguard Worker </td> 76*e07d83d3SAndroid Build Coastguard Worker </tr> 77*e07d83d3SAndroid Build Coastguard Worker <tr> 78*e07d83d3SAndroid Build Coastguard Worker <td>8 79*e07d83d3SAndroid Build Coastguard Worker </td> 80*e07d83d3SAndroid Build Coastguard Worker <td>Request cardinality violation (old version of server app expects unary rather than streaming, say) 81*e07d83d3SAndroid Build Coastguard Worker </td> 82*e07d83d3SAndroid Build Coastguard Worker </tr> 83*e07d83d3SAndroid Build Coastguard Worker <tr> 84*e07d83d3SAndroid Build Coastguard Worker <td>9 85*e07d83d3SAndroid Build Coastguard Worker </td> 86*e07d83d3SAndroid Build Coastguard Worker <td>Old version of the server app exposes target android.app.Service but doesn’t android:export it. 87*e07d83d3SAndroid Build Coastguard Worker </td> 88*e07d83d3SAndroid Build Coastguard Worker <td rowspan="3" >bindService() throws SecurityException 89*e07d83d3SAndroid Build Coastguard Worker </td> 90*e07d83d3SAndroid Build Coastguard Worker <td rowspan="5" ><p>PERMISSION_DENIED<p> 91*e07d83d3SAndroid Build Coastguard Worker“The caller does not have permission to execute the specified operation …” 92*e07d83d3SAndroid Build Coastguard Worker </td> 93*e07d83d3SAndroid Build Coastguard Worker </tr> 94*e07d83d3SAndroid Build Coastguard Worker <tr> 95*e07d83d3SAndroid Build Coastguard Worker <td>10 96*e07d83d3SAndroid Build Coastguard Worker </td> 97*e07d83d3SAndroid Build Coastguard Worker <td>Target android.app.Service requires an <android:permission> that client doesn’t hold. 98*e07d83d3SAndroid Build Coastguard Worker </td> 99*e07d83d3SAndroid Build Coastguard Worker <td>Prompt the user to grant the needed Android permission 100*e07d83d3SAndroid Build Coastguard Worker </td> 101*e07d83d3SAndroid Build Coastguard Worker </tr> 102*e07d83d3SAndroid Build Coastguard Worker <tr> 103*e07d83d3SAndroid Build Coastguard Worker <td>11 104*e07d83d3SAndroid Build Coastguard Worker </td> 105*e07d83d3SAndroid Build Coastguard Worker <td>Violations of the security policy for miscellaneous Android features like android:isolatedProcess, android:externalService, android:singleUser, instant apps, BIND_TREAT_LIKE_ACTIVITY, etc, 106*e07d83d3SAndroid Build Coastguard Worker </td> 107*e07d83d3SAndroid Build Coastguard Worker <td rowspan="3" >Give up - This is a programming or packaging error that only the app developer can fix. 108*e07d83d3SAndroid Build Coastguard Worker </td> 109*e07d83d3SAndroid Build Coastguard Worker </tr> 110*e07d83d3SAndroid Build Coastguard Worker <tr> 111*e07d83d3SAndroid Build Coastguard Worker <td>12 112*e07d83d3SAndroid Build Coastguard Worker </td> 113*e07d83d3SAndroid Build Coastguard Worker <td>Calling Android UID not allowed by ServerSecurityPolicy 114*e07d83d3SAndroid Build Coastguard Worker </td> 115*e07d83d3SAndroid Build Coastguard Worker <td rowspan="2" >N/A 116*e07d83d3SAndroid Build Coastguard Worker </td> 117*e07d83d3SAndroid Build Coastguard Worker </tr> 118*e07d83d3SAndroid Build Coastguard Worker <tr> 119*e07d83d3SAndroid Build Coastguard Worker <td>13 120*e07d83d3SAndroid Build Coastguard Worker </td> 121*e07d83d3SAndroid Build Coastguard Worker <td>Server Android UID not allowed by client’s SecurityPolicy 122*e07d83d3SAndroid Build Coastguard Worker </td> 123*e07d83d3SAndroid Build Coastguard Worker </tr> 124*e07d83d3SAndroid Build Coastguard Worker <tr> 125*e07d83d3SAndroid Build Coastguard Worker <td rowspan="3" >14 126*e07d83d3SAndroid Build Coastguard Worker </td> 127*e07d83d3SAndroid Build Coastguard Worker <td rowspan="3" >Server process crashed or killed with request in flight. 128*e07d83d3SAndroid Build Coastguard Worker </td> 129*e07d83d3SAndroid Build Coastguard Worker <td>onDisconnected() ServiceConnection callback 130*e07d83d3SAndroid Build Coastguard Worker </td> 131*e07d83d3SAndroid Build Coastguard Worker <td rowspan="6" ><p>UNAVAILABLE 132*e07d83d3SAndroid Build Coastguard Worker<p> 133*e07d83d3SAndroid Build Coastguard Worker“The service is currently unavailable. This is most likely a transient condition, which can be corrected by retrying with a backoff ...” 134*e07d83d3SAndroid Build Coastguard Worker </td> 135*e07d83d3SAndroid Build Coastguard Worker <td rowspan="6" >Retry with exponential backoff and deadline (see <a href="https://grpc.github.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html#enableRetry--">ManagedChannelBuilder#enableRetry()</a> 136*e07d83d3SAndroid Build Coastguard Worker </td> 137*e07d83d3SAndroid Build Coastguard Worker </tr> 138*e07d83d3SAndroid Build Coastguard Worker <tr> 139*e07d83d3SAndroid Build Coastguard Worker <td>onBinderDied() IBinder.DeathRecipient callback 140*e07d83d3SAndroid Build Coastguard Worker </td> 141*e07d83d3SAndroid Build Coastguard Worker </tr> 142*e07d83d3SAndroid Build Coastguard Worker <tr> 143*e07d83d3SAndroid Build Coastguard Worker <td>IBinder.transact() throws DeadObjectException 144*e07d83d3SAndroid Build Coastguard Worker </td> 145*e07d83d3SAndroid Build Coastguard Worker </tr> 146*e07d83d3SAndroid Build Coastguard Worker <tr> 147*e07d83d3SAndroid Build Coastguard Worker <td>15 148*e07d83d3SAndroid Build Coastguard Worker </td> 149*e07d83d3SAndroid Build Coastguard Worker <td>Server app is currently being upgraded to a new version 150*e07d83d3SAndroid Build Coastguard Worker </td> 151*e07d83d3SAndroid Build Coastguard Worker <td rowspan="2" >onBindingDied() ServiceConnection callback 152*e07d83d3SAndroid Build Coastguard Worker </td> 153*e07d83d3SAndroid Build Coastguard Worker </tr> 154*e07d83d3SAndroid Build Coastguard Worker <tr> 155*e07d83d3SAndroid Build Coastguard Worker <td>16 156*e07d83d3SAndroid Build Coastguard Worker </td> 157*e07d83d3SAndroid Build Coastguard Worker <td>The whole server app or the target android.app.Service was disabled 158*e07d83d3SAndroid Build Coastguard Worker </td> 159*e07d83d3SAndroid Build Coastguard Worker </tr> 160*e07d83d3SAndroid Build Coastguard Worker <tr> 161*e07d83d3SAndroid Build Coastguard Worker <td>17 162*e07d83d3SAndroid Build Coastguard Worker </td> 163*e07d83d3SAndroid Build Coastguard Worker <td>Binder transaction buffer overflow 164*e07d83d3SAndroid Build Coastguard Worker </td> 165*e07d83d3SAndroid Build Coastguard Worker <td>IBinder.transact() throws TransactionTooLargeException 166*e07d83d3SAndroid Build Coastguard Worker </td> 167*e07d83d3SAndroid Build Coastguard Worker </tr> 168*e07d83d3SAndroid Build Coastguard Worker <tr> 169*e07d83d3SAndroid Build Coastguard Worker <td>18 170*e07d83d3SAndroid Build Coastguard Worker </td> 171*e07d83d3SAndroid Build Coastguard Worker <td>Source Context for bindService() is destroyed with a request in flight 172*e07d83d3SAndroid Build Coastguard Worker </td> 173*e07d83d3SAndroid Build Coastguard Worker <td>onDestroy() 174*e07d83d3SAndroid Build Coastguard Worker </td> 175*e07d83d3SAndroid Build Coastguard Worker <td rowspan="2" ><p>CANCELLED 176*e07d83d3SAndroid Build Coastguard Worker<p> 177*e07d83d3SAndroid Build Coastguard Worker“The operation was cancelled, typically by the caller.” 178*e07d83d3SAndroid Build Coastguard Worker </td> 179*e07d83d3SAndroid Build Coastguard Worker <td rowspan="2" >Give up for now. 180*e07d83d3SAndroid Build Coastguard Worker<p> 181*e07d83d3SAndroid Build Coastguard Worker(Re. 18: The caller can try again later when the user opens the source Activity or restarts the source Service) 182*e07d83d3SAndroid Build Coastguard Worker </td> 183*e07d83d3SAndroid Build Coastguard Worker </tr> 184*e07d83d3SAndroid Build Coastguard Worker <tr> 185*e07d83d3SAndroid Build Coastguard Worker <td>19 186*e07d83d3SAndroid Build Coastguard Worker </td> 187*e07d83d3SAndroid Build Coastguard Worker <td>Client application cancelled the request 188*e07d83d3SAndroid Build Coastguard Worker </td> 189*e07d83d3SAndroid Build Coastguard Worker <td>N/A 190*e07d83d3SAndroid Build Coastguard Worker </td> 191*e07d83d3SAndroid Build Coastguard Worker </tr> 192*e07d83d3SAndroid Build Coastguard Worker <tr> 193*e07d83d3SAndroid Build Coastguard Worker <td rowspan="2" >19 194*e07d83d3SAndroid Build Coastguard Worker </td> 195*e07d83d3SAndroid Build Coastguard Worker <td rowspan="2" >Bug in Android itself or the way the io.grpc.binder transport uses it. 196*e07d83d3SAndroid Build Coastguard Worker </td> 197*e07d83d3SAndroid Build Coastguard Worker <td>IBinder.transact() returns false 198*e07d83d3SAndroid Build Coastguard Worker </td> 199*e07d83d3SAndroid Build Coastguard Worker <td rowspan="4" ><p>INTERNAL 200*e07d83d3SAndroid Build Coastguard Worker<p> 201*e07d83d3SAndroid Build Coastguard Worker“This means that some invariants expected by the underlying system have been broken. … Reserved for serious errors.” 202*e07d83d3SAndroid Build Coastguard Worker </td> 203*e07d83d3SAndroid Build Coastguard Worker <td rowspan="4" >Give up - This is a programming error that only the app or grpc developers can fix 204*e07d83d3SAndroid Build Coastguard Worker </td> 205*e07d83d3SAndroid Build Coastguard Worker </tr> 206*e07d83d3SAndroid Build Coastguard Worker <tr> 207*e07d83d3SAndroid Build Coastguard Worker <td>bindService() throws IllegalArgumentException 208*e07d83d3SAndroid Build Coastguard Worker </td> 209*e07d83d3SAndroid Build Coastguard Worker </tr> 210*e07d83d3SAndroid Build Coastguard Worker <tr> 211*e07d83d3SAndroid Build Coastguard Worker <td>20 212*e07d83d3SAndroid Build Coastguard Worker </td> 213*e07d83d3SAndroid Build Coastguard Worker <td>Flow-control protocol violation 214*e07d83d3SAndroid Build Coastguard Worker </td> 215*e07d83d3SAndroid Build Coastguard Worker <td rowspan="2" >N/A 216*e07d83d3SAndroid Build Coastguard Worker </td> 217*e07d83d3SAndroid Build Coastguard Worker </tr> 218*e07d83d3SAndroid Build Coastguard Worker <tr> 219*e07d83d3SAndroid Build Coastguard Worker <td>21 220*e07d83d3SAndroid Build Coastguard Worker </td> 221*e07d83d3SAndroid Build Coastguard Worker <td>Can’t parse request/response proto 222*e07d83d3SAndroid Build Coastguard Worker </td> 223*e07d83d3SAndroid Build Coastguard Worker </tr> 224*e07d83d3SAndroid Build Coastguard Worker</table> 225*e07d83d3SAndroid Build Coastguard Worker 226*e07d83d3SAndroid Build Coastguard Worker 227*e07d83d3SAndroid Build Coastguard Worker### Ambiguity 228*e07d83d3SAndroid Build Coastguard Worker 229*e07d83d3SAndroid Build Coastguard WorkerWe 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 9 and 10 above differently. But they can’t do so based on status code alone because those error cases map to the same one. 230*e07d83d3SAndroid Build Coastguard Worker 231*e07d83d3SAndroid Build Coastguard WorkerIn contrast, for example, even though error case 18 and 19 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. 232*e07d83d3SAndroid Build Coastguard Worker 233*e07d83d3SAndroid Build Coastguard Worker 234*e07d83d3SAndroid Build Coastguard Worker#### Ambiguity of PERMISSION_DENIED and Mitigations 235*e07d83d3SAndroid Build Coastguard Worker 236*e07d83d3SAndroid Build Coastguard WorkerThe mapping above has only one apparently ambiguous status code: `PERMISSION_DENIED`. However, this isn’t so bad because of the following: 237*e07d83d3SAndroid Build Coastguard Worker 238*e07d83d3SAndroid Build Coastguard WorkerThe use of `<android:permission>`s for inter-app IPC access control (error case 10) 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. 239*e07d83d3SAndroid Build Coastguard Worker 240*e07d83d3SAndroid Build Coastguard WorkerHowever, 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 10 simply by [checking whether they hold the required permissions](https://developer.android.com/training/permissions/requesting) before sending a request. 241*e07d83d3SAndroid Build Coastguard Worker 242*e07d83d3SAndroid Build Coastguard WorkerServer apps can avoid error case 9 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. 243*e07d83d3SAndroid Build Coastguard Worker 244*e07d83d3SAndroid Build Coastguard WorkerOnly error cases 11 - 13 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. 245*e07d83d3SAndroid Build Coastguard Worker 246*e07d83d3SAndroid Build Coastguard Worker 247*e07d83d3SAndroid Build Coastguard Worker#### Non-Ambiguity of UNIMPLEMENTED 248*e07d83d3SAndroid Build Coastguard Worker 249*e07d83d3SAndroid Build Coastguard WorkerThe `UNIMPLEMENTED` status code corresponds to quite a few different problems with the server app: It’s either not installed, too old, 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 very likely to cure problems 1-8. 250*e07d83d3SAndroid Build Coastguard Worker 251*e07d83d3SAndroid Build Coastguard Worker 252*e07d83d3SAndroid Build Coastguard Worker## Detailed Discussion of Binder Failure Modes 253*e07d83d3SAndroid Build Coastguard Worker 254*e07d83d3SAndroid Build Coastguard Worker### IBinder.transact() returns false 255*e07d83d3SAndroid Build Coastguard Worker 256*e07d83d3SAndroid Build Coastguard WorkerAccording to the [docs](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)), 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. 257*e07d83d3SAndroid Build Coastguard Worker 258*e07d83d3SAndroid Build Coastguard Worker### IBinder.transact() Throws an Exception 259*e07d83d3SAndroid Build Coastguard Worker 260*e07d83d3SAndroid Build Coastguard WorkerAccording to the docs, `transact()` can throw `RemoteException` but the significance of this exception [isn’t documented](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)). By inspection of the AOSP source, we see there are several cases: 261*e07d83d3SAndroid Build Coastguard Worker 262*e07d83d3SAndroid Build Coastguard Worker1. The remote process is no longer alive (`android.os.DeadObjectException`) 263*e07d83d3SAndroid Build Coastguard Worker2. The IPC buffer is full (`android.os.TransactionTooLargeException`) 264*e07d83d3SAndroid Build Coastguard Worker3. Certain internal errors from the underlying `ioctl()` system call. 265*e07d83d3SAndroid Build Coastguard Worker 266*e07d83d3SAndroid Build Coastguard WorkerStatus code mappings: 267*e07d83d3SAndroid Build Coastguard Worker<table> 268*e07d83d3SAndroid Build Coastguard Worker <tr> 269*e07d83d3SAndroid Build Coastguard Worker <td><strong>Exception</strong> 270*e07d83d3SAndroid Build Coastguard Worker </td> 271*e07d83d3SAndroid Build Coastguard Worker <td><strong>Status Code</strong> 272*e07d83d3SAndroid Build Coastguard Worker </td> 273*e07d83d3SAndroid Build Coastguard Worker <td><strong>Rationale</strong> 274*e07d83d3SAndroid Build Coastguard Worker </td> 275*e07d83d3SAndroid Build Coastguard Worker </tr> 276*e07d83d3SAndroid Build Coastguard Worker <tr> 277*e07d83d3SAndroid Build Coastguard Worker <td>android.os.DeadObjectException 278*e07d83d3SAndroid Build Coastguard Worker </td> 279*e07d83d3SAndroid Build Coastguard Worker <td>UNAVAILABLE 280*e07d83d3SAndroid Build Coastguard Worker </td> 281*e07d83d3SAndroid Build Coastguard Worker <td>So the caller can retry against a new incarnation of the server process 282*e07d83d3SAndroid Build Coastguard Worker </td> 283*e07d83d3SAndroid Build Coastguard Worker </tr> 284*e07d83d3SAndroid Build Coastguard Worker <tr> 285*e07d83d3SAndroid Build Coastguard Worker <td>android.os.TransactionTooLargeException 286*e07d83d3SAndroid Build Coastguard Worker </td> 287*e07d83d3SAndroid Build Coastguard Worker <td>UNAVAILABLE 288*e07d83d3SAndroid Build Coastguard Worker </td> 289*e07d83d3SAndroid Build Coastguard Worker <td>These are usually transient. A retry is likely to succeed later when demand for the IPC buffer subsides. 290*e07d83d3SAndroid Build Coastguard Worker </td> 291*e07d83d3SAndroid Build Coastguard Worker </tr> 292*e07d83d3SAndroid Build Coastguard Worker <tr> 293*e07d83d3SAndroid Build Coastguard Worker <td>Some other RemoteException 294*e07d83d3SAndroid Build Coastguard Worker </td> 295*e07d83d3SAndroid Build Coastguard Worker <td>INTERNAL 296*e07d83d3SAndroid Build Coastguard Worker </td> 297*e07d83d3SAndroid Build Coastguard Worker <td rowspan="2" >So the caller doesn’t bother retrying 298*e07d83d3SAndroid Build Coastguard Worker </td> 299*e07d83d3SAndroid Build Coastguard Worker </tr> 300*e07d83d3SAndroid Build Coastguard Worker <tr> 301*e07d83d3SAndroid Build Coastguard Worker <td>Some other RuntimeException 302*e07d83d3SAndroid Build Coastguard Worker </td> 303*e07d83d3SAndroid Build Coastguard Worker <td>INTERNAL 304*e07d83d3SAndroid Build Coastguard Worker </td> 305*e07d83d3SAndroid Build Coastguard Worker </tr> 306*e07d83d3SAndroid Build Coastguard Worker</table> 307*e07d83d3SAndroid Build Coastguard Worker 308*e07d83d3SAndroid Build Coastguard Worker 309*e07d83d3SAndroid Build Coastguard Worker### bindService() returns false 310*e07d83d3SAndroid Build Coastguard Worker 311*e07d83d3SAndroid Build Coastguard WorkerAccording to the [docs](https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)), 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. 312*e07d83d3SAndroid Build Coastguard Worker 313*e07d83d3SAndroid Build Coastguard WorkerAccording to a review of the AOSP source code, there are in fact several cases: 314*e07d83d3SAndroid Build Coastguard Worker 315*e07d83d3SAndroid Build Coastguard Worker1. The target package is not installed 316*e07d83d3SAndroid Build Coastguard Worker2. The target package is installed but does not declare the target Service in its manifest. 317*e07d83d3SAndroid Build Coastguard Worker3. 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. 318*e07d83d3SAndroid Build Coastguard Worker 319*e07d83d3SAndroid Build Coastguard WorkerStatus code mapping: **UNIMPLEMENTED** 320*e07d83d3SAndroid Build Coastguard Worker 321*e07d83d3SAndroid Build Coastguard Worker(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. 322*e07d83d3SAndroid Build Coastguard Worker 323*e07d83d3SAndroid Build Coastguard WorkerUnfortunately `UNIMPLEMENTED` doesn’t capture (3) but none of the other canonical status codes do either and we expect this case to be extremely rare. 324*e07d83d3SAndroid Build Coastguard Worker 325*e07d83d3SAndroid Build Coastguard Worker 326*e07d83d3SAndroid Build Coastguard Worker### bindService() throws SecurityException 327*e07d83d3SAndroid Build Coastguard Worker 328*e07d83d3SAndroid Build Coastguard WorkerAccording to the [docs](https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)), SecurityException is thrown “if the calling app does not have permission to bind to the given service”. There are quite a few specific cases: 329*e07d83d3SAndroid Build Coastguard Worker 330*e07d83d3SAndroid Build Coastguard Worker1. The target Service sets `android:exported = “false”` in its manifest but the caller is in a different app. 331*e07d83d3SAndroid Build Coastguard Worker2. The target Service declares a required permission in its manifest but the calling package doesn’t have it. 332*e07d83d3SAndroid Build Coastguard Worker3. The target Service is marked as `android:singleton` in the manifest but doesn’t hold the `INTERACT_ACROSS_USERS` permission. 333*e07d83d3SAndroid Build Coastguard Worker4. The caller is an `android:isolatedProcess`. 334*e07d83d3SAndroid Build Coastguard Worker5. The caller requested certain security-related flags on the binding without the necessary permission (gRPC/BinderChannel doesn’t do this) 335*e07d83d3SAndroid Build Coastguard Worker6. … according to the source code, a long tail of unlikely security-related internal errors. 336*e07d83d3SAndroid Build Coastguard Worker 337*e07d83d3SAndroid Build Coastguard WorkerStatus code mapping: **PERMISSION_DENIED**. 338*e07d83d3SAndroid Build Coastguard Worker 339*e07d83d3SAndroid Build Coastguard Worker### bindService() throws IllegalArgumentException 340*e07d83d3SAndroid Build Coastguard Worker 341*e07d83d3SAndroid Build Coastguard WorkerThere are a couple cases: 342*e07d83d3SAndroid Build Coastguard Worker 343*e07d83d3SAndroid Build Coastguard Worker1. The binding Intent is not explicit. 344*e07d83d3SAndroid Build Coastguard Worker2. The binding Intent contains file descriptors. 345*e07d83d3SAndroid Build Coastguard Worker 346*e07d83d3SAndroid Build Coastguard WorkerStatus Code mapping: **INTERNAL**. These cases should be impossible. 347*e07d83d3SAndroid Build Coastguard Worker 348*e07d83d3SAndroid Build Coastguard Worker 349*e07d83d3SAndroid Build Coastguard Worker### onBindingDied() ServiceConnection callback 350*e07d83d3SAndroid Build Coastguard Worker 351*e07d83d3SAndroid Build Coastguard WorkerAccording to the [docs](https://developer.android.com/reference/android/content/ServiceConnection#onBindingDied(android.content.ComponentName)): “... 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.” 352*e07d83d3SAndroid Build Coastguard Worker 353*e07d83d3SAndroid Build Coastguard WorkerStatus code mapping: **UNAVAILABLE** 354*e07d83d3SAndroid Build Coastguard Worker 355*e07d83d3SAndroid Build Coastguard Worker`UNAVAILABLE` is the best mapping since a retry is likely to succeed in the near future once the server application finishes updating. 356*e07d83d3SAndroid Build Coastguard Worker 357*e07d83d3SAndroid Build Coastguard Worker 358*e07d83d3SAndroid Build Coastguard Worker### onNullBinding() ServiceConnection callback 359*e07d83d3SAndroid Build Coastguard Worker 360*e07d83d3SAndroid Build Coastguard WorkerAccording to the [docs](https://developer.android.com/reference/android/content/ServiceConnection#onNullBinding(android.content.ComponentName)): “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.” 361*e07d83d3SAndroid Build Coastguard Worker 362*e07d83d3SAndroid Build Coastguard WorkerStatus code mapping: **UNIMPLEMENTED** 363*e07d83d3SAndroid Build Coastguard Worker 364*e07d83d3SAndroid Build Coastguard Worker`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. 365*e07d83d3SAndroid Build Coastguard Worker 366*e07d83d3SAndroid Build Coastguard Worker 367*e07d83d3SAndroid Build Coastguard Worker### onServiceDisconnected() ServiceConnection callback 368*e07d83d3SAndroid Build Coastguard Worker 369*e07d83d3SAndroid Build Coastguard WorkerAccording to the [docs](https://developer.android.com/reference/android/content/ServiceConnection#onServiceDisconnected(android.content.ComponentName)): “Called when a connection to the Service has been lost. This typically happens when the process hosting the service has crashed or been killed ...” 370*e07d83d3SAndroid Build Coastguard Worker 371*e07d83d3SAndroid Build Coastguard WorkerStatus code mapping: **UNAVAILABLE** 372*e07d83d3SAndroid Build Coastguard Worker 373*e07d83d3SAndroid Build Coastguard Worker`UNAVAILABLE` is used here since a retry is likely to succeed against a newly restarted instance of the server. 374*e07d83d3SAndroid Build Coastguard Worker 375*e07d83d3SAndroid Build Coastguard Worker 376*e07d83d3SAndroid Build Coastguard Worker### Response Parcel Contains an Exception 377*e07d83d3SAndroid Build Coastguard Worker 378*e07d83d3SAndroid Build Coastguard WorkerAndroid’s Parcel class exposes a mechanism for marshalling certain types of `RuntimeException`s 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. 379*e07d83d3SAndroid Build Coastguard Worker 380*e07d83d3SAndroid Build Coastguard Worker 381*e07d83d3SAndroid Build Coastguard Worker### Source Context for bindService() is Destroyed 382*e07d83d3SAndroid Build Coastguard Worker 383*e07d83d3SAndroid Build Coastguard WorkerThe 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. 384*e07d83d3SAndroid Build Coastguard Worker 385*e07d83d3SAndroid Build Coastguard WorkerStatus code mapping: **CANCELLED**