1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef SERVICES_SERVICE_MANAGER_PUBLIC_CPP_BINDER_MAP_H_
6 #define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_BINDER_MAP_H_
7 
8 #include <map>
9 #include <memory>
10 #include <string>
11 #include <type_traits>
12 #include <utility>
13 
14 #include "base/callback.h"
15 #include "base/macros.h"
16 #include "base/memory/weak_ptr.h"
17 #include "mojo/public/cpp/system/message_pipe.h"
18 #include "services/service_manager/public/cpp/binder_map_internal.h"
19 
20 namespace service_manager {
21 
22 // Helper class which maps interface names to callbacks that know how to bind
23 // receivers of the corresponding interface type. This is useful for services
24 // which may bind many different types of interface receivers throughout their
25 // lifetime, given the generic name + receiver pipe handle inputs provided by
26 // an incoming |Service::OnConnect| message.
27 //
28 // New binders can be registered in the map by calling |Add()|. To handle an
29 // incoming request to bind a receiver, the owner of this BinderMap should call
30 // |TryBind()| with the given name and receiver pipe. If a suitable binder
31 // callback is registered in the map, it will be run with the receiver passed
32 // in.
33 //
34 // If a non-void ContextType is specified, registered callbacks must accept an
35 // additional ContextType argument, and each invocation of |TryBind()| must
36 // provide such a value.
37 //
38 // NOTE: Most common uses of BinderMapWithContext do not require a context value
39 // per bind request. Use the BinderMap alias defined below this class in such
40 // cases.
41 template <typename ContextType>
42 class BinderMapWithContext {
43  public:
44   using Traits = internal::BinderContextTraits<ContextType>;
45   using ContextValueType = typename Traits::ValueType;
46   using GenericBinderType = typename Traits::GenericBinderType;
47 
48   template <typename Interface>
49   using BinderType = typename Traits::template BinderType<Interface>;
50 
51   BinderMapWithContext() = default;
52   ~BinderMapWithContext() = default;
53 
54   // Adds a new binder specifically for Interface receivers. This exists for the
55   // convenience of being able to register strongly-typed binding methods like:
56   //
57   //   void OnBindFoo(mojo::PendingReceiver<Foo> receiver) { ... }
58   //
59   // more easily.
60   template <typename Interface>
61   void Add(BinderType<Interface> binder,
62            scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
63     binders_[Interface::Name_] = std::make_unique<
64         internal::GenericCallbackBinderWithContext<ContextType>>(
65         Traits::MakeGenericBinder(std::move(binder)), std::move(task_runner));
66   }
67 
68   // Passes |*receiver_pipe| to a registered binder for |interface_name| if any
69   // exists. Returns |true| if successful, or |false| if no binder is registered
70   // for the given interface. Upon returning |false|, |*receiver_pipe| is left
71   // intact for the caller. Only usable when ContextType is void.
TryBind(const std::string & interface_name,mojo::ScopedMessagePipeHandle * receiver_pipe)72   bool TryBind(const std::string& interface_name,
73                mojo::ScopedMessagePipeHandle* receiver_pipe) {
74     static_assert(std::is_same<ContextType, void>::value,
75                   "TryBind() must be called with a context value when "
76                   "ContextType is non-void.");
77     auto it = binders_.find(interface_name);
78     if (it == binders_.end())
79       return false;
80 
81     it->second->BindInterface(std::move(*receiver_pipe));
82     return true;
83   }
84 
85   // Like above, but passes |context| to the binder if one exists. Only usable
86   // when ContextType is non-void.
TryBind(ContextValueType context,const std::string & interface_name,mojo::ScopedMessagePipeHandle * receiver_pipe)87   bool TryBind(ContextValueType context,
88                const std::string& interface_name,
89                mojo::ScopedMessagePipeHandle* receiver_pipe) {
90     static_assert(!std::is_same<ContextType, void>::value,
91                   "TryBind() must be called without a context value when "
92                   "ContextType is void.");
93     auto it = binders_.find(interface_name);
94     if (it == binders_.end())
95       return false;
96 
97     it->second->BindInterface(std::move(context), std::move(*receiver_pipe));
98     return true;
99   }
100 
101   // Clears all registered binders from this map.
Clear()102   void Clear() { binders_.clear(); }
103 
104  private:
105   std::map<
106       std::string,
107       std::unique_ptr<internal::GenericCallbackBinderWithContext<ContextType>>>
108       binders_;
109 
110   DISALLOW_COPY_AND_ASSIGN(BinderMapWithContext);
111 };
112 
113 // Common alias for BinderMapWithContext that has no context. Binders added to
114 // this type of map will only take a single PendingReceiver<T> argument (or a
115 // generic name+pipe combo).
116 using BinderMap = BinderMapWithContext<void>;
117 
118 }  // namespace service_manager
119 
120 #endif  // SERVICES_SERVICE_MANAGER_PUBLIC_CPP_BINDER_MAP_H_
121