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.
5 #include "chrome/browser/extensions/api/copresence/copresence_api.h"
7 #include "base/lazy_instance.h"
8 #include "base/memory/linked_ptr.h"
9 #include "base/prefs/pref_service.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.h"
13 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
14 #include "chrome/common/channel_info.h"
15 #include "chrome/common/extensions/api/copresence.h"
16 #include "chrome/common/extensions/manifest_handlers/copresence_manifest.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/copresence/copresence_manager_impl.h"
19 #include "components/copresence/proto/data.pb.h"
20 #include "components/copresence/proto/enums.pb.h"
21 #include "components/copresence/proto/rpcs.pb.h"
22 #include "components/pref_registry/pref_registry_syncable.h"
23 #include "content/public/browser/browser_context.h"
24 #include "extensions/browser/event_router.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/manifest_constants.h"
29 using user_prefs::PrefRegistrySyncable
;
31 namespace extensions
{
35 base::LazyInstance
<BrowserContextKeyedAPIFactory
<CopresenceService
>>
36 g_factory
= LAZY_INSTANCE_INITIALIZER
;
38 const char kInvalidOperationsMessage
[] =
39 "Invalid operation in operations array.";
40 const char kShuttingDownMessage
[] = "Shutting down.";
42 const std::string
GetPrefName(bool authenticated
) {
43 return authenticated
? prefs::kCopresenceAuthenticatedDeviceId
44 : prefs::kCopresenceAnonymousDeviceId
;
49 namespace Execute
= api::copresence::Execute
;
50 namespace OnMessagesReceived
= api::copresence::OnMessagesReceived
;
51 namespace OnStatusUpdated
= api::copresence::OnStatusUpdated
;
52 namespace SetApiKey
= api::copresence::SetApiKey
;
53 namespace SetAuthToken
= api::copresence::SetAuthToken
;
57 CopresenceService::CopresenceService(content::BrowserContext
* context
)
58 : is_shutting_down_(false), browser_context_(context
) {}
60 CopresenceService::~CopresenceService() {}
62 void CopresenceService::Shutdown() {
63 is_shutting_down_
= true;
65 whispernet_client_
.reset();
68 copresence::CopresenceManager
* CopresenceService::manager() {
69 if (!manager_
&& !is_shutting_down_
)
70 manager_
.reset(new copresence::CopresenceManagerImpl(this));
71 return manager_
.get();
74 const std::string
CopresenceService::auth_token(const std::string
& app_id
)
76 // This won't be const if we use map[]
77 const auto& key
= auth_tokens_by_app_
.find(app_id
);
78 return key
== auth_tokens_by_app_
.end() ? std::string() : key
->second
;
81 void CopresenceService::set_api_key(const std::string
& app_id
,
82 const std::string
& api_key
) {
83 DCHECK(!app_id
.empty());
84 api_keys_by_app_
[app_id
] = api_key
;
87 void CopresenceService::set_auth_token(const std::string
& app_id
,
88 const std::string
& token
) {
89 DCHECK(!app_id
.empty());
90 auth_tokens_by_app_
[app_id
] = token
;
93 void CopresenceService::set_manager_for_testing(
94 scoped_ptr
<copresence::CopresenceManager
> manager
) {
95 manager_
= manager
.Pass();
98 void CopresenceService::ResetState() {
99 DVLOG(2) << "Deleting copresence state";
100 GetPrefService()->ClearPref(prefs::kCopresenceAuthenticatedDeviceId
);
101 GetPrefService()->ClearPref(prefs::kCopresenceAnonymousDeviceId
);
106 void CopresenceService::RegisterProfilePrefs(PrefRegistrySyncable
* registry
) {
107 registry
->RegisterStringPref(prefs::kCopresenceAuthenticatedDeviceId
,
109 registry
->RegisterStringPref(prefs::kCopresenceAnonymousDeviceId
,
114 BrowserContextKeyedAPIFactory
<CopresenceService
>*
115 CopresenceService::GetFactoryInstance() {
116 return g_factory
.Pointer();
120 // Private functions.
122 void CopresenceService::HandleMessages(
123 const std::string
& /* app_id */,
124 const std::string
& subscription_id
,
125 const std::vector
<copresence::Message
>& messages
) {
126 // TODO(ckehoe): Once the server starts sending back the app ids associated
127 // with subscriptions, use that instead of the apps_by_subs registry.
128 std::string app_id
= apps_by_subscription_id_
[subscription_id
];
130 if (app_id
.empty()) {
131 LOG(ERROR
) << "Skipping message from unrecognized subscription "
136 int message_count
= messages
.size();
137 std::vector
<linked_ptr
<api::copresence::Message
>> api_messages(
140 for (int m
= 0; m
< message_count
; ++m
) {
141 api_messages
[m
].reset(new api::copresence::Message
);
142 api_messages
[m
]->type
= messages
[m
].type().type();
143 api_messages
[m
]->payload
.assign(messages
[m
].payload().begin(),
144 messages
[m
].payload().end());
145 DVLOG(2) << "Dispatching message of type " << api_messages
[m
]->type
<< ":\n"
146 << messages
[m
].payload();
149 // Send the messages to the client app.
150 scoped_ptr
<Event
> event(new Event(
151 events::COPRESENCE_ON_MESSAGES_RECEIVED
, OnMessagesReceived::kEventName
,
152 OnMessagesReceived::Create(subscription_id
, api_messages
),
154 EventRouter::Get(browser_context_
)
155 ->DispatchEventToExtension(app_id
, event
.Pass());
156 DVLOG(2) << "Passed " << api_messages
.size() << " messages to app \""
157 << app_id
<< "\" for subscription \"" << subscription_id
<< "\"";
160 void CopresenceService::HandleStatusUpdate(
161 copresence::CopresenceStatus status
) {
162 DCHECK_EQ(copresence::AUDIO_FAIL
, status
);
163 scoped_ptr
<Event
> event(new Event(
164 events::COPRESENCE_ON_STATUS_UPDATED
, OnStatusUpdated::kEventName
,
165 OnStatusUpdated::Create(api::copresence::STATUS_AUDIOFAILED
),
167 EventRouter::Get(browser_context_
)->BroadcastEvent(event
.Pass());
168 DVLOG(2) << "Sent Audio Failed status update.";
171 net::URLRequestContextGetter
* CopresenceService::GetRequestContext() const {
172 return browser_context_
->GetRequestContext();
175 const std::string
CopresenceService::GetPlatformVersionString() const {
176 return chrome::GetVersionString();
180 CopresenceService::GetAPIKey(const std::string
& app_id
) const {
181 // Check first if the app has set its key via the API.
182 const auto& key
= api_keys_by_app_
.find(app_id
);
183 if (key
!= api_keys_by_app_
.end())
186 // If no key was found, look in the manifest.
187 if (!app_id
.empty()) {
188 const Extension
* extension
= ExtensionRegistry::Get(browser_context_
)
189 ->GetExtensionById(app_id
, ExtensionRegistry::ENABLED
);
190 DCHECK(extension
) << "Invalid extension ID";
191 CopresenceManifestData
* manifest_data
=
192 static_cast<CopresenceManifestData
*>(
193 extension
->GetManifestData(manifest_keys::kCopresence
));
195 return manifest_data
->api_key
;
198 return std::string();
201 audio_modem::WhispernetClient
* CopresenceService::GetWhispernetClient() {
202 if (!whispernet_client_
&& !is_shutting_down_
)
203 whispernet_client_
.reset(new ChromeWhispernetClient(browser_context_
));
204 return whispernet_client_
.get();
207 gcm::GCMDriver
* CopresenceService::GetGCMDriver() {
208 gcm::GCMProfileService
* gcm_service
=
209 gcm::GCMProfileServiceFactory::GetForProfile(browser_context_
);
210 return gcm_service
? gcm_service
->driver() : nullptr;
213 const std::string
CopresenceService::GetDeviceId(bool authenticated
) {
214 std::string id
= GetPrefService()->GetString(GetPrefName(authenticated
));
215 DVLOG(3) << "Retrieved device ID \"" << id
<< "\", "
216 << "authenticated = " << authenticated
;
220 void CopresenceService::SaveDeviceId(bool authenticated
,
221 const std::string
& device_id
) {
222 DVLOG(3) << "Storing device ID \"" << device_id
<< "\", "
223 << "authenticated = " << authenticated
;
224 if (device_id
.empty())
225 GetPrefService()->ClearPref(GetPrefName(authenticated
));
227 GetPrefService()->SetString(GetPrefName(authenticated
), device_id
);
230 PrefService
* CopresenceService::GetPrefService() {
231 return Profile::FromBrowserContext(browser_context_
)->GetPrefs();
236 BrowserContextKeyedAPIFactory
<CopresenceService
>::DeclareFactoryDependencies() {
237 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
240 // CopresenceExecuteFunction implementation.
241 ExtensionFunction::ResponseAction
CopresenceExecuteFunction::Run() {
242 scoped_ptr
<Execute::Params
> params(Execute::Params::Create(*args_
));
243 EXTENSION_FUNCTION_VALIDATE(params
.get());
245 CopresenceService
* service
=
246 CopresenceService::GetFactoryInstance()->Get(browser_context());
248 // This can only happen if we're shutting down. In all other cases, if we
249 // don't have a manager, we'll create one.
250 if (!service
->manager())
251 return RespondNow(Error(kShuttingDownMessage
));
253 // Each execute will correspond to one ReportRequest protocol buffer.
254 copresence::ReportRequest request
;
255 if (!PrepareReportRequestProto(params
->operations
,
257 &service
->apps_by_subscription_id(),
259 return RespondNow(Error(kInvalidOperationsMessage
));
262 service
->manager()->ExecuteReportRequest(
265 service
->auth_token(extension_id()),
266 base::Bind(&CopresenceExecuteFunction::SendResult
, this));
267 return RespondLater();
270 void CopresenceExecuteFunction::SendResult(
271 copresence::CopresenceStatus status
) {
272 api::copresence::ExecuteStatus api_status
=
273 (status
== copresence::SUCCESS
) ? api::copresence::EXECUTE_STATUS_SUCCESS
274 : api::copresence::EXECUTE_STATUS_FAILED
;
275 Respond(ArgumentList(Execute::Results::Create(api_status
)));
278 // CopresenceSetApiKeyFunction implementation.
279 ExtensionFunction::ResponseAction
CopresenceSetApiKeyFunction::Run() {
280 scoped_ptr
<SetApiKey::Params
> params(SetApiKey::Params::Create(*args_
));
281 EXTENSION_FUNCTION_VALIDATE(params
.get());
283 LOG(WARNING
) << "copresence.setApiKey() is deprecated. "
284 << "Put the key in the manifest at copresence.api_key instead.";
286 // The api key may be set to empty, to clear it.
287 CopresenceService::GetFactoryInstance()->Get(browser_context())
288 ->set_api_key(extension_id(), params
->api_key
);
289 return RespondNow(NoArguments());
292 // CopresenceSetAuthTokenFunction implementation
293 ExtensionFunction::ResponseAction
CopresenceSetAuthTokenFunction::Run() {
294 scoped_ptr
<SetAuthToken::Params
> params(SetAuthToken::Params::Create(*args_
));
295 EXTENSION_FUNCTION_VALIDATE(params
.get());
297 // The token may be set to empty, to clear it.
298 CopresenceService::GetFactoryInstance()->Get(browser_context())
299 ->set_auth_token(extension_id(), params
->token
);
300 return RespondNow(NoArguments());
303 } // namespace extensions