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