| OLD | NEW |
| (Empty) |
| 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 #include "chrome/browser/extensions/api/copresence/copresence_api.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/lazy_instance.h" | |
| 10 #include "chrome/browser/copresence/chrome_whispernet_client.h" | |
| 11 #include "chrome/browser/profiles/profile.h" | |
| 12 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h" | |
| 13 #include "chrome/common/channel_info.h" | |
| 14 #include "chrome/common/extensions/api/copresence.h" | |
| 15 #include "chrome/common/extensions/manifest_handlers/copresence_manifest.h" | |
| 16 #include "chrome/common/pref_names.h" | |
| 17 #include "components/copresence/copresence_manager_impl.h" | |
| 18 #include "components/copresence/proto/data.pb.h" | |
| 19 #include "components/copresence/proto/enums.pb.h" | |
| 20 #include "components/copresence/proto/rpcs.pb.h" | |
| 21 #include "components/gcm_driver/gcm_profile_service.h" | |
| 22 #include "components/pref_registry/pref_registry_syncable.h" | |
| 23 #include "components/prefs/pref_service.h" | |
| 24 #include "content/public/browser/browser_context.h" | |
| 25 #include "content/public/browser/storage_partition.h" | |
| 26 #include "extensions/browser/event_router.h" | |
| 27 #include "extensions/browser/extension_registry.h" | |
| 28 #include "extensions/common/extension.h" | |
| 29 #include "extensions/common/manifest_constants.h" | |
| 30 | |
| 31 using user_prefs::PrefRegistrySyncable; | |
| 32 | |
| 33 namespace extensions { | |
| 34 | |
| 35 namespace { | |
| 36 | |
| 37 base::LazyInstance<BrowserContextKeyedAPIFactory<CopresenceService>> | |
| 38 g_factory = LAZY_INSTANCE_INITIALIZER; | |
| 39 | |
| 40 const char kInvalidOperationsMessage[] = | |
| 41 "Invalid operation in operations array."; | |
| 42 const char kShuttingDownMessage[] = "Shutting down."; | |
| 43 | |
| 44 const std::string GetPrefName(bool authenticated) { | |
| 45 return authenticated ? prefs::kCopresenceAuthenticatedDeviceId | |
| 46 : prefs::kCopresenceAnonymousDeviceId; | |
| 47 } | |
| 48 | |
| 49 } // namespace | |
| 50 | |
| 51 namespace Execute = api::copresence::Execute; | |
| 52 namespace OnMessagesReceived = api::copresence::OnMessagesReceived; | |
| 53 namespace OnStatusUpdated = api::copresence::OnStatusUpdated; | |
| 54 namespace SetApiKey = api::copresence::SetApiKey; | |
| 55 namespace SetAuthToken = api::copresence::SetAuthToken; | |
| 56 | |
| 57 // Public functions. | |
| 58 | |
| 59 CopresenceService::CopresenceService(content::BrowserContext* context) | |
| 60 : is_shutting_down_(false), browser_context_(context) {} | |
| 61 | |
| 62 CopresenceService::~CopresenceService() {} | |
| 63 | |
| 64 void CopresenceService::Shutdown() { | |
| 65 is_shutting_down_ = true; | |
| 66 manager_.reset(); | |
| 67 whispernet_client_.reset(); | |
| 68 } | |
| 69 | |
| 70 copresence::CopresenceManager* CopresenceService::manager() { | |
| 71 if (!manager_ && !is_shutting_down_) | |
| 72 manager_.reset(new copresence::CopresenceManagerImpl(this)); | |
| 73 return manager_.get(); | |
| 74 } | |
| 75 | |
| 76 std::string CopresenceService::auth_token(const std::string& app_id) | |
| 77 const { | |
| 78 // This won't be const if we use map[] | |
| 79 const auto& key = auth_tokens_by_app_.find(app_id); | |
| 80 return key == auth_tokens_by_app_.end() ? std::string() : key->second; | |
| 81 } | |
| 82 | |
| 83 void CopresenceService::set_api_key(const std::string& app_id, | |
| 84 const std::string& api_key) { | |
| 85 DCHECK(!app_id.empty()); | |
| 86 api_keys_by_app_[app_id] = api_key; | |
| 87 } | |
| 88 | |
| 89 void CopresenceService::set_auth_token(const std::string& app_id, | |
| 90 const std::string& token) { | |
| 91 DCHECK(!app_id.empty()); | |
| 92 auth_tokens_by_app_[app_id] = token; | |
| 93 } | |
| 94 | |
| 95 void CopresenceService::set_manager_for_testing( | |
| 96 std::unique_ptr<copresence::CopresenceManager> manager) { | |
| 97 manager_ = std::move(manager); | |
| 98 } | |
| 99 | |
| 100 void CopresenceService::ResetState() { | |
| 101 DVLOG(2) << "Deleting copresence state"; | |
| 102 GetPrefService()->ClearPref(prefs::kCopresenceAuthenticatedDeviceId); | |
| 103 GetPrefService()->ClearPref(prefs::kCopresenceAnonymousDeviceId); | |
| 104 manager_ = nullptr; | |
| 105 } | |
| 106 | |
| 107 // static | |
| 108 void CopresenceService::RegisterProfilePrefs(PrefRegistrySyncable* registry) { | |
| 109 registry->RegisterStringPref(prefs::kCopresenceAuthenticatedDeviceId, | |
| 110 std::string()); | |
| 111 registry->RegisterStringPref(prefs::kCopresenceAnonymousDeviceId, | |
| 112 std::string()); | |
| 113 } | |
| 114 | |
| 115 // static | |
| 116 BrowserContextKeyedAPIFactory<CopresenceService>* | |
| 117 CopresenceService::GetFactoryInstance() { | |
| 118 return g_factory.Pointer(); | |
| 119 } | |
| 120 | |
| 121 | |
| 122 // Private functions. | |
| 123 | |
| 124 void CopresenceService::HandleMessages( | |
| 125 const std::string& /* app_id */, | |
| 126 const std::string& subscription_id, | |
| 127 const std::vector<copresence::Message>& messages) { | |
| 128 // TODO(ckehoe): Once the server starts sending back the app ids associated | |
| 129 // with subscriptions, use that instead of the apps_by_subs registry. | |
| 130 std::string app_id = apps_by_subscription_id_[subscription_id]; | |
| 131 | |
| 132 if (app_id.empty()) { | |
| 133 LOG(ERROR) << "Skipping message from unrecognized subscription " | |
| 134 << subscription_id; | |
| 135 return; | |
| 136 } | |
| 137 | |
| 138 int message_count = messages.size(); | |
| 139 std::vector<api::copresence::Message> api_messages(message_count); | |
| 140 | |
| 141 for (const copresence::Message& message : messages) { | |
| 142 api::copresence::Message api_message; | |
| 143 api_message.type = message.type().type(); | |
| 144 api_message.payload.assign(message.payload().begin(), | |
| 145 message.payload().end()); | |
| 146 api_messages.push_back(std::move(api_message)); | |
| 147 DVLOG(2) << "Dispatching message of type " << api_message.type << ":\n" | |
| 148 << message.payload(); | |
| 149 } | |
| 150 | |
| 151 // Send the messages to the client app. | |
| 152 std::unique_ptr<Event> event(new Event( | |
| 153 events::COPRESENCE_ON_MESSAGES_RECEIVED, OnMessagesReceived::kEventName, | |
| 154 OnMessagesReceived::Create(subscription_id, api_messages), | |
| 155 browser_context_)); | |
| 156 EventRouter::Get(browser_context_) | |
| 157 ->DispatchEventToExtension(app_id, std::move(event)); | |
| 158 DVLOG(2) << "Passed " << api_messages.size() << " messages to app \"" | |
| 159 << app_id << "\" for subscription \"" << subscription_id << "\""; | |
| 160 } | |
| 161 | |
| 162 void CopresenceService::HandleStatusUpdate( | |
| 163 copresence::CopresenceStatus status) { | |
| 164 DCHECK_EQ(copresence::AUDIO_FAIL, status); | |
| 165 std::unique_ptr<Event> event(new Event( | |
| 166 events::COPRESENCE_ON_STATUS_UPDATED, OnStatusUpdated::kEventName, | |
| 167 OnStatusUpdated::Create(api::copresence::STATUS_AUDIOFAILED), | |
| 168 browser_context_)); | |
| 169 EventRouter::Get(browser_context_)->BroadcastEvent(std::move(event)); | |
| 170 DVLOG(2) << "Sent Audio Failed status update."; | |
| 171 } | |
| 172 | |
| 173 net::URLRequestContextGetter* CopresenceService::GetRequestContext() const { | |
| 174 return content::BrowserContext::GetDefaultStoragePartition(browser_context_)-> | |
| 175 GetURLRequestContext(); | |
| 176 } | |
| 177 | |
| 178 std::string CopresenceService::GetPlatformVersionString() const { | |
| 179 return chrome::GetVersionString(); | |
| 180 } | |
| 181 | |
| 182 std::string | |
| 183 CopresenceService::GetAPIKey(const std::string& app_id) const { | |
| 184 // Check first if the app has set its key via the API. | |
| 185 const auto& key = api_keys_by_app_.find(app_id); | |
| 186 if (key != api_keys_by_app_.end()) | |
| 187 return key->second; | |
| 188 | |
| 189 // If no key was found, look in the manifest. | |
| 190 if (!app_id.empty()) { | |
| 191 const Extension* extension = ExtensionRegistry::Get(browser_context_) | |
| 192 ->GetExtensionById(app_id, ExtensionRegistry::ENABLED); | |
| 193 DCHECK(extension) << "Invalid extension ID"; | |
| 194 CopresenceManifestData* manifest_data = | |
| 195 static_cast<CopresenceManifestData*>( | |
| 196 extension->GetManifestData(manifest_keys::kCopresence)); | |
| 197 if (manifest_data) | |
| 198 return manifest_data->api_key; | |
| 199 } | |
| 200 | |
| 201 return std::string(); | |
| 202 } | |
| 203 | |
| 204 audio_modem::WhispernetClient* CopresenceService::GetWhispernetClient() { | |
| 205 if (!whispernet_client_ && !is_shutting_down_) | |
| 206 whispernet_client_.reset(new ChromeWhispernetClient(browser_context_)); | |
| 207 return whispernet_client_.get(); | |
| 208 } | |
| 209 | |
| 210 gcm::GCMDriver* CopresenceService::GetGCMDriver() { | |
| 211 gcm::GCMProfileService* gcm_service = | |
| 212 gcm::GCMProfileServiceFactory::GetForProfile(browser_context_); | |
| 213 return gcm_service ? gcm_service->driver() : nullptr; | |
| 214 } | |
| 215 | |
| 216 std::string CopresenceService::GetDeviceId(bool authenticated) { | |
| 217 std::string id = GetPrefService()->GetString(GetPrefName(authenticated)); | |
| 218 DVLOG(3) << "Retrieved device ID \"" << id << "\", " | |
| 219 << "authenticated = " << authenticated; | |
| 220 return id; | |
| 221 } | |
| 222 | |
| 223 void CopresenceService::SaveDeviceId(bool authenticated, | |
| 224 const std::string& device_id) { | |
| 225 DVLOG(3) << "Storing device ID \"" << device_id << "\", " | |
| 226 << "authenticated = " << authenticated; | |
| 227 if (device_id.empty()) | |
| 228 GetPrefService()->ClearPref(GetPrefName(authenticated)); | |
| 229 else | |
| 230 GetPrefService()->SetString(GetPrefName(authenticated), device_id); | |
| 231 } | |
| 232 | |
| 233 PrefService* CopresenceService::GetPrefService() { | |
| 234 return Profile::FromBrowserContext(browser_context_)->GetPrefs(); | |
| 235 } | |
| 236 | |
| 237 template <> | |
| 238 void | |
| 239 BrowserContextKeyedAPIFactory<CopresenceService>::DeclareFactoryDependencies() { | |
| 240 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); | |
| 241 } | |
| 242 | |
| 243 // CopresenceExecuteFunction implementation. | |
| 244 ExtensionFunction::ResponseAction CopresenceExecuteFunction::Run() { | |
| 245 std::unique_ptr<Execute::Params> params(Execute::Params::Create(*args_)); | |
| 246 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
| 247 | |
| 248 CopresenceService* service = | |
| 249 CopresenceService::GetFactoryInstance()->Get(browser_context()); | |
| 250 | |
| 251 // This can only happen if we're shutting down. In all other cases, if we | |
| 252 // don't have a manager, we'll create one. | |
| 253 if (!service->manager()) | |
| 254 return RespondNow(Error(kShuttingDownMessage)); | |
| 255 | |
| 256 // Each execute will correspond to one ReportRequest protocol buffer. | |
| 257 copresence::ReportRequest request; | |
| 258 if (!PrepareReportRequestProto(params->operations, | |
| 259 extension_id(), | |
| 260 &service->apps_by_subscription_id(), | |
| 261 &request)) { | |
| 262 return RespondNow(Error(kInvalidOperationsMessage)); | |
| 263 } | |
| 264 | |
| 265 service->manager()->ExecuteReportRequest( | |
| 266 request, | |
| 267 extension_id(), | |
| 268 service->auth_token(extension_id()), | |
| 269 base::Bind(&CopresenceExecuteFunction::SendResult, this)); | |
| 270 return RespondLater(); | |
| 271 } | |
| 272 | |
| 273 void CopresenceExecuteFunction::SendResult( | |
| 274 copresence::CopresenceStatus status) { | |
| 275 api::copresence::ExecuteStatus api_status = | |
| 276 (status == copresence::SUCCESS) ? api::copresence::EXECUTE_STATUS_SUCCESS | |
| 277 : api::copresence::EXECUTE_STATUS_FAILED; | |
| 278 Respond(ArgumentList(Execute::Results::Create(api_status))); | |
| 279 } | |
| 280 | |
| 281 // CopresenceSetApiKeyFunction implementation. | |
| 282 ExtensionFunction::ResponseAction CopresenceSetApiKeyFunction::Run() { | |
| 283 std::unique_ptr<SetApiKey::Params> params(SetApiKey::Params::Create(*args_)); | |
| 284 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
| 285 | |
| 286 LOG(WARNING) << "copresence.setApiKey() is deprecated. " | |
| 287 << "Put the key in the manifest at copresence.api_key instead."; | |
| 288 | |
| 289 // The api key may be set to empty, to clear it. | |
| 290 CopresenceService::GetFactoryInstance()->Get(browser_context()) | |
| 291 ->set_api_key(extension_id(), params->api_key); | |
| 292 return RespondNow(NoArguments()); | |
| 293 } | |
| 294 | |
| 295 // CopresenceSetAuthTokenFunction implementation | |
| 296 ExtensionFunction::ResponseAction CopresenceSetAuthTokenFunction::Run() { | |
| 297 std::unique_ptr<SetAuthToken::Params> params( | |
| 298 SetAuthToken::Params::Create(*args_)); | |
| 299 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
| 300 | |
| 301 // The token may be set to empty, to clear it. | |
| 302 CopresenceService::GetFactoryInstance()->Get(browser_context()) | |
| 303 ->set_auth_token(extension_id(), params->token); | |
| 304 return RespondNow(NoArguments()); | |
| 305 } | |
| 306 | |
| 307 } // namespace extensions | |
| OLD | NEW |