Commit 07b355ac authored by johnme's avatar johnme Committed by Commit bot

Push API: Fix unsubscribing from GCM on Android

We were calling Android GCM's unregister API, which clears all of an app's subscriptions (all sender ids and subtypes). Since from Android's point of view, the app is Chrome, if any webapp unsubscribed from push messaging, all webapps would be unsubscribed!

Instead now we call the unsubscribe API, which only unsubscribes a specific (sender_id,subtype) pair (where the subtype corresponds to GCMDriver's app_id). So now if a Service Worker calls pushSubscription.unsubscribe(), it'll only unsubscribe that SW's push subscription.

In order for this to work, we now also call the corresponding subscribe API (accepting a single sender_id) instead of register.

GoogleCloudMessagingV2.java is still temporary code, that will be removed once we switch to the Google Play Services client library.

BUG=457374

Review URL: https://codereview.chromium.org/914693002

Cr-Commit-Position: refs/heads/master@{#317062}
parent c1b101e5
...@@ -89,15 +89,20 @@ class InfoBarResponder : public infobars::InfoBarManager::Observer { ...@@ -89,15 +89,20 @@ class InfoBarResponder : public infobars::InfoBarManager::Observer {
// FakeGCMProfileService::UnregisterCallback. // FakeGCMProfileService::UnregisterCallback.
class UnregistrationCallback { class UnregistrationCallback {
public: public:
UnregistrationCallback() : done_(false) {} UnregistrationCallback() : done_(false), waiting_(false) {}
void Run(const std::string& app_id) { void Run(const std::string& app_id) {
app_id_ = app_id; app_id_ = app_id;
done_ = true; done_ = true;
if (waiting_)
base::MessageLoop::current()->Quit(); base::MessageLoop::current()->Quit();
} }
void WaitUntilSatisfied() { void WaitUntilSatisfied() {
if (done_)
return;
waiting_ = true;
while (!done_) while (!done_)
content::RunMessageLoop(); content::RunMessageLoop();
} }
...@@ -108,6 +113,7 @@ class UnregistrationCallback { ...@@ -108,6 +113,7 @@ class UnregistrationCallback {
private: private:
bool done_; bool done_;
bool waiting_;
std::string app_id_; std::string app_id_;
}; };
...@@ -458,6 +464,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) { ...@@ -458,6 +464,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) {
ASSERT_EQ("true - is controlled", script_result); ASSERT_EQ("true - is controlled", script_result);
GCMClient::IncomingMessage message; GCMClient::IncomingMessage message;
message.sender_id = "1234567890";
message.data["data"] = "testdata"; message.data["data"] = "testdata";
push_service()->OnMessage(app_id.app_id_guid(), message); push_service()->OnMessage(app_id.app_id_guid(), message);
ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result)); ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
...@@ -495,48 +502,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) { ...@@ -495,48 +502,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) {
base::Unretained(&callback))); base::Unretained(&callback)));
GCMClient::IncomingMessage message; GCMClient::IncomingMessage message;
message.data["data"] = "testdata"; message.sender_id = "1234567890";
push_service()->OnMessage(app_id.app_id_guid(), message);
callback.WaitUntilSatisfied();
EXPECT_EQ(app_id.app_id_guid(), callback.app_id());
// No push data should have been received.
ASSERT_TRUE(RunScript("resultQueue.popImmediately()", &script_result));
EXPECT_EQ("null", script_result);
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoPermission) {
if (!IsPushSupported())
return;
std::string script_result;
TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL);
EXPECT_EQ(app_id.app_id_guid(), gcm_service()->last_registered_app_id());
EXPECT_EQ("1234567890", gcm_service()->last_registered_sender_ids()[0]);
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("false - is not controlled", script_result);
LoadTestPage(); // Reload to become controlled.
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("true - is controlled", script_result);
// Revoke Push permission.
browser()->profile()->GetHostContentSettingsMap()->
ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PUSH_MESSAGING);
// When the push service will receive its next message, given that there is no
// SW available, it should unregister |app_id|.
UnregistrationCallback callback;
gcm_service()->SetUnregisterCallback(base::Bind(&UnregistrationCallback::Run,
base::Unretained(&callback)));
GCMClient::IncomingMessage message;
message.data["data"] = "testdata"; message.data["data"] = "testdata";
push_service()->OnMessage(app_id.app_id_guid(), message); push_service()->OnMessage(app_id.app_id_guid(), message);
...@@ -581,6 +547,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, ...@@ -581,6 +547,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
// If the site is visible in an active tab, we should not force a notification // If the site is visible in an active tab, we should not force a notification
// to be shown. Try it twice, since we allow one mistake per 10 push events. // to be shown. Try it twice, since we allow one mistake per 10 push events.
GCMClient::IncomingMessage message; GCMClient::IncomingMessage message;
message.sender_id = "1234567890";
for (int n = 0; n < 2; n++) { for (int n = 0; n < 2; n++) {
message.data["data"] = "testdata"; message.data["data"] = "testdata";
push_service()->OnMessage(app_id.app_id_guid(), message); push_service()->OnMessage(app_id.app_id_guid(), message);
...@@ -671,6 +638,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, ...@@ -671,6 +638,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
base::Bind(&NotificationAddedCallback::Run, base::Unretained(&callback))); base::Bind(&NotificationAddedCallback::Run, base::Unretained(&callback)));
GCMClient::IncomingMessage message; GCMClient::IncomingMessage message;
message.sender_id = "1234567890";
message.data["data"] = "shownotification-without-waituntil"; message.data["data"] = "shownotification-without-waituntil";
push_service()->OnMessage(app_id.app_id_guid(), message); push_service()->OnMessage(app_id.app_id_guid(), message);
ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents)); ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
......
...@@ -179,6 +179,8 @@ void PushMessagingServiceImpl::ShutdownHandler() { ...@@ -179,6 +179,8 @@ void PushMessagingServiceImpl::ShutdownHandler() {
// TODO(johnme): Do any necessary cleanup. // TODO(johnme): Do any necessary cleanup.
} }
// OnMessage methods -----------------------------------------------------------
void PushMessagingServiceImpl::OnMessage( void PushMessagingServiceImpl::OnMessage(
const std::string& app_id, const std::string& app_id,
const GCMClient::IncomingMessage& message) { const GCMClient::IncomingMessage& message) {
...@@ -234,42 +236,6 @@ void PushMessagingServiceImpl::OnMessage( ...@@ -234,42 +236,6 @@ void PushMessagingServiceImpl::OnMessage(
application_id.service_worker_registration_id(), message)); application_id.service_worker_registration_id(), message));
} }
void PushMessagingServiceImpl::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
std::string resource_identifier) {
if (content_type != CONTENT_SETTINGS_TYPE_PUSH_MESSAGING &&
content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
return;
}
for (const auto& id : PushMessagingApplicationId::GetAll(profile_)) {
// If |primary_pattern| is not valid, we should always check for a
// permission change because it can happen for example when the entire
// Push or Notifications permissions are cleared.
// Otherwise, the permission should be checked if the pattern matches the
// origin.
if (primary_pattern.IsValid() && !primary_pattern.Matches(id.origin()))
continue;
if (HasPermission(id.origin()))
continue;
// Unregister the PushMessagingApplicationId with the push service.
Unregister(id.app_id_guid(), true /* retry */, UnregisterCallback());
// Clear the associated service worker push registration id.
PushMessagingService::ClearPushRegistrationID(
profile_, id.origin(), id.service_worker_registration_id());
}
}
void PushMessagingServiceImpl::SetProfileForTesting(Profile* profile) {
profile_ = profile;
profile_->GetHostContentSettingsMap()->AddObserver(this);
}
void PushMessagingServiceImpl::DeliverMessageCallback( void PushMessagingServiceImpl::DeliverMessageCallback(
const std::string& app_id_guid, const std::string& app_id_guid,
const GURL& requesting_origin, const GURL& requesting_origin,
...@@ -294,7 +260,8 @@ void PushMessagingServiceImpl::DeliverMessageCallback( ...@@ -294,7 +260,8 @@ void PushMessagingServiceImpl::DeliverMessageCallback(
case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID: case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID:
case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED: case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED:
case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER: case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER:
Unregister(app_id_guid, true /*retry_on_failure*/, UnregisterCallback()); Unregister(app_id_guid, message.sender_id, true /* retry_on_failure */,
UnregisterCallback());
break; break;
} }
} }
...@@ -451,6 +418,8 @@ void PushMessagingServiceImpl::DidGetNotificationsShown( ...@@ -451,6 +418,8 @@ void PushMessagingServiceImpl::DidGetNotificationsShown(
} }
} }
// Other GCMAppHandler methods -------------------------------------------------
void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) { void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
// TODO(mvanouwerkerk): Fire push error event on the Service Worker // TODO(mvanouwerkerk): Fire push error event on the Service Worker
// corresponding to app_id. // corresponding to app_id.
...@@ -468,10 +437,14 @@ void PushMessagingServiceImpl::OnSendAcknowledged( ...@@ -468,10 +437,14 @@ void PushMessagingServiceImpl::OnSendAcknowledged(
NOTREACHED() << "The Push API shouldn't have sent messages upstream"; NOTREACHED() << "The Push API shouldn't have sent messages upstream";
} }
// GetPushEndpoint method ------------------------------------------------------
GURL PushMessagingServiceImpl::GetPushEndpoint() { GURL PushMessagingServiceImpl::GetPushEndpoint() {
return GURL(std::string(kPushMessagingEndpoint)); return GURL(std::string(kPushMessagingEndpoint));
} }
// Register and GetPermissionStatus methods ------------------------------------
void PushMessagingServiceImpl::RegisterFromDocument( void PushMessagingServiceImpl::RegisterFromDocument(
const GURL& requesting_origin, const GURL& requesting_origin,
int64 service_worker_registration_id, int64 service_worker_registration_id,
...@@ -636,9 +609,12 @@ void PushMessagingServiceImpl::DidRequestPermission( ...@@ -636,9 +609,12 @@ void PushMessagingServiceImpl::DidRequestPermission(
application_id, register_callback)); application_id, register_callback));
} }
// Unregister methods ----------------------------------------------------------
void PushMessagingServiceImpl::Unregister( void PushMessagingServiceImpl::Unregister(
const GURL& requesting_origin, const GURL& requesting_origin,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const std::string& sender_id,
bool retry_on_failure, bool retry_on_failure,
const content::PushMessagingService::UnregisterCallback& callback) { const content::PushMessagingService::UnregisterCallback& callback) {
DCHECK(gcm_profile_service_->driver()); DCHECK(gcm_profile_service_->driver());
...@@ -653,11 +629,13 @@ void PushMessagingServiceImpl::Unregister( ...@@ -653,11 +629,13 @@ void PushMessagingServiceImpl::Unregister(
return; return;
} }
Unregister(application_id.app_id_guid(), retry_on_failure, callback); Unregister(application_id.app_id_guid(), sender_id, retry_on_failure,
callback);
} }
void PushMessagingServiceImpl::Unregister( void PushMessagingServiceImpl::Unregister(
const std::string& app_id_guid, const std::string& app_id_guid,
const std::string& sender_id,
bool retry_on_failure, bool retry_on_failure,
const content::PushMessagingService::UnregisterCallback& callback) { const content::PushMessagingService::UnregisterCallback& callback) {
DCHECK(gcm_profile_service_->driver()); DCHECK(gcm_profile_service_->driver());
...@@ -673,11 +651,17 @@ void PushMessagingServiceImpl::Unregister( ...@@ -673,11 +651,17 @@ void PushMessagingServiceImpl::Unregister(
application_id.DeleteFromDisk(profile_); application_id.DeleteFromDisk(profile_);
} }
gcm_profile_service_->driver()->Unregister( const auto& unregister_callback =
app_id_guid,
base::Bind(&PushMessagingServiceImpl::DidUnregister, base::Bind(&PushMessagingServiceImpl::DidUnregister,
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr(),
app_id_guid, retry_on_failure, callback)); app_id_guid, retry_on_failure, callback);
#if defined(OS_ANDROID)
// On Android the backend is different, and requires the original sender_id.
gcm_profile_service_->driver()->UnregisterWithSenderId(app_id_guid, sender_id,
unregister_callback);
#else
gcm_profile_service_->driver()->Unregister(app_id_guid, unregister_callback);
#endif
} }
void PushMessagingServiceImpl::DidUnregister( void PushMessagingServiceImpl::DidUnregister(
...@@ -729,6 +713,52 @@ void PushMessagingServiceImpl::DidUnregister( ...@@ -729,6 +713,52 @@ void PushMessagingServiceImpl::DidUnregister(
} }
} }
// OnContentSettingChanged methods ---------------------------------------------
void PushMessagingServiceImpl::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
std::string resource_identifier) {
if (content_type != CONTENT_SETTINGS_TYPE_PUSH_MESSAGING &&
content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
return;
}
for (const auto& id : PushMessagingApplicationId::GetAll(profile_)) {
// If |primary_pattern| is not valid, we should always check for a
// permission change because it can happen for example when the entire
// Push or Notifications permissions are cleared.
// Otherwise, the permission should be checked if the pattern matches the
// origin.
if (primary_pattern.IsValid() && !primary_pattern.Matches(id.origin()))
continue;
if (HasPermission(id.origin()))
continue;
PushMessagingService::GetSenderId(
profile_, id.origin(), id.service_worker_registration_id(),
base::Bind(
&PushMessagingServiceImpl::UnregisterBecausePermissionRevoked,
weak_factory_.GetWeakPtr(), id));
}
}
void PushMessagingServiceImpl::UnregisterBecausePermissionRevoked(
const PushMessagingApplicationId& id,
const std::string& sender_id, bool success, bool not_found) {
// Unregister the PushMessagingApplicationId with the push service.
Unregister(id.app_id_guid(), sender_id, true /* retry_on_failure */,
UnregisterCallback());
// Clear the associated service worker push registration id.
PushMessagingService::ClearPushRegistrationID(
profile_, id.origin(), id.service_worker_registration_id());
}
// Helper methods --------------------------------------------------------------
bool PushMessagingServiceImpl::HasPermission(const GURL& origin) { bool PushMessagingServiceImpl::HasPermission(const GURL& origin) {
gcm::PushMessagingPermissionContext* permission_context = gcm::PushMessagingPermissionContext* permission_context =
gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_); gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_);
...@@ -738,4 +768,9 @@ bool PushMessagingServiceImpl::HasPermission(const GURL& origin) { ...@@ -738,4 +768,9 @@ bool PushMessagingServiceImpl::HasPermission(const GURL& origin) {
CONTENT_SETTING_ALLOW; CONTENT_SETTING_ALLOW;
} }
void PushMessagingServiceImpl::SetProfileForTesting(Profile* profile) {
profile_ = profile;
profile_->GetHostContentSettingsMap()->AddObserver(this);
}
} // namespace gcm } // namespace gcm
...@@ -69,6 +69,7 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -69,6 +69,7 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
void Unregister( void Unregister(
const GURL& requesting_origin, const GURL& requesting_origin,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const std::string& sender_id,
bool retry_on_failure, bool retry_on_failure,
const content::PushMessagingService::UnregisterCallback&) override; const content::PushMessagingService::UnregisterCallback&) override;
blink::WebPushPermissionStatus GetPermissionStatus( blink::WebPushPermissionStatus GetPermissionStatus(
...@@ -88,6 +89,8 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -88,6 +89,8 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
void IncreasePushRegistrationCount(int add, bool is_pending); void IncreasePushRegistrationCount(int add, bool is_pending);
void DecreasePushRegistrationCount(int subtract, bool was_pending); void DecreasePushRegistrationCount(int subtract, bool was_pending);
// OnMessage methods ---------------------------------------------------------
void DeliverMessageCallback(const std::string& app_id_guid, void DeliverMessageCallback(const std::string& app_id_guid,
const GURL& requesting_origin, const GURL& requesting_origin,
int64 service_worker_registration_id, int64 service_worker_registration_id,
...@@ -109,6 +112,8 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -109,6 +112,8 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
bool success, bool success,
bool not_found); bool not_found);
// Register methods ----------------------------------------------------------
void RegisterEnd( void RegisterEnd(
const content::PushMessagingService::RegisterCallback& callback, const content::PushMessagingService::RegisterCallback& callback,
const std::string& registration_id, const std::string& registration_id,
...@@ -126,7 +131,10 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -126,7 +131,10 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
const content::PushMessagingService::RegisterCallback& callback, const content::PushMessagingService::RegisterCallback& callback,
bool allow); bool allow);
// Unregister methods --------------------------------------------------------
void Unregister(const std::string& app_id_guid, void Unregister(const std::string& app_id_guid,
const std::string& sender_id,
bool retry_on_failure, bool retry_on_failure,
const content::PushMessagingService::UnregisterCallback&); const content::PushMessagingService::UnregisterCallback&);
...@@ -135,7 +143,15 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -135,7 +143,15 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
const content::PushMessagingService::UnregisterCallback&, const content::PushMessagingService::UnregisterCallback&,
GCMClient::Result result); GCMClient::Result result);
// Helper method that checks if a given origin is allowed to use Push. // OnContentSettingChanged methods -------------------------------------------
void UnregisterBecausePermissionRevoked(const PushMessagingApplicationId& id,
const std::string& sender_id,
bool success, bool not_found);
// Helper methods ------------------------------------------------------------
// Checks if a given origin is allowed to use Push.
bool HasPermission(const GURL& origin); bool HasPermission(const GURL& origin);
GCMProfileService* gcm_profile_service_; // It owns us. GCMProfileService* gcm_profile_service_; // It owns us.
......
...@@ -37,10 +37,12 @@ public class GCMDriver { ...@@ -37,10 +37,12 @@ public class GCMDriver {
private long mNativeGCMDriverAndroid; private long mNativeGCMDriverAndroid;
private final Context mContext; private final Context mContext;
private final GoogleCloudMessagingV2 mGcm;
private GCMDriver(long nativeGCMDriverAndroid, Context context) { private GCMDriver(long nativeGCMDriverAndroid, Context context) {
mNativeGCMDriverAndroid = nativeGCMDriverAndroid; mNativeGCMDriverAndroid = nativeGCMDriverAndroid;
mContext = context; mContext = context;
mGcm = new GoogleCloudMessagingV2(context);
} }
/** /**
...@@ -72,18 +74,16 @@ public class GCMDriver { ...@@ -72,18 +74,16 @@ public class GCMDriver {
} }
@CalledByNative @CalledByNative
private void register(final String appId, final String[] senderIds) { private void register(final String appId, final String senderId) {
new AsyncTask<Void, Void, String>() { new AsyncTask<Void, Void, String>() {
@Override @Override
protected String doInBackground(Void... voids) { protected String doInBackground(Void... voids) {
// TODO(johnme): Should check if GMS is installed on the device first. Ditto below.
try { try {
String subtype = appId; String subtype = appId;
GoogleCloudMessagingV2 gcm = new GoogleCloudMessagingV2(mContext); String registrationId = mGcm.subscribe(senderId, subtype, null);
String registrationId = gcm.register(subtype, senderIds);
return registrationId; return registrationId;
} catch (IOException ex) { } catch (IOException ex) {
Log.w(TAG, "GCMv2 registration failed for " + appId, ex); Log.w(TAG, "GCM subscription failed for " + appId + ", " + senderId, ex);
return ""; return "";
} }
} }
...@@ -96,17 +96,16 @@ public class GCMDriver { ...@@ -96,17 +96,16 @@ public class GCMDriver {
} }
@CalledByNative @CalledByNative
private void unregister(final String appId) { private void unregister(final String appId, final String senderId) {
new AsyncTask<Void, Void, Boolean>() { new AsyncTask<Void, Void, Boolean>() {
@Override @Override
protected Boolean doInBackground(Void... voids) { protected Boolean doInBackground(Void... voids) {
try { try {
String subtype = appId; String subtype = appId;
GoogleCloudMessagingV2 gcm = new GoogleCloudMessagingV2(mContext); mGcm.unsubscribe(senderId, subtype, null);
gcm.unregister(subtype);
return true; return true;
} catch (IOException ex) { } catch (IOException ex) {
Log.w(TAG, "GCMv2 unregistration failed for " + appId, ex); Log.w(TAG, "GCM unsubscription failed for " + appId + ", " + senderId, ex);
return false; return false;
} }
} }
......
...@@ -7,6 +7,9 @@ package org.chromium.components.gcm_driver; ...@@ -7,6 +7,9 @@ package org.chromium.components.gcm_driver;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
...@@ -19,29 +22,25 @@ import java.util.concurrent.TimeUnit; ...@@ -19,29 +22,25 @@ import java.util.concurrent.TimeUnit;
/** /**
* Temporary code for sending subtypes when (un)registering with GCM. * Temporary code for sending subtypes when (un)subscribing with GCM.
* Subtypes are experimental and may change without notice! * Subtypes are experimental and may change without notice!
* TODO(johnme): Remove this file, once we switch to the GMS client library. * TODO(johnme): Remove this file, once we switch to the GMS client library.
*/ */
public class GoogleCloudMessagingV2 { public class GoogleCloudMessagingV2 {
// Inlined com.google.android.gms.common.GooglePlayServicesUtil.GOOGLE_PLAY_SERVICES_PACKAGE
// since this class mustn't depend on the GMS client library.
private static final String GOOGLE_PLAY_SERVICES_PACKAGE = "com.google.android.gms"; private static final String GOOGLE_PLAY_SERVICES_PACKAGE = "com.google.android.gms";
private static final long REGISTER_TIMEOUT = 5000; private static final long REGISTER_TIMEOUT = 5000;
private static final String ACTION_C2DM_REGISTER = private static final String ACTION_C2DM_REGISTER = "com.google.android.c2dm.intent.REGISTER";
"com.google.android.c2dm.intent.REGISTER";
private static final String ACTION_C2DM_UNREGISTER =
"com.google.android.c2dm.intent.UNREGISTER";
private static final String C2DM_EXTRA_ERROR = "error"; private static final String C2DM_EXTRA_ERROR = "error";
private static final String INTENT_PARAM_APP = "app"; private static final String INTENT_PARAM_APP = "app";
private static final String ERROR_MAIN_THREAD = "MAIN_THREAD"; private static final String ERROR_MAIN_THREAD = "MAIN_THREAD";
private static final String ERROR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE"; private static final String ERROR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
private static final String EXTRA_DELETE = "delete";
private static final String EXTRA_REGISTRATION_ID = "registration_id"; private static final String EXTRA_REGISTRATION_ID = "registration_id";
private static final String EXTRA_UNREGISTERED = "unregistered";
private static final String EXTRA_SENDER = "sender"; private static final String EXTRA_SENDER = "sender";
private static final String EXTRA_MESSENGER = "google.messenger"; private static final String EXTRA_MESSENGER = "google.messenger";
private static final String EXTRA_SUBTYPE = "subtype"; private static final String EXTRA_SUBTYPE = "subtype";
private static final String EXTRA_SUBSCRIPTION = "subscription";
private Context mContext; private Context mContext;
private PendingIntent mAppPendingIntent = null; private PendingIntent mAppPendingIntent = null;
...@@ -51,80 +50,132 @@ public class GoogleCloudMessagingV2 { ...@@ -51,80 +50,132 @@ public class GoogleCloudMessagingV2 {
mContext = context; mContext = context;
} }
public String subscribe(String source, String subtype, Bundle data) throws IOException {
if (data == null) {
data = new Bundle();
}
data.putString(EXTRA_SUBTYPE, subtype);
Bundle result = subscribe(source, data);
return result.getString(EXTRA_REGISTRATION_ID);
}
public void unsubscribe(String source, String subtype, Bundle data) throws IOException {
if (data == null) {
data = new Bundle();
}
data.putString(EXTRA_SUBTYPE, subtype);
unsubscribe(source, data);
return;
}
/** /**
* Register the application for GCM and return the registration ID. You must call this once, * Subscribe to receive GCM messages from a specific source.
* when your application is installed, and send the returned registration ID to the server.
* <p>
* This is a blocking call&mdash;you shouldn't call it from the UI thread.
* <p> * <p>
* Repeated calls to this method will return the original registration ID. * Source Types:
* <p> * <ul>
* If you want to modify the list of senders, you must call {@code unregister()} first. * <li>Sender ID - if you have multiple senders you can call this method
* for each additional sender. Each sender can use the corresponding
* {@link #REGISTRATION_ID} returned in the bundle to send messages
* from the server.</li>
* <li>Cloud Pub/Sub topic - You can subscribe to a topic and receive
* notifications from the owner of that topic, when something changes.
* For more information see
* <a href="https://cloud.google.com/pubsub">Cloud Pub/Sub</a>.</li>
* </ul>
* This function is blocking and should not be called on the main thread.
*
* @param source of the desired notifications.
* @param data (optional) additional information.
* @return Bundle containing subscription information including {@link #REGISTRATION_ID}
* @throws IOException if the request fails.
*/
public Bundle subscribe(String source, Bundle data) throws IOException {
if (data == null) {
data = new Bundle();
}
// Expected by older versions of GMS and servlet
data.putString(EXTRA_SENDER, source);
// New name of the sender parameter
data.putString(EXTRA_SUBSCRIPTION, source);
// DB buster for older versions of GCM.
if (data.getString(EXTRA_SUBTYPE) == null) {
data.putString(EXTRA_SUBTYPE, source);
}
Intent resultIntent = registerRpc(data);
getExtraOrThrow(resultIntent, EXTRA_REGISTRATION_ID);
return resultIntent.getExtras();
}
/**
* Unsubscribe from a source to stop receiving messages from it.
* <p> * <p>
* Most applications use a single sender ID. You may use multiple senders if different * This function is blocking and should not be called on the main thread.
* servers may send messages to the app or for testing.
* *
* @param senderIds list of project numbers or Google accounts identifying who is allowed to * @param source to unsubscribe
* send messages to this application. * @param data (optional) additional information.
* @return registration id * @throws IOException if the request fails.
*/ */
public String register(String subtype, String... senderIds) throws IOException { public void unsubscribe(String source, Bundle data) throws IOException {
if (data == null) {
data = new Bundle();
}
// Use the register servlet, with 'delete=true'.
// Registration service returns a registration_id on success - or an error code.
data.putString(EXTRA_DELETE, "1");
subscribe(source, data);
}
private Intent registerRpc(Bundle data) throws IOException {
if (Looper.getMainLooper() == Looper.myLooper()) { if (Looper.getMainLooper() == Looper.myLooper()) {
throw new IOException(ERROR_MAIN_THREAD); throw new IOException(ERROR_MAIN_THREAD);
} }
if (getGcmVersion() < 0) {
throw new IOException("Google Play Services missing");
}
if (data == null) {
data = new Bundle();
}
final BlockingQueue<Intent> registerResult = new LinkedBlockingQueue<Intent>(); final BlockingQueue<Intent> responseResult = new LinkedBlockingQueue<Intent>();
Handler registrationHandler = new Handler(Looper.getMainLooper()) { Handler responseHandler = new Handler(Looper.getMainLooper()) {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
Intent res = (Intent) msg.obj; Intent res = (Intent) msg.obj;
registerResult.add(res); responseResult.add(res);
} }
}; };
Messenger messenger = new Messenger(registrationHandler); Messenger responseMessenger = new Messenger(responseHandler);
internalRegister(messenger, subtype, senderIds);
Intent intent = new Intent(ACTION_C2DM_REGISTER);
intent.setPackage(GOOGLE_PLAY_SERVICES_PACKAGE);
setPackageNameExtra(intent);
intent.putExtras(data);
intent.putExtra(EXTRA_MESSENGER, responseMessenger);
mContext.startService(intent);
try { try {
Intent regIntent = registerResult.poll(REGISTER_TIMEOUT, TimeUnit.MILLISECONDS); return responseResult.poll(REGISTER_TIMEOUT, TimeUnit.MILLISECONDS);
if (regIntent == null) {
throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
}
String registrationId = regIntent.getStringExtra(EXTRA_REGISTRATION_ID);
// registration succeeded
if (registrationId != null) {
return registrationId;
}
String err = regIntent.getStringExtra(C2DM_EXTRA_ERROR);
if (err != null) {
throw new IOException(err);
} else {
throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
}
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new IOException(e.getMessage()); throw new IOException(e.getMessage());
} }
} }
private void internalRegister(Messenger messenger, String subtype, String... senderIds) { private String getExtraOrThrow(Intent intent, String extraKey) throws IOException {
Intent intent = new Intent(ACTION_C2DM_REGISTER); if (intent == null) {
intent.setPackage(GOOGLE_PLAY_SERVICES_PACKAGE); throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
if (subtype != null) intent.putExtra("subtype", subtype);
intent.putExtra(EXTRA_MESSENGER, messenger);
setPackageNameExtra(intent);
intent.putExtra(EXTRA_SENDER, getFlatSenderIds(senderIds));
mContext.startService(intent);
} }
private String getFlatSenderIds(String... senderIds) { String extraValue = intent.getStringExtra(extraKey);
if (senderIds == null || senderIds.length == 0) { if (extraValue != null) {
throw new IllegalArgumentException("No senderIds"); return extraValue;
} }
StringBuilder builder = new StringBuilder(senderIds[0]);
for (int i = 1; i < senderIds.length; i++) { String err = intent.getStringExtra(C2DM_EXTRA_ERROR);
builder.append(',').append(senderIds[i]); if (err != null) {
throw new IOException(err);
} else {
throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
} }
return builder.toString();
} }
private void setPackageNameExtra(Intent intent) { private void setPackageNameExtra(Intent intent) {
...@@ -140,63 +191,14 @@ public class GoogleCloudMessagingV2 { ...@@ -140,63 +191,14 @@ public class GoogleCloudMessagingV2 {
intent.putExtra(INTENT_PARAM_APP, mAppPendingIntent); intent.putExtra(INTENT_PARAM_APP, mAppPendingIntent);
} }
/** private int getGcmVersion() {
* Unregister the application. Calling {@code unregister()} stops any PackageManager pm = mContext.getPackageManager();
* messages from the server.
* <p>
* This is a blocking call&mdash;you shouldn't call it from the UI thread.
* <p>
* You should rarely (if ever) need to call this method. Not only is it
* expensive in terms of resources, but it invalidates your registration ID,
* which you should never change unnecessarily. A better approach is to simply
* have your server stop sending messages. Only use unregister if you want
* to change your sender ID.
*
* @throws IOException if we can't connect to server to unregister.
*/
public void unregister(String subtype) throws IOException {
if (Looper.getMainLooper() == Looper.myLooper()) {
throw new IOException(ERROR_MAIN_THREAD);
}
final BlockingQueue<Intent> registerResult = new LinkedBlockingQueue<Intent>();
Handler registrationHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Intent res = (Intent) msg.obj;
registerResult.add(res);
}
};
Messenger messenger = new Messenger(registrationHandler);
internalUnregister(messenger, subtype);
try { try {
Intent regIntent = registerResult.poll(REGISTER_TIMEOUT, TimeUnit.MILLISECONDS); PackageInfo packageInfo = pm.getPackageInfo(GOOGLE_PLAY_SERVICES_PACKAGE, 0);
if (regIntent == null) { return packageInfo.versionCode;
throw new IOException(ERROR_SERVICE_NOT_AVAILABLE); } catch (PackageManager.NameNotFoundException e) {
} // No problem
String unregistered = regIntent.getStringExtra(EXTRA_UNREGISTERED);
if (unregistered != null) {
// All done
return;
}
String err = regIntent.getStringExtra(C2DM_EXTRA_ERROR);
if (err != null) {
throw new IOException(err);
} else {
throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
} }
} catch (InterruptedException e) { return -1;
throw new IOException(e.getMessage());
}
}
private void internalUnregister(Messenger messenger, String subtype) {
Intent intent = new Intent(ACTION_C2DM_UNREGISTER);
intent.setPackage(GOOGLE_PLAY_SERVICES_PACKAGE);
if (subtype != null) {
intent.putExtra("subtype", subtype);
}
intent.putExtra(EXTRA_MESSENGER, messenger);
setPackageNameExtra(intent);
mContext.startService(intent);
} }
} }
...@@ -70,6 +70,20 @@ void GCMDriver::Register(const std::string& app_id, ...@@ -70,6 +70,20 @@ void GCMDriver::Register(const std::string& app_id,
void GCMDriver::Unregister(const std::string& app_id, void GCMDriver::Unregister(const std::string& app_id,
const UnregisterCallback& callback) { const UnregisterCallback& callback) {
UnregisterInternal(app_id, nullptr /* sender_id */, callback);
}
void GCMDriver::UnregisterWithSenderId(
const std::string& app_id,
const std::string& sender_id,
const UnregisterCallback& callback) {
DCHECK(!sender_id.empty());
UnregisterInternal(app_id, &sender_id, callback);
}
void GCMDriver::UnregisterInternal(const std::string& app_id,
const std::string* sender_id,
const UnregisterCallback& callback) {
DCHECK(!app_id.empty()); DCHECK(!app_id.empty());
DCHECK(!callback.is_null()); DCHECK(!callback.is_null());
...@@ -88,6 +102,9 @@ void GCMDriver::Unregister(const std::string& app_id, ...@@ -88,6 +102,9 @@ void GCMDriver::Unregister(const std::string& app_id,
unregister_callbacks_[app_id] = callback; unregister_callbacks_[app_id] = callback;
if (sender_id)
UnregisterWithSenderIdImpl(app_id, *sender_id);
else
UnregisterImpl(app_id); UnregisterImpl(app_id);
} }
...@@ -117,6 +134,11 @@ void GCMDriver::Send(const std::string& app_id, ...@@ -117,6 +134,11 @@ void GCMDriver::Send(const std::string& app_id,
SendImpl(app_id, receiver_id, message); SendImpl(app_id, receiver_id, message);
} }
void GCMDriver::UnregisterWithSenderIdImpl(const std::string& app_id,
const std::string& sender_id) {
NOTREACHED();
}
void GCMDriver::RegisterFinished(const std::string& app_id, void GCMDriver::RegisterFinished(const std::string& app_id,
const std::string& registration_id, const std::string& registration_id,
GCMClient::Result result) { GCMClient::Result result) {
......
...@@ -37,8 +37,9 @@ class GCMDriver { ...@@ -37,8 +37,9 @@ class GCMDriver {
GCMDriver(); GCMDriver();
virtual ~GCMDriver(); virtual ~GCMDriver();
// Registers |sender_id| for an app. A registration ID will be returned by // Registers |sender_ids| for an app. A registration ID will be returned by
// the GCM server. // the GCM server. On Android, only a single sender ID is supported, but
// instead multiple simultaneous registrations are allowed.
// |app_id|: application ID. // |app_id|: application ID.
// |sender_ids|: list of IDs of the servers that are allowed to send the // |sender_ids|: list of IDs of the servers that are allowed to send the
// messages to the application. These IDs are assigned by the // messages to the application. These IDs are assigned by the
...@@ -48,12 +49,22 @@ class GCMDriver { ...@@ -48,12 +49,22 @@ class GCMDriver {
const std::vector<std::string>& sender_ids, const std::vector<std::string>& sender_ids,
const RegisterCallback& callback); const RegisterCallback& callback);
// Unregisters an app from using GCM. // Unregisters all sender_ids for an app. Only works on non-Android.
// |app_id|: application ID. // |app_id|: application ID.
// |callback|: to be called once the asynchronous operation is done. // |callback|: to be called once the asynchronous operation is done.
void Unregister(const std::string& app_id, void Unregister(const std::string& app_id,
const UnregisterCallback& callback); const UnregisterCallback& callback);
// Unregisters an (app_id, sender_id) pair from using GCM. Only works on
// Android.
// TODO(jianli): Switch to using GCM's unsubscribe API.
// |app_id|: application ID.
// |sender_id|: the sender ID that was passed when registering.
// |callback|: to be called once the asynchronous operation is done.
void UnregisterWithSenderId(const std::string& app_id,
const std::string& sender_id,
const UnregisterCallback& callback);
// Sends a message to a given receiver. // Sends a message to a given receiver.
// |app_id|: application ID. // |app_id|: application ID.
// |receiver_id|: registration ID of the receiver party. // |receiver_id|: registration ID of the receiver party.
...@@ -146,6 +157,10 @@ class GCMDriver { ...@@ -146,6 +157,10 @@ class GCMDriver {
// Platform-specific implementation of Unregister. // Platform-specific implementation of Unregister.
virtual void UnregisterImpl(const std::string& app_id) = 0; virtual void UnregisterImpl(const std::string& app_id) = 0;
// Platform-specific implementation of UnregisterWithSenderId.
virtual void UnregisterWithSenderIdImpl(const std::string& app_id,
const std::string& sender_id);
// Platform-specific implementation of Send. // Platform-specific implementation of Send.
virtual void SendImpl(const std::string& app_id, virtual void SendImpl(const std::string& app_id,
const std::string& receiver_id, const std::string& receiver_id,
...@@ -170,6 +185,11 @@ class GCMDriver { ...@@ -170,6 +185,11 @@ class GCMDriver {
void ClearCallbacks(); void ClearCallbacks();
private: private:
// Common code shared by Unregister and UnregisterWithSenderId.
void UnregisterInternal(const std::string& app_id,
const std::string* sender_id,
const UnregisterCallback& callback);
// Called after unregistration completes in order to trigger the pending // Called after unregistration completes in order to trigger the pending
// registration. // registration.
void RegisterAfterUnregister( void RegisterAfterUnregister(
......
...@@ -169,20 +169,26 @@ GCMClient::Result GCMDriverAndroid::EnsureStarted( ...@@ -169,20 +169,26 @@ GCMClient::Result GCMDriverAndroid::EnsureStarted(
} }
void GCMDriverAndroid::RegisterImpl( void GCMDriverAndroid::RegisterImpl(
const std::string& app_id, const std::string& app_id, const std::vector<std::string>& sender_ids) {
const std::vector<std::string>& sender_ids) { DCHECK_EQ(1u, sender_ids.size());
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
Java_GCMDriver_register( Java_GCMDriver_register(
env, java_ref_.obj(), env, java_ref_.obj(),
ConvertUTF8ToJavaString(env, app_id).Release(), ConvertUTF8ToJavaString(env, app_id).Release(),
ToJavaArrayOfStrings(env, sender_ids).obj()); ConvertUTF8ToJavaString(env, sender_ids[0]).Release());
} }
void GCMDriverAndroid::UnregisterImpl(const std::string& app_id) { void GCMDriverAndroid::UnregisterImpl(const std::string& app_id) {
NOTREACHED();
}
void GCMDriverAndroid::UnregisterWithSenderIdImpl(
const std::string& app_id, const std::string& sender_id) {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
Java_GCMDriver_unregister( Java_GCMDriver_unregister(
env, java_ref_.obj(), env, java_ref_.obj(),
ConvertUTF8ToJavaString(env, app_id).Release()); ConvertUTF8ToJavaString(env, app_id).Release(),
ConvertUTF8ToJavaString(env, sender_id).Release());
} }
void GCMDriverAndroid::SendImpl(const std::string& app_id, void GCMDriverAndroid::SendImpl(const std::string& app_id,
......
...@@ -71,6 +71,8 @@ class GCMDriverAndroid : public GCMDriver { ...@@ -71,6 +71,8 @@ class GCMDriverAndroid : public GCMDriver {
void RegisterImpl(const std::string& app_id, void RegisterImpl(const std::string& app_id,
const std::vector<std::string>& sender_ids) override; const std::vector<std::string>& sender_ids) override;
void UnregisterImpl(const std::string& app_id) override; void UnregisterImpl(const std::string& app_id) override;
void UnregisterWithSenderIdImpl(const std::string& app_id,
const std::string& sender_id) override;
void SendImpl(const std::string& app_id, void SendImpl(const std::string& app_id,
const std::string& receiver_id, const std::string& receiver_id,
const GCMClient::OutgoingMessage& message) override; const GCMClient::OutgoingMessage& message) override;
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
namespace content { namespace content {
const char kPushSenderIdServiceWorkerKey[] = "push_sender_id";
const char kPushRegistrationIdServiceWorkerKey[] = "push_registration_id"; const char kPushRegistrationIdServiceWorkerKey[] = "push_registration_id";
namespace { namespace {
...@@ -38,8 +39,6 @@ void RecordRegistrationStatus(PushRegistrationStatus status) { ...@@ -38,8 +39,6 @@ void RecordRegistrationStatus(PushRegistrationStatus status) {
PUSH_REGISTRATION_STATUS_LAST + 1); PUSH_REGISTRATION_STATUS_LAST + 1);
} }
const char kSenderIdServiceWorkerKey[] = "push_sender_id";
} // namespace } // namespace
struct PushMessagingMessageFilter::RegisterData { struct PushMessagingMessageFilter::RegisterData {
...@@ -71,7 +70,8 @@ class PushMessagingMessageFilter::Core { ...@@ -71,7 +70,8 @@ class PushMessagingMessageFilter::Core {
// Called via PostTask from IO thread. // Called via PostTask from IO thread.
void UnregisterFromService(int request_id, void UnregisterFromService(int request_id,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const GURL& requesting_origin); const GURL& requesting_origin,
const std::string& sender_id);
// Public GetPermission methods on UI thread --------------------------------- // Public GetPermission methods on UI thread ---------------------------------
...@@ -212,7 +212,7 @@ void PushMessagingMessageFilter::OnRegisterFromDocument( ...@@ -212,7 +212,7 @@ void PushMessagingMessageFilter::OnRegisterFromDocument(
service_worker_context_->context()->storage()->StoreUserData( service_worker_context_->context()->storage()->StoreUserData(
service_worker_registration_id, service_worker_registration_id,
data.requesting_origin, data.requesting_origin,
kSenderIdServiceWorkerKey, kPushSenderIdServiceWorkerKey,
sender_id, sender_id,
base::Bind(&PushMessagingMessageFilter::DidPersistSenderId, base::Bind(&PushMessagingMessageFilter::DidPersistSenderId,
weak_factory_io_to_io_.GetWeakPtr(), weak_factory_io_to_io_.GetWeakPtr(),
...@@ -286,7 +286,7 @@ void PushMessagingMessageFilter::DidCheckForExistingRegistration( ...@@ -286,7 +286,7 @@ void PushMessagingMessageFilter::DidCheckForExistingRegistration(
} else { } else {
service_worker_context_->context()->storage()->GetUserData( service_worker_context_->context()->storage()->GetUserData(
data.service_worker_registration_id, data.service_worker_registration_id,
kSenderIdServiceWorkerKey, kPushSenderIdServiceWorkerKey,
base::Bind(&PushMessagingMessageFilter::DidGetSenderIdFromStorage, base::Bind(&PushMessagingMessageFilter::DidGetSenderIdFromStorage,
weak_factory_io_to_io_.GetWeakPtr(), data)); weak_factory_io_to_io_.GetWeakPtr(), data));
} }
...@@ -436,18 +436,46 @@ void PushMessagingMessageFilter::OnUnregister( ...@@ -436,18 +436,46 @@ void PushMessagingMessageFilter::OnUnregister(
service_worker_context_->context()->storage()->GetUserData( service_worker_context_->context()->storage()->GetUserData(
service_worker_registration_id, service_worker_registration_id,
kPushRegistrationIdServiceWorkerKey, kPushRegistrationIdServiceWorkerKey,
base::Bind(&PushMessagingMessageFilter::DoUnregister, base::Bind(
&PushMessagingMessageFilter::UnregisterHavingGottenPushRegistrationId,
weak_factory_io_to_io_.GetWeakPtr(), request_id,
service_worker_registration_id,
service_worker_registration->pattern().GetOrigin()));
}
void PushMessagingMessageFilter::UnregisterHavingGottenPushRegistrationId(
int request_id,
int64 service_worker_registration_id,
const GURL& requesting_origin,
const std::string& push_registration_id, // Unused, we just want the status
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (service_worker_status == SERVICE_WORKER_OK) {
service_worker_context_->context()->storage()->GetUserData(
service_worker_registration_id,
kPushSenderIdServiceWorkerKey,
base::Bind(
&PushMessagingMessageFilter::UnregisterHavingGottenSenderId,
weak_factory_io_to_io_.GetWeakPtr(), weak_factory_io_to_io_.GetWeakPtr(),
request_id, request_id,
service_worker_registration_id, service_worker_registration_id,
service_worker_registration->pattern().GetOrigin())); requesting_origin));
} else {
// Errors are handled the same, whether we were trying to get the
// push_registration_id or the sender_id.
UnregisterHavingGottenSenderId(request_id, service_worker_registration_id,
requesting_origin,
std::string() /* sender_id */,
service_worker_status);
}
} }
void PushMessagingMessageFilter::DoUnregister( void PushMessagingMessageFilter::UnregisterHavingGottenSenderId(
int request_id, int request_id,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const GURL& requesting_origin, const GURL& requesting_origin,
const std::string& push_registration_id, const std::string& sender_id,
ServiceWorkerStatusCode service_worker_status) { ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
...@@ -457,7 +485,8 @@ void PushMessagingMessageFilter::DoUnregister( ...@@ -457,7 +485,8 @@ void PushMessagingMessageFilter::DoUnregister(
BrowserThread::UI, FROM_HERE, BrowserThread::UI, FROM_HERE,
base::Bind(&Core::UnregisterFromService, base::Bind(&Core::UnregisterFromService,
base::Unretained(ui_core_.get()), request_id, base::Unretained(ui_core_.get()), request_id,
service_worker_registration_id, requesting_origin)); service_worker_registration_id, requesting_origin,
sender_id));
return; return;
case SERVICE_WORKER_ERROR_NOT_FOUND: case SERVICE_WORKER_ERROR_NOT_FOUND:
// We did not find a registration, stop here and notify the renderer that // We did not find a registration, stop here and notify the renderer that
...@@ -490,7 +519,8 @@ void PushMessagingMessageFilter::DoUnregister( ...@@ -490,7 +519,8 @@ void PushMessagingMessageFilter::DoUnregister(
void PushMessagingMessageFilter::Core::UnregisterFromService( void PushMessagingMessageFilter::Core::UnregisterFromService(
int request_id, int request_id,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const GURL& requesting_origin) { const GURL& requesting_origin,
const std::string& sender_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
PushMessagingService* push_service = service(); PushMessagingService* push_service = service();
if (!push_service) { if (!push_service) {
...@@ -502,7 +532,7 @@ void PushMessagingMessageFilter::Core::UnregisterFromService( ...@@ -502,7 +532,7 @@ void PushMessagingMessageFilter::Core::UnregisterFromService(
} }
push_service->Unregister( push_service->Unregister(
requesting_origin, service_worker_registration_id, requesting_origin, service_worker_registration_id, sender_id,
false /* retry_on_failure */, false /* retry_on_failure */,
base::Bind(&Core::DidUnregisterFromService, base::Bind(&Core::DidUnregisterFromService,
weak_factory_ui_to_ui_.GetWeakPtr(), weak_factory_ui_to_ui_.GetWeakPtr(),
......
...@@ -22,6 +22,7 @@ namespace content { ...@@ -22,6 +22,7 @@ namespace content {
class PushMessagingService; class PushMessagingService;
class ServiceWorkerContextWrapper; class ServiceWorkerContextWrapper;
extern const char kPushSenderIdServiceWorkerKey[];
extern const char kPushRegistrationIdServiceWorkerKey[]; extern const char kPushRegistrationIdServiceWorkerKey[];
class PushMessagingMessageFilter : public BrowserMessageFilter { class PushMessagingMessageFilter : public BrowserMessageFilter {
...@@ -95,12 +96,20 @@ class PushMessagingMessageFilter : public BrowserMessageFilter { ...@@ -95,12 +96,20 @@ class PushMessagingMessageFilter : public BrowserMessageFilter {
void OnUnregister(int request_id, int64 service_worker_registration_id); void OnUnregister(int request_id, int64 service_worker_registration_id);
void DoUnregister(int request_id, void UnregisterHavingGottenPushRegistrationId(
int request_id,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const GURL& requesting_origin, const GURL& requesting_origin,
const std::string& push_registration_id, const std::string& push_registration_id,
ServiceWorkerStatusCode service_worker_status); ServiceWorkerStatusCode service_worker_status);
void UnregisterHavingGottenSenderId(
int request_id,
int64 service_worker_registration_id,
const GURL& requesting_origin,
const std::string& sender_id,
ServiceWorkerStatusCode service_worker_status);
// Called via PostTask from UI thread. // Called via PostTask from UI thread.
void ClearRegistrationData(int request_id, void ClearRegistrationData(int request_id,
int64 service_worker_registration_id, int64 service_worker_registration_id,
......
...@@ -17,8 +17,8 @@ namespace { ...@@ -17,8 +17,8 @@ namespace {
const char kNotificationsShownServiceWorkerKey[] = const char kNotificationsShownServiceWorkerKey[] =
"notifications_shown_by_last_few_pushes"; "notifications_shown_by_last_few_pushes";
void CallGetNotificationsShownCallbackFromIO( void CallStringCallbackFromIO(
const PushMessagingService::GetNotificationsShownCallback& callback, const PushMessagingService::StringCallback& callback,
const std::string& data, const std::string& data,
ServiceWorkerStatusCode service_worker_status) { ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
...@@ -37,14 +37,15 @@ void CallResultCallbackFromIO( ...@@ -37,14 +37,15 @@ void CallResultCallbackFromIO(
base::Bind(callback, success)); base::Bind(callback, success));
} }
void GetNotificationsShownOnIO( void GetUserDataOnIO(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_wrapper, scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_wrapper,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const PushMessagingService::GetNotificationsShownCallback& callback) { const std::string& key,
const PushMessagingService::StringCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
service_worker_context_wrapper->context()->storage()->GetUserData( service_worker_context_wrapper->context()->storage()->GetUserData(
service_worker_registration_id, kNotificationsShownServiceWorkerKey, service_worker_registration_id, key,
base::Bind(&CallGetNotificationsShownCallbackFromIO, callback)); base::Bind(&CallStringCallbackFromIO, callback));
} }
void SetNotificationsShownOnIO( void SetNotificationsShownOnIO(
...@@ -73,20 +74,30 @@ void ClearPushRegistrationIDOnIO( ...@@ -73,20 +74,30 @@ void ClearPushRegistrationIDOnIO(
base::Bind(&OnClearPushRegistrationServiceWorkerKey)); base::Bind(&OnClearPushRegistrationServiceWorkerKey));
} }
scoped_refptr<ServiceWorkerContextWrapper> GetServiceWorkerContext(
BrowserContext* browser_context, const GURL& origin) {
StoragePartition* partition =
BrowserContext::GetStoragePartitionForSite(browser_context, origin);
return make_scoped_refptr(
static_cast<ServiceWorkerContextWrapper*>(
partition->GetServiceWorkerContext()));
}
} // anonymous namespace } // anonymous namespace
// static // static
void PushMessagingService::GetNotificationsShownByLastFewPushes( void PushMessagingService::GetNotificationsShownByLastFewPushes(
ServiceWorkerContext* service_worker_context, ServiceWorkerContext* service_worker_context,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const GetNotificationsShownCallback& callback) { const StringCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
scoped_refptr<ServiceWorkerContextWrapper> wrapper = scoped_refptr<ServiceWorkerContextWrapper> wrapper =
static_cast<ServiceWorkerContextWrapper*>(service_worker_context); static_cast<ServiceWorkerContextWrapper*>(service_worker_context);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&GetNotificationsShownOnIO, base::Bind(&GetUserDataOnIO,
wrapper, wrapper,
service_worker_registration_id, service_worker_registration_id,
kNotificationsShownServiceWorkerKey,
callback)); callback));
} }
...@@ -109,23 +120,33 @@ void PushMessagingService::SetNotificationsShownByLastFewPushes( ...@@ -109,23 +120,33 @@ void PushMessagingService::SetNotificationsShownByLastFewPushes(
callback)); callback));
} }
// static
void PushMessagingService::GetSenderId(BrowserContext* browser_context,
const GURL& origin,
int64 service_worker_registration_id,
const StringCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&GetUserDataOnIO,
GetServiceWorkerContext(browser_context, origin),
service_worker_registration_id,
kPushSenderIdServiceWorkerKey,
callback));
}
// static // static
void PushMessagingService::ClearPushRegistrationID( void PushMessagingService::ClearPushRegistrationID(
BrowserContext* browser_context, BrowserContext* browser_context,
const GURL& origin, const GURL& origin,
int64 service_worker_registration_id) { int64 service_worker_registration_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
StoragePartition* partition =
BrowserContext::GetStoragePartitionForSite(browser_context, origin);
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context =
static_cast<ServiceWorkerContextWrapper*>(
partition->GetServiceWorkerContext());
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::IO, BrowserThread::IO,
FROM_HERE, FROM_HERE,
base::Bind(&ClearPushRegistrationIDOnIO, base::Bind(&ClearPushRegistrationIDOnIO,
service_worker_context, GetServiceWorkerContext(browser_context, origin),
service_worker_registration_id)); service_worker_registration_id));
} }
......
...@@ -22,17 +22,17 @@ class ServiceWorkerContext; ...@@ -22,17 +22,17 @@ class ServiceWorkerContext;
// push messaging services like GCM. Must only be used on the UI thread. // push messaging services like GCM. Must only be used on the UI thread.
class CONTENT_EXPORT PushMessagingService { class CONTENT_EXPORT PushMessagingService {
public: public:
using GetNotificationsShownCallback =
base::Callback<void(const std::string& notifications_shown,
bool success, bool not_found)>;
using ResultCallback = base::Callback<void(bool success)>;
using RegisterCallback = using RegisterCallback =
base::Callback<void(const std::string& /* registration_id */, base::Callback<void(const std::string& /* registration_id */,
PushRegistrationStatus /* status */)>; PushRegistrationStatus /* status */)>;
using UnregisterCallback = base::Callback<void(PushUnregistrationStatus)>; using UnregisterCallback = base::Callback<void(PushUnregistrationStatus)>;
using StringCallback = base::Callback<void(const std::string& data,
bool success,
bool not_found)>;
using ResultCallback = base::Callback<void(bool success)>;
virtual ~PushMessagingService() {} virtual ~PushMessagingService() {}
// Returns the absolute URL exposed by the push server where the webapp server // Returns the absolute URL exposed by the push server where the webapp server
...@@ -63,6 +63,7 @@ class CONTENT_EXPORT PushMessagingService { ...@@ -63,6 +63,7 @@ class CONTENT_EXPORT PushMessagingService {
// the push service. // the push service.
virtual void Unregister(const GURL& requesting_origin, virtual void Unregister(const GURL& requesting_origin,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const std::string& sender_id,
bool retry_on_failure, bool retry_on_failure,
const UnregisterCallback& callback) = 0; const UnregisterCallback& callback) = 0;
...@@ -81,7 +82,7 @@ class CONTENT_EXPORT PushMessagingService { ...@@ -81,7 +82,7 @@ class CONTENT_EXPORT PushMessagingService {
static void GetNotificationsShownByLastFewPushes( static void GetNotificationsShownByLastFewPushes(
ServiceWorkerContext* service_worker_context, ServiceWorkerContext* service_worker_context,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const GetNotificationsShownCallback& callback); const StringCallback& callback);
static void SetNotificationsShownByLastFewPushes( static void SetNotificationsShownByLastFewPushes(
ServiceWorkerContext* service_worker_context, ServiceWorkerContext* service_worker_context,
int64 service_worker_registration_id, int64 service_worker_registration_id,
...@@ -89,6 +90,11 @@ class CONTENT_EXPORT PushMessagingService { ...@@ -89,6 +90,11 @@ class CONTENT_EXPORT PushMessagingService {
const std::string& notifications_shown, const std::string& notifications_shown,
const ResultCallback& callback); const ResultCallback& callback);
static void GetSenderId(BrowserContext* browser_context,
const GURL& origin,
int64 service_worker_registration_id,
const StringCallback& callback);
// Clear the push registration id stored in the service worker with the given // Clear the push registration id stored in the service worker with the given
// |service_worker_registration_id| for the given |origin|. // |service_worker_registration_id| for the given |origin|.
static void ClearPushRegistrationID(BrowserContext* browser_context, static void ClearPushRegistrationID(BrowserContext* browser_context,
......
...@@ -67,6 +67,7 @@ LayoutTestPushMessagingService::GetPermissionStatus( ...@@ -67,6 +67,7 @@ LayoutTestPushMessagingService::GetPermissionStatus(
void LayoutTestPushMessagingService::Unregister( void LayoutTestPushMessagingService::Unregister(
const GURL& requesting_origin, const GURL& requesting_origin,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const std::string& sender_id,
bool retry_on_failure, bool retry_on_failure,
const UnregisterCallback& callback) { const UnregisterCallback& callback) {
callback.Run(PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTER); callback.Run(PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTER);
......
...@@ -42,6 +42,7 @@ class LayoutTestPushMessagingService : public PushMessagingService { ...@@ -42,6 +42,7 @@ class LayoutTestPushMessagingService : public PushMessagingService {
const GURL& embedding_origin) override; const GURL& embedding_origin) override;
void Unregister(const GURL& requesting_origin, void Unregister(const GURL& requesting_origin,
int64 service_worker_registration_id, int64 service_worker_registration_id,
const std::string& sender_id,
bool retry_on_failure, bool retry_on_failure,
const UnregisterCallback& callback) override; const UnregisterCallback& callback) override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment