// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ANDROID_BINDER_H_
#define BASE_ANDROID_BINDER_H_
#include <android/binder_ibinder.h>
#include <android/binder_parcel.h>
#include <android/binder_status.h>
#include <jni.h>
#include <cstdint>
#include <type_traits>
#include <utility>
#include <vector>
#include "base/android/scoped_java_ref.h"
#include "base/base_export.h"
#include "base/check.h"
#include "base/containers/span.h"
#include "base/files/scoped_file.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/numerics/safe_conversions.h"
#include "base/synchronization/lock.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
// DEFINE_BINDER_CLASS() generates a definition for a unique binder class.
// Binder classes are used by the binder implementation to enforce a kind of
// type safety, requiring client IBinders to be associated with the same class
// as the remote object's original IBinder.
//
// Objects implementing SupportsBinder<T> must specify such a class as the T;
// and clients wishing to perform transactions against such objects must use a
// TypedBinderRef<T> to do so.
//
// See usage comments on SupportsBinder<T> below.
#define _BINDER_CLASS_LINE(line) _BINDER_CLASS_LINE2(line)
#define _BINDER_CLASS_LINE2(line) #line
#define DEFINE_BINDER_CLASS(name) \
struct name : public base::android::internal::BinderClassBase { \
using BinderRef = base::android::TypedBinderRef<name>; \
static inline AIBinder_Class* GetBinderClass() { \
static AIBinder_Class* const clazz = RegisterBinderClass( \
#name ":" __FILE__ ":" _BINDER_CLASS_LINE(__LINE__)); \
return clazz; \
} \
static inline base::android::TypedBinderRef<name> AdoptBinderRef( \
base::android::BinderRef binder) { \
return base::android::TypedBinderRef<name>::Adopt(std::move(binder)); \
} \
}
namespace base::android {
class BinderRef;
class Parcel;
template <typename T>
using BinderStatusOr = expected<T, binder_status_t>;
// Provides a read-only view into a AParcel. Does not retain ownership of the
// AParcel, which must outlive this object.
class BASE_EXPORT ParcelReader {
public:
explicit ParcelReader(const AParcel* parcel);
explicit ParcelReader(const Parcel& parcel);
ParcelReader(const ParcelReader&);
ParcelReader& operator=(const ParcelReader&);
~ParcelReader();
// A subset of the NDK functions defined for reading from an AParcel. Others
// may be exposed here as needed.
BinderStatusOr<BinderRef> ReadBinder() const;
BinderStatusOr<int32_t> ReadInt32() const;
BinderStatusOr<uint32_t> ReadUint32() const;
BinderStatusOr<uint64_t> ReadUint64() const;
BinderStatusOr<ScopedFD> ReadFileDescriptor() const;
// Reads a byte array from the parcel. `allocator` is called with a single
// size_t argument for the number of bytes in the array and must return a
// pointer to at least that much memory, into which ReadByteArray() will copy
// the array data before returning. If the parcel contains an empty or null
// byte array, `allocator` is not invoked. If `allocator` is invoked and
// returns null, ReadByteArray() returns an error.
template <typename Allocator>
BinderStatusOr<void> ReadByteArray(Allocator allocator) const {
auto c_allocator = [](void* context, int32_t length, int8_t** out) {
const auto& allocator = *static_cast<Allocator*>(context);
const auto size = saturated_cast<size_t>(length);
if (!size) {
*out = nullptr;
return true;
}
// Binder API wants int8_t for bytes, but we generally use uint8_t.
uint8_t* const data = allocator(size);
*out = reinterpret_cast<int8_t*>(data);
return !!data;
};
return ReadByteArrayImpl(c_allocator, &allocator);
}
private:
BinderStatusOr<void> ReadByteArrayImpl(AParcel_byteArrayAllocator allocator,
void* context) const;
raw_ptr<const AParcel> parcel_;
};
// Provides a writable view into a AParcel. Does not retain ownership of the
// AParcel, which must outlive this object.
class BASE_EXPORT ParcelWriter {
public:
explicit ParcelWriter(AParcel* parcel);
explicit ParcelWriter(Parcel& parcel);
ParcelWriter(const ParcelWriter&);
ParcelWriter& operator=(const ParcelWriter&);
~ParcelWriter();
// A subset of the NDK functions defined for writing to an AParcel. Others may
// be exposed here as needed.
BinderStatusOr<void> WriteBinder(BinderRef binder) const;
BinderStatusOr<void> WriteInt32(int32_t value) const;
BinderStatusOr<void> WriteUint32(uint32_t value) const;
BinderStatusOr<void> WriteUint64(uint64_t value) const;
BinderStatusOr<void> WriteByteArray(span<const uint8_t> bytes) const;
BinderStatusOr<void> WriteFileDescriptor(ScopedFD fd) const;
private:
raw_ptr<AParcel> parcel_;
};
// Wraps unique ownership of an AParcel. This is similar to the NDK's
// ScopedAParcel, but it uses our internal BinderApi to invoke NDK functions.
class BASE_EXPORT Parcel {
public:
Parcel();
explicit Parcel(AParcel* parcel);
Parcel(Parcel&& other);
Parcel& operator=(Parcel&& other);
~Parcel();
explicit operator bool() const { return parcel_ != nullptr; }
const AParcel* get() const { return parcel_; }
AParcel* get() { return parcel_; }
[[nodiscard]] AParcel* release() { return std::exchange(parcel_, nullptr); }
void reset();
ParcelReader reader() const { return ParcelReader(*this); }
ParcelWriter writer() { return ParcelWriter(*this); }
private:
raw_ptr<AParcel> parcel_ = nullptr;
};
// A BinderRef owns a strong ref-count on an AIBinder. This is like the NDK's
// SpAIBinder, but it uses our internal BinderApi to invoke NDK functions.
class BASE_EXPORT BinderRef {
public:
BinderRef();
explicit BinderRef(AIBinder* binder);
BinderRef(const BinderRef& other);
BinderRef& operator=(const BinderRef& other);
BinderRef(BinderRef&& other);
BinderRef& operator=(BinderRef&& other);
~BinderRef();
explicit operator bool() const { return binder_ != nullptr; }
AIBinder* get() const { return binder_; }
[[nodiscard]] AIBinder* release() { return std::exchange(binder_, nullptr); }
void reset();
// Returns a new strong reference to this binder as a local Java object
// reference.
ScopedJavaLocalRef<jobject> ToJavaBinder(JNIEnv* env) const;
// Returns a new strong reference to an existing Java binder as a BinderRef.
static BinderRef FromJavaBinder(JNIEnv* env, jobject java_binder);
// Attempts to associate this binder with `binder_class`. Generally should be
// used via TypedBinderRef<T>::Adopt() or the equivalent T::AdoptBinderRef()
// for some binder class T.
bool AssociateWithClass(AIBinder_Class* binder_class);
protected:
// Protected to force usage through a strongly typed subclass, ensuring that
// transaction clients have an associated binder class. See documentation on
// TypedBinderRef<T> below.
BinderStatusOr<Parcel> PrepareTransaction();
BinderStatusOr<Parcel> TransactImpl(transaction_code_t code,
Parcel parcel,
binder_flags_t flags);
protected:
raw_ptr<AIBinder> binder_ = nullptr;
};
namespace internal {
// Base class for classes generated by DEFINE_BINDER_CLASS().
class BASE_EXPORT BinderClassBase {
public:
static AIBinder_Class* RegisterBinderClass(const char* descriptor);
};
// Common implementation for SupportsBinder<T> below. Instances of this base
// class handle IBinder callbacks and forward events for destruction and
// incoming transactions to a templated subclass.
class BASE_EXPORT SupportsBinderBase
: public RefCountedThreadSafe<SupportsBinderBase> {
public:
explicit SupportsBinderBase(AIBinder_Class* binder_class);
// Called for every incoming transaction on the underlying IBinder. Note that
// this is called from the binder thread pool so implementations must be
// thread-safe.
virtual BinderStatusOr<void> OnBinderTransaction(transaction_code_t code,
const ParcelReader& in,
const ParcelWriter& out) = 0;
// Called any time the underlying IBinder is destroyed. Note that this may be
// invoked multiple times, as the underlying IBinder exists only as long as
// there are living BinderRefs referencing this object. If BinderRefs are
// created and then all destroyed, this will be invoked once. If subsequent
// BinderRefs are created and then all destroyed, this will be invoked again.
//
// Similar to OnBinderTransaction, this is invoked from the binder thread pool
// and implementations must be thread-safe.
virtual void OnBinderDestroyed();
protected:
friend class RefCountedThreadSafe<SupportsBinderBase>;
friend class BinderClassBase;
virtual ~SupportsBinderBase();
// Creates a strong reference to the underlying IBinder, allocating a new
// IBinder if one did not already exist for this object.
BinderRef GetBinder();
private:
void OnBinderDestroyedBase();
// Binder class callbacks.
static void* OnIBinderCreate(void* self);
static void OnIBinderDestroy(void* self);
static binder_status_t OnIBinderTransact(AIBinder* binder,
transaction_code_t code,
const AParcel* in,
AParcel* out);
const raw_ptr<AIBinder_Class> binder_class_;
Lock lock_;
// A weak reference to the underlying IBinder, if one exists.
raw_ptr<AIBinder_Weak> weak_binder_ GUARDED_BY(lock_) = nullptr;
// As long as any IBinder is alive for this object, we retain an extra ref
// count on `this` to ensure that transactions can be handled safely.
scoped_refptr<SupportsBinderBase> self_for_binder_ GUARDED_BY(lock_);
};
} // namespace internal
// A BinderRef which has been associated with a specific binder class.
template <typename T>
class TypedBinderRef : public BinderRef {
public:
static_assert(std::is_base_of_v<android::internal::BinderClassBase, T>,
"Invalid binder class type");
TypedBinderRef() = default;
// Asserts that the binder can be associated with class T. This is safe to
// call when it's known that the binder hasn't been associated with any other
// class in the calling process yet.
explicit TypedBinderRef(BinderRef binder) {
CHECK(!binder || binder.AssociateWithClass(T::GetBinderClass()));
binder_ = binder.release();
}
TypedBinderRef(const TypedBinderRef&) = default;
TypedBinderRef& operator=(const TypedBinderRef&) = default;
TypedBinderRef(TypedBinderRef&&) = default;
TypedBinderRef& operator=(TypedBinderRef&&) = default;
~TypedBinderRef() = default;
// Adopts a BinderRef that is not already associated with another binder
// class, associating it with T. If `binder` is already associated with T this
// is a no-op which only narrows the ref type.
//
// If `binder` was already associated with a binder class other than T, the
// reference is dropped and this returns null.
//
// For convenience clients may instead prefer to call this method via
// T::AdoptBinderRef() as defined by DEFINE_BINDER_CLASS(T).
static TypedBinderRef<T> Adopt(BinderRef binder) {
TypedBinderRef<T> typed_binder;
if (binder.AssociateWithClass(T::GetBinderClass())) {
typed_binder.binder_ = binder.release();
}
return typed_binder;
}
// Prepares a new transaction on this binder, returning a Parcel that can be
// populated and then sent via Transact() or TransactOneWay() below.
BinderStatusOr<Parcel> PrepareTransaction() {
return BinderRef::PrepareTransaction();
}
// Transact with a `parcel` created by a call to PrepareTransaction() on the
// same binder. Returns the output parcel from the transaction. `code` is
// an arbitrary value with interface-specific meaning.
BinderStatusOr<Parcel> Transact(transaction_code_t code, Parcel parcel) {
return TransactImpl(code, std::move(parcel), /*flags=*/0);
}
// Like Transact(), but this internally prepares a transacation and passes the
// allocated Parcel into `fn`. After `fn` returns the Parcel is transacted.
template <typename Fn>
BinderStatusOr<Parcel> Transact(transaction_code_t code, Fn fn) {
ASSIGN_OR_RETURN(auto parcel, PrepareTransaction());
RETURN_IF_ERROR(fn(ParcelWriter(parcel.get())));
return Transact(code, std::move(parcel));
}
// Like Transact() but asynchronous. Discards the empty response parcel.
BinderStatusOr<void> TransactOneWay(transaction_code_t code, Parcel parcel) {
RETURN_IF_ERROR(TransactImpl(code, std::move(parcel), FLAG_ONEWAY));
return ok();
}
// Like TransactOneWay(), but this internally prepares a transaction
// passes the allocated Parcel into `fn`. After `fn` returns the Parcel is
// transacted.
template <typename Fn>
BinderStatusOr<void> TransactOneWay(transaction_code_t code, Fn fn) {
ASSIGN_OR_RETURN(auto parcel, PrepareTransaction());
RETURN_IF_ERROR(fn(ParcelWriter(parcel.get())));
return TransactOneWay(code, std::move(parcel));
}
};
// Base class for objects which support native binder transactions. Example
// usage:
//
// // In some common header.
// DEFINE_BINDER_CLASS(ThingyInterface);
//
// // The interface implementation.
// class Thingy : public base::android::SupportsBinder<ThingyInterface> {
// public:
// ... (normal class stuff, plus overrides of SupportsBinder methods)
// };
//
// // The client. `ref` generally comes from the parent process untyped,
// // specifically from some SupportsBinder<T> subclass calling GetBinder().
// void UseThingy(BinderRef ref) {
// auto thingy = ThingyInterface::AdoptBinderRef(std::move(ref));
// ... (do transactions with `thingy`)
// }
template <typename T>
class BASE_EXPORT SupportsBinder : public internal::SupportsBinderBase {
public:
static_assert(std::is_base_of_v<android::internal::BinderClassBase, T>,
"Invalid binder class type");
SupportsBinder() : SupportsBinderBase(T::GetBinderClass()) {}
// Creates a strong reference to the underlying IBinder, allocating a new
// IBinder if one did not already exist for this object.
TypedBinderRef<T> GetBinder() {
return TypedBinderRef<T>(SupportsBinderBase::GetBinder());
}
protected:
~SupportsBinder() override = default;
};
// Indicates whether Binder NDK functionality is generally available to the
// caller. If this returns false, BinderRefs will always be null and
// SupportsBinder<T> implementations will never receive binder transactions; but
// definitions within this header are otherwise still safe to reference and use.
BASE_EXPORT bool IsNativeBinderAvailable();
// Stashes a global collection of BinderRefs for later retrieval by
// TakeBinderFromParent(). This is intended for use by generic multiprocess
// support code to retain interfaces from the parent process so application-
// specific logic in the child process can retrieve them later. It should be
// called at most once per process, and as early as possible.
BASE_EXPORT void SetBindersFromParent(std::vector<BinderRef> binders);
// Retrieves (by index) a BinderRef which was stashed earlier by
// SetBindersFromParent(). If there is no binder for the given index, the
// returned BinderRef is null. This consumes the binder for that index, so
// subsequent calls for the same index will always return null.
BASE_EXPORT BinderRef TakeBinderFromParent(size_t index);
} // namespace base::android
#endif // BASE_ANDROID_BINDER_H_