Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6e361344 authored by Christopher Wiley's avatar Christopher Wiley
Browse files

libbinder: Add binder::Status type

(cherry-pick of 09eb7497)

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
parent ddfe7bd8
Loading
Loading
Loading
Loading
+121 −0
Original line number Diff line number Diff line
/*
 * 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
+1 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ sources := \
    PermissionCache.cpp \
    ProcessState.cpp \
    Static.cpp \
    Status.cpp \
    TextOutput.cpp \

LOCAL_PATH:= $(call my-dir)
+6 −16
Original line number Diff line number Diff line
@@ -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

libs/binder/Status.cpp

0 → 100644
+133 −0
Original line number Diff line number Diff line
/*
 * 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