// 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 #include #include #include #include #include #include #include #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 must specify such a class as the T; // and clients wishing to perform transactions against such objects must use a // TypedBinderRef to do so. // // See usage comments on SupportsBinder 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; \ static inline AIBinder_Class* GetBinderClass() { \ static AIBinder_Class* const clazz = RegisterBinderClass( \ #name ":" __FILE__ ":" _BINDER_CLASS_LINE(__LINE__)); \ return clazz; \ } \ static inline base::android::TypedBinderRef AdoptBinderRef( \ base::android::BinderRef binder) { \ return base::android::TypedBinderRef::Adopt(std::move(binder)); \ } \ } namespace base::android { class BinderRef; class Parcel; template using BinderStatusOr = expected; // 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 ReadBinder() const; BinderStatusOr ReadInt32() const; BinderStatusOr ReadUint32() const; BinderStatusOr ReadUint64() const; BinderStatusOr 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 BinderStatusOr ReadByteArray(Allocator allocator) const { auto c_allocator = [](void* context, int32_t length, int8_t** out) { const auto& allocator = *static_cast(context); const auto size = saturated_cast(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(data); return !!data; }; return ReadByteArrayImpl(c_allocator, &allocator); } private: BinderStatusOr ReadByteArrayImpl(AParcel_byteArrayAllocator allocator, void* context) const; raw_ptr 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 WriteBinder(BinderRef binder) const; BinderStatusOr WriteInt32(int32_t value) const; BinderStatusOr WriteUint32(uint32_t value) const; BinderStatusOr WriteUint64(uint64_t value) const; BinderStatusOr WriteByteArray(span bytes) const; BinderStatusOr WriteFileDescriptor(ScopedFD fd) const; private: raw_ptr 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 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 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::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 below. BinderStatusOr PrepareTransaction(); BinderStatusOr TransactImpl(transaction_code_t code, Parcel parcel, binder_flags_t flags); protected: raw_ptr 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 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 { 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 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; 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 binder_class_; Lock lock_; // A weak reference to the underlying IBinder, if one exists. raw_ptr 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 self_for_binder_ GUARDED_BY(lock_); }; } // namespace internal // A BinderRef which has been associated with a specific binder class. template class TypedBinderRef : public BinderRef { public: static_assert(std::is_base_of_v, "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 Adopt(BinderRef binder) { TypedBinderRef 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 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 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 BinderStatusOr 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 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 BinderStatusOr 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 { // public: // ... (normal class stuff, plus overrides of SupportsBinder methods) // }; // // // The client. `ref` generally comes from the parent process untyped, // // specifically from some SupportsBinder subclass calling GetBinder(). // void UseThingy(BinderRef ref) { // auto thingy = ThingyInterface::AdoptBinderRef(std::move(ref)); // ... (do transactions with `thingy`) // } template class BASE_EXPORT SupportsBinder : public internal::SupportsBinderBase { public: static_assert(std::is_base_of_v, "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 GetBinder() { return TypedBinderRef(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 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 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_