From: Arman Uguray Date: Fri, 4 Sep 2015 19:57:37 +0000 (-0700) Subject: service: Added RemoteCallbackMap X-Git-Tag: android-x86-7.1-r1~394^2~253^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=f8711f2e;p=android-x86%2Fsystem-bt.git service: Added RemoteCallbackMap Added the RemoteCallbackMap template class which allows storing IInterface subclasses in an associative array while tracking the death of the remote process that owns the associated binder. This is similar to RemoteCallbackList but stores entries in key-value pairs. Also fixed some RemoteCallbackList documentation and change the code to use std::unordered_map rather than std::map. Bug: 23793954 Change-Id: I205c0491d21fef3bc5eeb5c4a7356dab4b6eb8dc --- diff --git a/service/ipc/binder/remote_callback_list.h b/service/ipc/binder/remote_callback_list.h index 33c975d18..aa612fa2e 100644 --- a/service/ipc/binder/remote_callback_list.h +++ b/service/ipc/binder/remote_callback_list.h @@ -17,8 +17,8 @@ #pragma once #include -#include #include +#include #include #include @@ -69,12 +69,12 @@ class RemoteCallbackList final { private: class CallbackDeathRecipient : public android::IBinder::DeathRecipient { public: - explicit CallbackDeathRecipient(const android::sp& callback, - RemoteCallbackList* owner); + CallbackDeathRecipient(const android::sp& callback, + RemoteCallbackList* owner); android::sp get_callback() const { return callback_; } - // android::DeathRecipient override: + // android::IBinder::DeathRecipient override: void binderDied(const android::wp& who) override; private: @@ -84,8 +84,8 @@ class RemoteCallbackList final { // Typedef for internal map container. This keeps track of a given Binder and // a death receiver associated with it. - using CallbackMap = std::map, - android::sp>; + using CallbackMap = std::unordered_map>; bool UnregisterInternal(typename CallbackMap::iterator iter); @@ -115,7 +115,7 @@ bool RemoteCallbackList::Register(const sp& callback) { std::lock_guard lock(map_lock_); sp binder = IInterface::asBinder(callback.get()); - if (callbacks_.find(binder) != callbacks_.end()) { + if (callbacks_.find(binder.get()) != callbacks_.end()) { VLOG(1) << "Callback list already contains given callback"; return false; } @@ -127,9 +127,9 @@ bool RemoteCallbackList::Register(const sp& callback) { return false; } - callbacks_[binder] = dr; + callbacks_[binder.get()] = dr; - VLOG(1) << "Callback successfully registered"; + VLOG(2) << "Callback successfully registered with list"; return true; } @@ -139,9 +139,9 @@ bool RemoteCallbackList::Unregister(const sp& callback) { std::lock_guard lock(map_lock_); sp binder = IInterface::asBinder(callback.get()); - auto iter = callbacks_.find(binder); + auto iter = callbacks_.find(binder.get()); if (iter == callbacks_.end()) { - VLOG(1) << "Given callback not registered"; + VLOG(2) << "Given callback not registered with this list"; return false; } @@ -164,10 +164,14 @@ bool RemoteCallbackList::UnregisterInternal( 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(1) << "Callback successfully unregistered"; + VLOG(2) << "Callback successfully removed from list"; return true; } @@ -192,7 +196,7 @@ void RemoteCallbackList::CallbackDeathRecipient::binderDied( // Remove the callback but no need to call unlinkToDeath. std::lock_guard lock(owner_->map_lock_); - auto iter = owner_->callbacks_.find(binder); + auto iter = owner_->callbacks_.find(binder.get()); CHECK(iter != owner_->callbacks_.end()); owner_->callbacks_.erase(iter); diff --git a/service/ipc/binder/remote_callback_map.h b/service/ipc/binder/remote_callback_map.h new file mode 100644 index 000000000..7fdb197b4 --- /dev/null +++ b/service/ipc/binder/remote_callback_map.h @@ -0,0 +1,260 @@ +// +// Copyright (C) 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 +#include + +#include +#include +#include +#include + +namespace ipc { +namespace binder { + +// A map of remote interfaces where the value type "V" must inherit from +// android::IInterface. This is just like RemoteCallbackList except it provides +// a key-value mapping. +// +// 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. +template +class RemoteCallbackMap final { + public: + RemoteCallbackMap() = default; + ~RemoteCallbackMap(); + + // The Delegate interface is used to notify when a registered callback is + // removed from the map as a result of the death of the remote process that + // owns the registered callback. + class Delegate { + public: + virtual ~Delegate() = default; + + // Called when a remote callback associated with the key |key| has been + // removed. This won't get called if the callback was removed as a result of + // a call to RemoteCallbackMap::Unregister. + virtual void OnRemoteCallbackRemoved(const K& key) = 0; + }; + + // Register a callback interface and associate it with the given key. + // 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 map. + // + // An optional |delegate| can be passed which will be assocated with the given + // key/value pair. |delegate| must outlive this map. + bool Register(const K& key, + const android::sp& callback, + Delegate* delegate = nullptr); + + // Unregisters a previously registered callback with the given key. Returns + // false if |key| doesn't exist. + bool Unregister(const K& key); + + // Returns the callback associated with the given key. Returns NULL if |key| + // doesn't exist. + android::sp Get(const K& key); + + // Removes the callback associated with the given key from the map and returns + // the value. Returns NULL if the key doesn't exists. + android::sp Remove(const K& key); + + // Clear the contents of the map. + void Clear(); + + private: + class CallbackDeathRecipient : public android::IBinder::DeathRecipient { + public: + CallbackDeathRecipient( + const K& key, + const android::sp& callback, + RemoteCallbackMap* owner, + Delegate* delegate); + + android::sp get_callback() const { return callback_; } + + // android::IBinder::DeathRecipient override: + void binderDied(const android::wp& who) override; + + private: + K key_; + android::sp callback_; + RemoteCallbackMap* owner_; // weak + Delegate* delegate_; // weak + }; + + // Typedef for internal map container. + using CallbackMap = + std::unordered_map>; + + bool UnregisterInternal(typename CallbackMap::iterator iter); + + std::mutex map_lock_; + CallbackMap map_; + + DISALLOW_COPY_AND_ASSIGN(RemoteCallbackMap); +}; + +// Template Implementation details below +// ======================================================== + +using android::IBinder; +using android::IInterface; +using android::sp; +using android::wp; +using std::lock_guard; +using std::mutex; + +template +RemoteCallbackMap::~RemoteCallbackMap() { + Clear(); +} + +template +bool RemoteCallbackMap::Register( + const K& key, + const sp& callback, + Delegate* delegate) { + lock_guard lock(map_lock_); + + if (map_.find(key) != map_.end()) { + VLOG(1) << "Callback map already contains key"; + return false; + } + + sp dr( + new CallbackDeathRecipient(key, callback, this, delegate)); + sp binder = IInterface::asBinder(callback.get()); + if (binder->linkToDeath(dr) != android::NO_ERROR) { + LOG(ERROR) << "Failed to link death recipient to binder"; + return false; + } + + map_[key] = dr; + + VLOG(2) << "Callback successfully registered with map"; + + return true; +} + +template +bool RemoteCallbackMap::Unregister(const K& key) { + lock_guard lock(map_lock_); + + auto iter = map_.find(key); + if (iter == map_.end()) { + VLOG(1) << "Callback with given key not found"; + return false; + } + + return UnregisterInternal(iter); +} + +template +sp RemoteCallbackMap::Get(const K& key) { + lock_guard lock(map_lock_); + + auto iter = map_.find(key); + if (iter == map_.end()) + return nullptr; + + return iter->second; +} + +template +sp RemoteCallbackMap::Remove(const K& key) { + lock_guard lock(map_lock_); + + auto iter = map_.find(key); + if (iter == map_.end()) + return nullptr; + + sp val = iter->second->get_callback(); + UnregisterInternal(iter); + + return val; +} +template +void RemoteCallbackMap::Clear() { + lock_guard lock(map_lock_); + + for (auto iter = map_.begin(); iter != map_.end();) + UnregisterInternal(iter++); +} + +template +bool RemoteCallbackMap::UnregisterInternal( + typename CallbackMap::iterator iter) { + sp dr = iter->second; + map_.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 map"; + + return true; +} + +template +RemoteCallbackMap::CallbackDeathRecipient::CallbackDeathRecipient( + const K& key, + const sp& callback, + RemoteCallbackMap* owner, + Delegate* delegate) + : key_(key), + callback_(callback), + owner_(owner), + delegate_(delegate) { + CHECK(callback_.get()); +} + +template +void RemoteCallbackMap::CallbackDeathRecipient::binderDied( + const wp& who) { + VLOG(1) << "Received binderDied"; + + sp binder = IInterface::asBinder(callback_.get()); + CHECK(who.unsafe_get() == binder.get()); + + // Remove the callback but no need to call unlinkToDeath. + { + lock_guard lock(owner_->map_lock_); + auto iter = owner_->map_.find(key_); + CHECK(iter != owner_->map_.end()); + owner_->map_.erase(iter); + } + + VLOG(1) << "Callback from dead process unregistered; notifying delegate"; + + // Notify delegate. + if (delegate_) + delegate_->OnRemoteCallbackRemoved(key_); +} + +} // namespace binder +} // namespace ipc diff --git a/service/uuid.h b/service/uuid.h index 2bc742e54..89a2c8e17 100644 --- a/service/uuid.h +++ b/service/uuid.h @@ -19,6 +19,8 @@ #include #include +#include + #include "hardware/bluetooth.h" namespace bluetooth { @@ -72,3 +74,21 @@ class UUID { }; } // namespace bluetooth + +// Custom std::hash specialization so that bluetooth::UUID can be used as a key +// in std::unordered_map. +namespace std { + +template<> +struct hash { + std::size_t operator()(const bluetooth::UUID& key) const { + // Compute individual hash values for each byte and then combine them using + // XOR and bitshifting. + const auto& uuid_bytes = key.GetFullBigEndian(); + + return base::SuperFastHash(reinterpret_cast(uuid_bytes.data()), + uuid_bytes.size()); + } +}; + +} // namespace std