// // Copyright 2015 Google, Inc. // // 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. // #pragma once #include <functional> #include <mutex> #include <unordered_map> #include <base/logging.h> #include <base/macros.h> #include <binder/IBinder.h> #include <binder/IInterface.h> namespace ipc { namespace binder { // Takes care of the grunt work of maintaining a list of remote interfaces, // typically for the use of performing registered callbacks from a remote // service. This is a native equivalent of the the android.os.RemoteCallbackList // Java class. The type "T" must inherit from android::IInterface. // // TODO(armansito): We need to unit test this class. Right now it's defined as a // simple template interface over the native libbinder types directly and we // can't compile libbinder for host unless the Binder kernel module is enabled // on the system. Figure out whether to: // 1) write the Binder test-code in a new target-only executable; // 2) conditionally compile into the host-native test suite if the Binder // module is built; // 3) provide a test-only static library that re-defines the libbinder // classes as mocks. // (See http://b/23386387) // // TODO(armansito): We should make this class non-final and the template // interface abstract, so that code that depends on this can be unit tested // against a mock version of this class. // // TODO(armansito): Consider submitting this class to frameworks/native/binder. template <typename T> class RemoteCallbackList final { public: RemoteCallbackList() = default; ~RemoteCallbackList(); // Register and unregister a callback interface. Registering will // automatically start tracking for death notifications in case the remote // process hosting the Binder dies. In such a case, the Binder is // automatically removed from the list. bool Register(const android::sp<T>& callback); bool Unregister(const android::sp<T>& callback); // Calls the given function on each of the contained callbacks. The internal // mutex is locked for the duration of the iteration. void ForEach(const std::function<void(T*)>& callback); private: class CallbackDeathRecipient : public android::IBinder::DeathRecipient { public: CallbackDeathRecipient(const android::sp<T>& callback, RemoteCallbackList* owner); android::sp<T> get_callback() const { return callback_; } // android::IBinder::DeathRecipient override: void binderDied(const android::wp<android::IBinder>& who) override; private: android::sp<T> callback_; RemoteCallbackList<T>* owner_; // weak }; // Typedef for internal map container. This keeps track of a given Binder and // a death receiver associated with it. using CallbackMap = std::unordered_map<android::IBinder*, android::sp<CallbackDeathRecipient>>; bool UnregisterInternal(typename CallbackMap::iterator iter); std::mutex map_lock_; CallbackMap callbacks_; DISALLOW_COPY_AND_ASSIGN(RemoteCallbackList); }; // Template Implementation details below // ======================================================== using android::IBinder; using android::IInterface; using android::sp; using android::wp; template <typename T> RemoteCallbackList<T>::~RemoteCallbackList() { std::lock_guard<std::mutex> lock(map_lock_); for (auto iter = callbacks_.begin(); iter != callbacks_.end(); ++iter) UnregisterInternal(iter); } template <typename T> bool RemoteCallbackList<T>::Register(const sp<T>& callback) { std::lock_guard<std::mutex> lock(map_lock_); sp<IBinder> binder = IInterface::asBinder(callback.get()); if (callbacks_.find(binder.get()) != callbacks_.end()) { VLOG(1) << "Callback list already contains given callback"; return false; } sp<CallbackDeathRecipient> dr(new CallbackDeathRecipient(callback, this)); if (binder->linkToDeath(dr) != android::NO_ERROR) { LOG(ERROR) << "Failed to link death recipient to binder"; return false; } callbacks_[binder.get()] = dr; VLOG(2) << "Callback successfully registered with list"; return true; } template <typename T> bool RemoteCallbackList<T>::Unregister(const sp<T>& callback) { std::lock_guard<std::mutex> lock(map_lock_); sp<IBinder> binder = IInterface::asBinder(callback.get()); auto iter = callbacks_.find(binder.get()); if (iter == callbacks_.end()) { VLOG(2) << "Given callback not registered with this list"; return false; } return UnregisterInternal(iter); } template <typename T> void RemoteCallbackList<T>::ForEach(const std::function<void(T*)>& callback) { std::lock_guard<std::mutex> lock(map_lock_); for (const auto& iter : callbacks_) callback(iter.second->get_callback().get()); } template <typename T> bool RemoteCallbackList<T>::UnregisterInternal( typename CallbackMap::iterator iter) { sp<CallbackDeathRecipient> dr = iter->second; callbacks_.erase(iter); if (IInterface::asBinder(dr->get_callback().get())->unlinkToDeath(dr) != android::NO_ERROR) { LOG(ERROR) << "Failed to unlink death recipient from binder"; // We removed the entry from |map_| but unlinkToDeath failed. There isn't // really much we can do here other than deleting the binder and returning // an error. return false; } VLOG(2) << "Callback successfully removed from list"; return true; } template <typename T> RemoteCallbackList<T>::CallbackDeathRecipient::CallbackDeathRecipient( const sp<T>& callback, RemoteCallbackList<T>* owner) : callback_(callback), owner_(owner) { CHECK(callback_.get()); CHECK(owner_); } template <typename T> void RemoteCallbackList<T>::CallbackDeathRecipient::binderDied( const wp<IBinder>& who) { VLOG(1) << "Received binderDied"; sp<IBinder> binder = IInterface::asBinder(callback_.get()); CHECK(who.unsafe_get() == binder.get()); // Remove the callback but no need to call unlinkToDeath. std::lock_guard<std::mutex> lock(owner_->map_lock_); auto iter = owner_->callbacks_.find(binder.get()); CHECK(iter != owner_->callbacks_.end()); owner_->callbacks_.erase(iter); VLOG(1) << "Callback from dead process unregistered"; } } // namespace binder } // namespace ipc