diff options
| author | 2015-11-09 15:06:15 -0800 | |
|---|---|---|
| committer | 2015-11-19 08:02:37 -0800 | |
| commit | 6e361344fda22c98a36c596819470a699896dcac (patch) | |
| tree | b8d89229339af7c9c1254562bf894c10221e5a85 | |
| parent | ddfe7bd8b89673473f77f927c3464e7de0d83baf (diff) | |
libbinder: Add binder::Status type
(cherry-pick of 09eb749704afd9e226e1347cb20c90be2016cd21)
This object implements equivalent functionality to the Java logic which
serializes and re-throws exceptions from services.
Bug: 25615695
Test: Integration test for generated AIDL code reveals this to work
correctly.
Change-Id: I5a57710a148ffbd18a4a2c6f0f4fb6d409e0bf8f
| -rw-r--r-- | include/binder/Status.h | 121 | ||||
| -rw-r--r-- | libs/binder/Android.mk | 1 | ||||
| -rw-r--r-- | libs/binder/Parcel.cpp | 22 | ||||
| -rw-r--r-- | libs/binder/Status.cpp | 133 |
4 files changed, 261 insertions, 16 deletions
diff --git a/include/binder/Status.h b/include/binder/Status.h new file mode 100644 index 0000000000..04738f8dd1 --- /dev/null +++ b/include/binder/Status.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BINDER_STATUS_H +#define ANDROID_BINDER_STATUS_H + +#include <cstdint> + +#include <binder/Parcel.h> +#include <utils/String8.h> + +namespace android { +namespace binder { + +// An object similar in function to a status_t except that it understands +// how exceptions are encoded in the prefix of a Parcel. Used like: +// +// Parcel data; +// Parcel reply; +// status_t status; +// binder::Status remote_exception; +// if ((status = data.writeInterfaceToken(interface_descriptor)) != OK || +// (status = data.writeInt32(function_input)) != OK) { +// // We failed to write into the memory of our local parcel? +// } +// if ((status = remote()->transact(transaction, data, &reply)) != OK) { +// // Something has gone wrong in the binder driver or libbinder. +// } +// if ((status = remote_exception.readFromParcel(reply)) != OK) { +// // The remote didn't correctly write the exception header to the +// // reply. +// } +// if (!remote_exception.isOk()) { +// // The transaction went through correctly, but the remote reported an +// // exception during handling. +// } +// +class Status final { +public: + // Keep the exception codes in sync with android/os/Parcel.java. + enum Exception { + EX_NONE = 0, + EX_SECURITY = -1, + EX_BAD_PARCELABLE = -2, + EX_ILLEGAL_ARGUMENT = -3, + EX_NULL_POINTER = -4, + EX_ILLEGAL_STATE = -5, + EX_NETWORK_MAIN_THREAD = -6, + EX_UNSUPPORTED_OPERATION = -7, + EX_TRANSACTION_FAILED = -8, + + // This is special and Java specific; see Parcel.java. + EX_HAS_REPLY_HEADER = -128, + }; + + // Allow authors to explicitly pick whether their integer is a status_t or + // exception code. + static Status fromExceptionCode(int32_t exception_code); + static Status fromStatusT(status_t status); + // A more readable alias for the default constructor. + static Status ok(); + + Status() = default; + Status(int32_t exception_code, const String8& message); + Status(int32_t exception_code, const char* message); + + + // Status objects are copyable and contain just simple data. + Status(const Status& status) = default; + Status(Status&& status) = default; + Status& operator=(const Status& status) = default; + + ~Status() = default; + + // Bear in mind that if the client or service is a Java endpoint, this + // is not the logic which will provide/interpret the data here. + status_t readFromParcel(const Parcel& parcel); + status_t writeToParcel(Parcel* parcel) const; + + // Set one of the pre-defined exception types defined above. + void setException(int32_t ex, const String8& message); + // A few of the status_t values map to exception codes, but most of them + // simply map to "transaction failed." + void setFromStatusT(status_t status); + + // Get information about an exception. + // Any argument may be given as nullptr. + void getException(int32_t* returned_exception, + String8* returned_message) const; + int32_t exceptionCode() const { return mException; } + const String8& exceptionMessage() const { return mMessage; } + + bool isOk() const { return mException == EX_NONE; } + + // For logging. + String8 toString8() const; + +private: + // We always write |mException| to the parcel. + // If |mException| != EX_NONE, we write message as well. + int32_t mException = EX_NONE; + String8 mMessage; +}; // class Status + +} // namespace binder +} // namespace android + +#endif // ANDROID_BINDER_STATUS_H diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk index d5860ef6ca..bd432f318a 100644 --- a/libs/binder/Android.mk +++ b/libs/binder/Android.mk @@ -36,6 +36,7 @@ sources := \ PermissionCache.cpp \ ProcessState.cpp \ Static.cpp \ + Status.cpp \ TextOutput.cpp \ LOCAL_PATH:= $(call my-dir) diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index b8f5436491..1d6ec9e9fe 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -23,6 +23,7 @@ #include <binder/Binder.h> #include <binder/BpBinder.h> #include <binder/ProcessState.h> +#include <binder/Status.h> #include <binder/TextOutput.h> #include <errno.h> @@ -69,9 +70,6 @@ static size_t pad_size(size_t s) { // Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER #define STRICT_MODE_PENALTY_GATHER (0x40 << 16) -// Note: must be kept in sync with android/os/Parcel.java's EX_HAS_REPLY_HEADER -#define EX_HAS_REPLY_HEADER -128 - // XXX This can be made public if we want to provide // support for typed data. struct small_flat_data @@ -1230,7 +1228,8 @@ restart_write: status_t Parcel::writeNoException() { - return writeInt32(0); + binder::Status status; + return status.writeToParcel(this); } void Parcel::remove(size_t /*start*/, size_t /*amt*/) @@ -1643,18 +1642,9 @@ wp<IBinder> Parcel::readWeakBinder() const int32_t Parcel::readExceptionCode() const { - int32_t exception_code = readAligned<int32_t>(); - if (exception_code == EX_HAS_REPLY_HEADER) { - int32_t header_start = dataPosition(); - int32_t header_size = readAligned<int32_t>(); - // Skip over fat responses headers. Not used (or propagated) in - // native code - setDataPosition(header_start + header_size); - // And fat response headers are currently only used when there are no - // exceptions, so return no error: - return 0; - } - return exception_code; + binder::Status status; + status.readFromParcel(*this); + return status.exceptionCode(); } native_handle* Parcel::readNativeHandle() const diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp new file mode 100644 index 0000000000..41fff3d4cd --- /dev/null +++ b/libs/binder/Status.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/Status.h> + +namespace android { +namespace binder { + +Status Status::fromExceptionCode(int32_t exception_code) { + return Status(exception_code, ""); +} + +Status Status::fromStatusT(status_t status) { + Status ret; + ret.setFromStatusT(status); + return ret; +} + +Status Status::ok() { + return Status(); +} + +Status::Status(int32_t exception_code, const String8& message) + : mException(exception_code), + mMessage(message) {} + +Status::Status(int32_t exception_code, const char* message) + : mException(exception_code), + mMessage(message) {} + +status_t Status::readFromParcel(const Parcel& parcel) { + status_t status = parcel.readInt32(&mException); + if (status != OK) { + setFromStatusT(status); + return status; + } + + // Skip over fat response headers. Not used (or propagated) in native code. + if (mException == EX_HAS_REPLY_HEADER) { + // Note that the header size includes the 4 byte size field. + const int32_t header_start = parcel.dataPosition(); + int32_t header_size; + status = parcel.readInt32(&header_size); + if (status != OK) { + setFromStatusT(status); + return status; + } + parcel.setDataPosition(header_start + header_size); + // And fat response headers are currently only used when there are no + // exceptions, so act like there was no error. + mException = EX_NONE; + } + + if (mException == EX_NONE) { + return status; + } + + // The remote threw an exception. Get the message back. + mMessage = String8(parcel.readString16()); + + return status; +} + +status_t Status::writeToParcel(Parcel* parcel) const { + status_t status = parcel->writeInt32(mException); + if (status != OK) { return status; } + if (mException == EX_NONE) { + // We have no more information to write. + return status; + } + status = parcel->writeString16(String16(mMessage)); + return status; +} + +void Status::setFromStatusT(status_t status) { + switch (status) { + case NO_ERROR: + mException = EX_NONE; + mMessage.clear(); + break; + case UNEXPECTED_NULL: + mException = EX_NULL_POINTER; + mMessage.setTo("Unexpected null reference in Parcel"); + break; + default: + mException = EX_TRANSACTION_FAILED; + mMessage.setTo("Transaction failed"); + break; + } +} + +void Status::setException(int32_t ex, const String8& message) { + mException = ex; + mMessage.setTo(message); +} + +void Status::getException(int32_t* returned_exception, + String8* returned_message) const { + if (returned_exception) { + *returned_exception = mException; + } + if (returned_message) { + returned_message->setTo(mMessage); + } +} + +String8 Status::toString8() const { + String8 ret; + if (mException == EX_NONE) { + ret.append("No error"); + } else { + ret.appendFormat("Status(%d): '", mException); + ret.append(String8(mMessage)); + ret.append("'"); + } + return ret; +} + +} // namespace binder +} // namespace android |