1 // Copyright 2014 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 EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
6 #define EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
7 
8 #include <map>
9 #include <memory>
10 #include <string>
11 #include <unordered_set>
12 #include <utility>
13 
14 #include "base/bind.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/scoped_observer.h"
18 #include "base/sequence_checker.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/task/post_task.h"
21 #include "components/keyed_service/core/keyed_service.h"
22 #include "content/public/browser/browser_task_traits.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "extensions/browser/browser_context_keyed_api_factory.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/browser/extension_registry_factory.h"
27 #include "extensions/browser/extension_registry_observer.h"
28 #include "extensions/browser/process_manager.h"
29 #include "extensions/browser/process_manager_factory.h"
30 #include "extensions/browser/process_manager_observer.h"
31 #include "extensions/common/extension.h"
32 
33 namespace extensions {
34 class CastChannelAsyncApiFunction;
35 
36 namespace api {
37 class BluetoothSocketApiFunction;
38 class BluetoothSocketEventDispatcher;
39 class SerialConnectFunction;
40 class SerialPortManager;
41 class TCPServerSocketEventDispatcher;
42 class TCPSocketEventDispatcher;
43 class UDPSocketEventDispatcher;
44 }  // namespace api
45 
46 template <typename T>
47 struct NamedThreadTraits {
48   static_assert(T::kThreadId == content::BrowserThread::IO ||
49                     T::kThreadId == content::BrowserThread::UI,
50                 "ApiResources can only belong to the IO or UI thread.");
51 
IsThreadInitializedNamedThreadTraits52   static bool IsThreadInitialized() {
53     return content::BrowserThread::IsThreadInitialized(T::kThreadId);
54   }
55 
GetSequencedTaskRunnerNamedThreadTraits56   static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
57     return base::CreateSingleThreadTaskRunner({T::kThreadId});
58   }
59 };
60 
61 // An ApiResourceManager manages the lifetime of a set of resources that
62 // that live on named threads (i.e. BrowserThread::IO) which ApiFunctions use.
63 // Examples of such resources are sockets or USB connections. Note: The only
64 // named threads that are allowed are the IO and UI threads, since all others
65 // are deprecated. If we ever need a resource on a different background thread,
66 // we can modify NamedThreadTraits to be more generic and just return a task
67 // runner.
68 //
69 // Users of this class should define kThreadId to be the thread that
70 // ApiResourceManager to works on. The default is defined in ApiResource.
71 // The user must also define a static const char* service_name() that returns
72 // the name of the service, and in order for ApiResourceManager to use
73 // service_name() friend this class.
74 //
75 // In the cc file the user must define a GetFactoryInstance() and manage their
76 // own instances (typically using LazyInstance or Singleton).
77 //
78 // E.g.:
79 //
80 // class Resource {
81 //  public:
82 //   static const BrowserThread::ID kThreadId = BrowserThread::IO;
83 //  private:
84 //   friend class ApiResourceManager<Resource>;
85 //   static const char* service_name() {
86 //     return "ResourceManager";
87 //    }
88 // };
89 //
90 // In the cc file:
91 //
92 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
93 //     ApiResourceManager<Resource> > >
94 //         g_factory = LAZY_INSTANCE_INITIALIZER;
95 //
96 //
97 // template <>
98 // BrowserContextKeyedAPIFactory<ApiResourceManager<Resource> >*
99 // ApiResourceManager<Resource>::GetFactoryInstance() {
100 //   return g_factory.Pointer();
101 // }
102 template <class T, typename ThreadingTraits = NamedThreadTraits<T>>
103 class ApiResourceManager : public BrowserContextKeyedAPI,
104                            public ExtensionRegistryObserver,
105                            public ProcessManagerObserver {
106  public:
ApiResourceManager(content::BrowserContext * context)107   explicit ApiResourceManager(content::BrowserContext* context)
108       : data_(base::MakeRefCounted<ApiResourceData>()) {
109     extension_registry_observer_.Add(ExtensionRegistry::Get(context));
110     process_manager_observer_.Add(ProcessManager::Get(context));
111   }
112 
~ApiResourceManager()113   virtual ~ApiResourceManager() {
114     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
115     DCHECK(ThreadingTraits::IsThreadInitialized())
116         << "A unit test is using an ApiResourceManager but didn't provide "
117            "the thread message loop needed for that kind of resource. "
118            "Please ensure that the appropriate message loop is operational.";
119 
120     data_->InititateCleanup();
121   }
122 
123   // TODO(lazyboy): Pass unique_ptr<T> instead of T*.
124   // Takes ownership.
Add(T * api_resource)125   int Add(T* api_resource) { return data_->Add(api_resource); }
126 
Remove(const std::string & extension_id,int api_resource_id)127   void Remove(const std::string& extension_id, int api_resource_id) {
128     data_->Remove(extension_id, api_resource_id);
129   }
130 
Get(const std::string & extension_id,int api_resource_id)131   T* Get(const std::string& extension_id, int api_resource_id) {
132     return data_->Get(extension_id, api_resource_id);
133   }
134 
GetResourceIds(const std::string & extension_id)135   std::unordered_set<int>* GetResourceIds(const std::string& extension_id) {
136     return data_->GetResourceIds(extension_id);
137   }
138 
139   // BrowserContextKeyedAPI implementation.
140   static BrowserContextKeyedAPIFactory<ApiResourceManager<T>>*
141   GetFactoryInstance();
142 
143   // Convenience method to get the ApiResourceManager for a profile.
Get(content::BrowserContext * context)144   static ApiResourceManager<T>* Get(content::BrowserContext* context) {
145     return BrowserContextKeyedAPIFactory<ApiResourceManager<T>>::Get(context);
146   }
147 
148   // BrowserContextKeyedAPI implementation.
service_name()149   static const char* service_name() { return T::service_name(); }
150 
151   // Change the resource mapped to this |extension_id| at this
152   // |api_resource_id| to |resource|. Returns true and succeeds unless
153   // |api_resource_id| does not already identify a resource held by
154   // |extension_id|.
Replace(const std::string & extension_id,int api_resource_id,T * resource)155   bool Replace(const std::string& extension_id,
156                int api_resource_id,
157                T* resource) {
158     return data_->Replace(extension_id, api_resource_id, resource);
159   }
160 
161  protected:
162   // ProcessManagerObserver:
OnBackgroundHostClose(const std::string & extension_id)163   void OnBackgroundHostClose(const std::string& extension_id) override {
164     data_->InitiateExtensionSuspendedCleanup(extension_id);
165   }
166 
167   // ExtensionRegistryObserver:
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionReason reason)168   void OnExtensionUnloaded(content::BrowserContext* browser_context,
169                            const Extension* extension,
170                            UnloadedExtensionReason reason) override {
171     data_->InitiateExtensionUnloadedCleanup(extension->id());
172   }
173 
174  private:
175   // TODO(rockot): ApiResourceData could be moved out of ApiResourceManager and
176   // we could avoid maintaining a friends list here.
177   friend class BluetoothAPI;
178   friend class CastChannelAsyncApiFunction;
179   friend class api::BluetoothSocketApiFunction;
180   friend class api::BluetoothSocketEventDispatcher;
181   friend class api::SerialConnectFunction;
182   friend class api::SerialPortManager;
183   friend class api::TCPServerSocketEventDispatcher;
184   friend class api::TCPSocketEventDispatcher;
185   friend class api::UDPSocketEventDispatcher;
186   friend class BrowserContextKeyedAPIFactory<ApiResourceManager<T>>;
187 
188   static const bool kServiceHasOwnInstanceInIncognito = true;
189   static const bool kServiceIsNULLWhileTesting = true;
190 
191   // ApiResourceData class handles resource bookkeeping on a thread
192   // where resource lifetime is handled.
193   class ApiResourceData : public base::RefCountedThreadSafe<ApiResourceData> {
194    public:
195     typedef std::map<int, std::unique_ptr<T>> ApiResourceMap;
196     // Lookup map from extension id's to allocated resource id's.
197     typedef std::map<std::string, std::unordered_set<int>>
198         ExtensionToResourceMap;
199 
ApiResourceData()200     ApiResourceData() : next_id_(1) { sequence_checker_.DetachFromSequence(); }
201 
202     // TODO(lazyboy): Pass unique_ptr<T> instead of T*.
Add(T * api_resource)203     int Add(T* api_resource) {
204       DCHECK(sequence_checker_.CalledOnValidSequence());
205       int id = GenerateId();
206       if (id > 0) {
207         api_resource_map_[id] = base::WrapUnique<T>(api_resource);
208 
209         const std::string& extension_id = api_resource->owner_extension_id();
210         ExtensionToResourceMap::iterator it =
211             extension_resource_map_.find(extension_id);
212         if (it == extension_resource_map_.end()) {
213           it = extension_resource_map_
214                    .insert(
215                        std::make_pair(extension_id, std::unordered_set<int>()))
216                    .first;
217         }
218         it->second.insert(id);
219         return id;
220       }
221       return 0;
222     }
223 
Remove(const std::string & extension_id,int api_resource_id)224     void Remove(const std::string& extension_id, int api_resource_id) {
225       DCHECK(sequence_checker_.CalledOnValidSequence());
226       if (GetOwnedResource(extension_id, api_resource_id)) {
227         ExtensionToResourceMap::iterator it =
228             extension_resource_map_.find(extension_id);
229         it->second.erase(api_resource_id);
230         api_resource_map_.erase(api_resource_id);
231       }
232     }
233 
Get(const std::string & extension_id,int api_resource_id)234     T* Get(const std::string& extension_id, int api_resource_id) {
235       DCHECK(sequence_checker_.CalledOnValidSequence());
236       return GetOwnedResource(extension_id, api_resource_id);
237     }
238 
239     // Change the resource mapped to this |extension_id| at this
240     // |api_resource_id| to |resource|. Returns true and succeeds unless
241     // |api_resource_id| does not already identify a resource held by
242     // |extension_id|.
Replace(const std::string & extension_id,int api_resource_id,T * api_resource)243     bool Replace(const std::string& extension_id,
244                  int api_resource_id,
245                  T* api_resource) {
246       DCHECK(sequence_checker_.CalledOnValidSequence());
247       T* old_resource = api_resource_map_[api_resource_id].get();
248       if (old_resource && extension_id == old_resource->owner_extension_id()) {
249         api_resource_map_[api_resource_id] = base::WrapUnique<T>(api_resource);
250         return true;
251       }
252       return false;
253     }
254 
GetResourceIds(const std::string & extension_id)255     std::unordered_set<int>* GetResourceIds(const std::string& extension_id) {
256       DCHECK(sequence_checker_.CalledOnValidSequence());
257       return GetOwnedResourceIds(extension_id);
258     }
259 
InitiateExtensionUnloadedCleanup(const std::string & extension_id)260     void InitiateExtensionUnloadedCleanup(const std::string& extension_id) {
261       ThreadingTraits::GetSequencedTaskRunner()->PostTask(
262           FROM_HERE,
263           base::BindOnce(
264               &ApiResourceData::CleanupResourcesFromUnloadedExtension, this,
265               extension_id));
266     }
267 
InitiateExtensionSuspendedCleanup(const std::string & extension_id)268     void InitiateExtensionSuspendedCleanup(const std::string& extension_id) {
269       ThreadingTraits::GetSequencedTaskRunner()->PostTask(
270           FROM_HERE,
271           base::BindOnce(
272               &ApiResourceData::CleanupResourcesFromSuspendedExtension, this,
273               extension_id));
274     }
275 
InititateCleanup()276     void InititateCleanup() {
277       ThreadingTraits::GetSequencedTaskRunner()->PostTask(
278           FROM_HERE, base::BindOnce(&ApiResourceData::Cleanup, this));
279     }
280 
281    private:
282     friend class base::RefCountedThreadSafe<ApiResourceData>;
283 
~ApiResourceData()284     virtual ~ApiResourceData() {}
285 
GetOwnedResource(const std::string & extension_id,int api_resource_id)286     T* GetOwnedResource(const std::string& extension_id, int api_resource_id) {
287       const std::unique_ptr<T>& ptr = api_resource_map_[api_resource_id];
288       T* resource = ptr.get();
289       if (resource && extension_id == resource->owner_extension_id())
290         return resource;
291       return NULL;
292     }
293 
GetOwnedResourceIds(const std::string & extension_id)294     std::unordered_set<int>* GetOwnedResourceIds(
295         const std::string& extension_id) {
296       DCHECK(sequence_checker_.CalledOnValidSequence());
297       ExtensionToResourceMap::iterator it =
298           extension_resource_map_.find(extension_id);
299       if (it == extension_resource_map_.end())
300         return NULL;
301       return &(it->second);
302     }
303 
CleanupResourcesFromUnloadedExtension(const std::string & extension_id)304     void CleanupResourcesFromUnloadedExtension(
305         const std::string& extension_id) {
306       CleanupResourcesFromExtension(extension_id, true);
307     }
308 
CleanupResourcesFromSuspendedExtension(const std::string & extension_id)309     void CleanupResourcesFromSuspendedExtension(
310         const std::string& extension_id) {
311       CleanupResourcesFromExtension(extension_id, false);
312     }
313 
CleanupResourcesFromExtension(const std::string & extension_id,bool remove_all)314     void CleanupResourcesFromExtension(const std::string& extension_id,
315                                        bool remove_all) {
316       DCHECK(sequence_checker_.CalledOnValidSequence());
317 
318       ExtensionToResourceMap::iterator it =
319           extension_resource_map_.find(extension_id);
320       if (it == extension_resource_map_.end())
321         return;
322 
323       // Remove all resources, or the non persistent ones only if |remove_all|
324       // is false.
325       std::unordered_set<int>& resource_ids = it->second;
326       for (std::unordered_set<int>::iterator it = resource_ids.begin();
327            it != resource_ids.end();) {
328         bool erase = false;
329         if (remove_all) {
330           erase = true;
331         } else {
332           std::unique_ptr<T>& ptr = api_resource_map_[*it];
333           T* resource = ptr.get();
334           erase = (resource && !resource->IsPersistent());
335         }
336 
337         if (erase) {
338           api_resource_map_.erase(*it);
339           resource_ids.erase(it++);
340         } else {
341           ++it;
342         }
343       }  // end for
344 
345       // Remove extension entry if we removed all its resources.
346       if (resource_ids.size() == 0) {
347         extension_resource_map_.erase(extension_id);
348       }
349     }
350 
Cleanup()351     void Cleanup() {
352       DCHECK(sequence_checker_.CalledOnValidSequence());
353 
354       // Subtle: Move |api_resource_map_| to a temporary and clear that.
355       // |api_resource_map_| will become empty and any destructors called
356       // transitively from clearing |local_api_resource_map| will see empty
357       // |api_resource_map_| instead of trying to access being-destroyed map.
358       ApiResourceMap local_api_resource_map;
359       api_resource_map_.swap(local_api_resource_map);
360       local_api_resource_map.clear();
361       // Do the same as above for |extension_resource_map_|.
362       ExtensionToResourceMap local_extension_resource_map;
363       extension_resource_map_.swap(local_extension_resource_map);
364       local_extension_resource_map.clear();
365     }
366 
GenerateId()367     int GenerateId() { return next_id_++; }
368 
369     int next_id_;
370     ApiResourceMap api_resource_map_;
371     ExtensionToResourceMap extension_resource_map_;
372     base::SequenceChecker sequence_checker_;
373   };
374 
375   content::NotificationRegistrar registrar_;
376   scoped_refptr<ApiResourceData> data_;
377 
378   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
379       extension_registry_observer_{this};
380   ScopedObserver<ProcessManager, ProcessManagerObserver>
381       process_manager_observer_{this};
382 
383   SEQUENCE_CHECKER(sequence_checker_);
384 };
385 
386 template <class T>
387 struct BrowserContextFactoryDependencies<ApiResourceManager<T>> {
388   static void DeclareFactoryDependencies(
389       BrowserContextKeyedAPIFactory<ApiResourceManager<T>>* factory) {
390     factory->DependsOn(
391         ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
392     factory->DependsOn(ExtensionRegistryFactory::GetInstance());
393     factory->DependsOn(ProcessManagerFactory::GetInstance());
394   }
395 };
396 
397 }  // namespace extensions
398 
399 #endif  // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
400