libbinder: Enable service specific error codes

Add another factory method that takes a message and service
specific error code.

Bug: 25800533
Test: system/tools/aidl integration tests pass

Change-Id: I592cb7def0538576965d14c200ab58548b3bef32
diff --git a/Status.cpp b/Status.cpp
index 67f0d59..d3520d6 100644
--- a/Status.cpp
+++ b/Status.cpp
@@ -24,12 +24,21 @@
 }
 
 Status Status::fromExceptionCode(int32_t exceptionCode) {
-    return Status(exceptionCode);
+    return Status(exceptionCode, OK);
 }
 
 Status Status::fromExceptionCode(int32_t exceptionCode,
                                  const String8& message) {
-    return Status(exceptionCode, message);
+    return Status(exceptionCode, OK, message);
+}
+
+Status Status::fromServiceSpecificError(int32_t serviceSpecificErrorCode) {
+    return Status(EX_SERVICE_SPECIFIC, serviceSpecificErrorCode);
+}
+
+Status Status::fromServiceSpecificError(int32_t serviceSpecificErrorCode,
+                                        const String8& message) {
+    return Status(EX_SERVICE_SPECIFIC, serviceSpecificErrorCode, message);
 }
 
 Status Status::fromStatusT(status_t status) {
@@ -38,9 +47,13 @@
     return ret;
 }
 
-Status::Status(int32_t exceptionCode) : mException(exceptionCode) {}
-Status::Status(int32_t exceptionCode, const String8& message)
+Status::Status(int32_t exceptionCode, int32_t errorCode)
     : mException(exceptionCode),
+      mErrorCode(errorCode) {}
+
+Status::Status(int32_t exceptionCode, int32_t errorCode, const String8& message)
+    : mException(exceptionCode),
+      mErrorCode(errorCode),
       mMessage(message) {}
 
 status_t Status::readFromParcel(const Parcel& parcel) {
@@ -79,6 +92,14 @@
     }
     mMessage = String8(message);
 
+    if (mException == EX_SERVICE_SPECIFIC) {
+        status = parcel.readInt32(&mErrorCode);
+    }
+    if (status != OK) {
+        setFromStatusT(status);
+        return status;
+    }
+
     return status;
 }
 
@@ -96,28 +117,39 @@
         return status;
     }
     status = parcel->writeString16(String16(mMessage));
+    if (mException != EX_SERVICE_SPECIFIC) {
+        // We have no more information to write.
+        return status;
+    }
+    status = parcel->writeInt32(mErrorCode);
     return status;
 }
 
-void Status::setFromStatusT(status_t status) {
-    mException = (status == NO_ERROR) ? EX_NONE : EX_TRANSACTION_FAILED;
-    mErrorCode = status;
-    mMessage.clear();
-}
-
 void Status::setException(int32_t ex, const String8& message) {
     mException = ex;
     mErrorCode = NO_ERROR;  // an exception, not a transaction failure.
     mMessage.setTo(message);
 }
 
+void Status::setServiceSpecificError(int32_t errorCode, const String8& message) {
+    setException(EX_SERVICE_SPECIFIC, message);
+    mErrorCode = errorCode;
+}
+
+void Status::setFromStatusT(status_t status) {
+    mException = (status == NO_ERROR) ? EX_NONE : EX_TRANSACTION_FAILED;
+    mErrorCode = status;
+    mMessage.clear();
+}
+
 String8 Status::toString8() const {
     String8 ret;
     if (mException == EX_NONE) {
         ret.append("No error");
     } else {
         ret.appendFormat("Status(%d): '", mException);
-        if (mException == EX_TRANSACTION_FAILED) {
+        if (mException == EX_SERVICE_SPECIFIC ||
+            mException == EX_TRANSACTION_FAILED) {
             ret.appendFormat("%d: ", mErrorCode);
         }
         ret.append(String8(mMessage));
diff --git a/include/hwbinder/Status.h b/include/hwbinder/Status.h
index d19824d..203a01e 100644
--- a/include/hwbinder/Status.h
+++ b/include/hwbinder/Status.h
@@ -60,6 +60,7 @@
         EX_ILLEGAL_STATE = -5,
         EX_NETWORK_MAIN_THREAD = -6,
         EX_UNSUPPORTED_OPERATION = -7,
+        EX_SERVICE_SPECIFIC = -8,
 
         // This is special and Java specific; see Parcel.java.
         EX_HAS_REPLY_HEADER = -128,
@@ -70,11 +71,21 @@
 
     // A more readable alias for the default constructor.
     static Status ok();
-    // Allow authors to explicitly pick whether their integer is a status_t or
-    // exception code.
+    // Authors should explicitly pick whether their integer is:
+    //  - an exception code (EX_* above)
+    //  - service specific error code
+    //  - status_t
+    //
+    //  Prefer a generic exception code when possible, then a service specific
+    //  code, and finally a status_t for low level failures or legacy support.
+    //  Exception codes and service specific errors map to nicer exceptions for
+    //  Java clients.
     static Status fromExceptionCode(int32_t exceptionCode);
     static Status fromExceptionCode(int32_t exceptionCode,
                                     const String8& message);
+    static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode);
+    static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode,
+                                           const String8& message);
     static Status fromStatusT(status_t status);
 
     Status() = default;
@@ -92,9 +103,11 @@
 
     // Set one of the pre-defined exception types defined above.
     void setException(int32_t ex, const String8& message);
+    // Set a service specific exception with error code.
+    void setServiceSpecificError(int32_t errorCode, const String8& message);
     // Setting a |status| != OK causes generated code to return |status|
     // from Binder transactions, rather than writing an exception into the
-    // reply Parcel.
+    // reply Parcel.  This is the least preferable way of reporting errors.
     void setFromStatusT(status_t status);
 
     // Get information about an exception.
@@ -103,6 +116,9 @@
     status_t transactionError() const {
         return mException == EX_TRANSACTION_FAILED ? mErrorCode : OK;
     }
+    int32_t serviceSpecificErrorCode() const {
+        return mException == EX_SERVICE_SPECIFIC ? mErrorCode : 0;
+    }
 
     bool isOk() const { return mException == EX_NONE; }
 
@@ -110,8 +126,8 @@
     String8 toString8() const;
 
 private:
-    Status(int32_t exception_code);
-    Status(int32_t exception_code, const String8& message);
+    Status(int32_t exceptionCode, int32_t errorCode);
+    Status(int32_t exceptionCode, int32_t errorCode, const String8& message);
 
     // If |mException| == EX_TRANSACTION_FAILED, generated code will return
     // |mErrorCode| as the result of the transaction rather than write an
@@ -119,6 +135,7 @@
     //
     // Otherwise, we always write |mException| to the parcel.
     // If |mException| !=  EX_NONE, we write |mMessage| as well.
+    // If |mException| == EX_SERVICE_SPECIFIC we write |mErrorCode| as well.
     int32_t mException = EX_NONE;
     int32_t mErrorCode = 0;
     String8 mMessage;