| Index: chrome/browser/notifications/notification_ui_manager_android.cc
|
| diff --git a/chrome/browser/notifications/notification_ui_manager_android.cc b/chrome/browser/notifications/notification_ui_manager_android.cc
|
| index a3a339c15ad21ae533bceea3e9c581b6c020829d..68cd9e8b66a9254e700f0a28c03affb5d6d03703 100644
|
| --- a/chrome/browser/notifications/notification_ui_manager_android.cc
|
| +++ b/chrome/browser/notifications/notification_ui_manager_android.cc
|
| @@ -4,11 +4,18 @@
|
|
|
| #include "chrome/browser/notifications/notification_ui_manager_android.h"
|
|
|
| +#include "base/android/jni_array.h"
|
| #include "base/android/jni_string.h"
|
| #include "base/logging.h"
|
| +#include "base/pickle.h"
|
| #include "base/strings/string16.h"
|
| #include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/notifications/persistent_notification_delegate.h"
|
| +#include "chrome/browser/notifications/platform_notification_service_impl.h"
|
| #include "chrome/browser/notifications/profile_notification.h"
|
| +#include "chrome/browser/profiles/profile_manager.h"
|
| +#include "content/public/common/persistent_notification_status.h"
|
| +#include "content/public/common/platform_notification_data.h"
|
| #include "jni/NotificationUIManager_jni.h"
|
| #include "ui/gfx/android/java_bitmap.h"
|
| #include "ui/gfx/image/image.h"
|
| @@ -18,6 +25,102 @@ using base::android::ConvertJavaStringToUTF8;
|
| using base::android::ConvertUTF16ToJavaString;
|
| using base::android::ConvertUTF8ToJavaString;
|
|
|
| +namespace {
|
| +
|
| +// The maximum size of the serialized pickle that carries a notification's meta
|
| +// information. Notifications carrying more data will be silently dropped - with
|
| +// an error being written to the log.
|
| +const int kMaximumSerializedNotificationSizeBytes = 1024 * 1024;
|
| +
|
| +// Persistent notifications are likely to outlive the browser process they were
|
| +// created by on Android. In order to be able to re-surface the notification
|
| +// when the user interacts with them, all relevant notification data needs to
|
| +// be serialized with the notification itself.
|
| +//
|
| +// In the near future, as more features get added to Chrome's notification
|
| +// implementation, this will be done by storing the persistent notification data
|
| +// in a database. However, to support launching Chrome for Android from a
|
| +// notification event until that exists, serialize the data in the Intent.
|
| +//
|
| +// TODO(peter): Move towards storing notification data in a database rather than
|
| +// as a serialized Intent extra.
|
| +
|
| +scoped_ptr<Pickle> SerializePersistentNotification(
|
| + const content::PlatformNotificationData& notification_data,
|
| + const GURL& origin,
|
| + int64 service_worker_registration_id) {
|
| + scoped_ptr<Pickle> pickle(new Pickle);
|
| +
|
| + // content::PlatformNotificationData
|
| + pickle->WriteString16(notification_data.title);
|
| + pickle->WriteInt(static_cast<int>(notification_data.direction));
|
| + pickle->WriteString(notification_data.lang);
|
| + pickle->WriteString16(notification_data.body);
|
| + pickle->WriteString16(notification_data.tag);
|
| + pickle->WriteString(notification_data.icon.spec());
|
| +
|
| + // The origin which is displaying the notification.
|
| + pickle->WriteString(origin.spec());
|
| +
|
| + // The Service Worker registration this notification is associated with.
|
| + pickle->WriteInt64(service_worker_registration_id);
|
| +
|
| + if (pickle->size() > kMaximumSerializedNotificationSizeBytes)
|
| + return nullptr;
|
| +
|
| + return pickle.Pass();
|
| +}
|
| +
|
| +bool UnserializePersistentNotification(
|
| + const Pickle& pickle,
|
| + content::PlatformNotificationData* notification_data,
|
| + GURL* origin,
|
| + int64* service_worker_registration_id) {
|
| + DCHECK(notification_data && origin && service_worker_registration_id);
|
| + PickleIterator iterator(pickle);
|
| +
|
| + std::string icon_url, origin_url;
|
| + int direction_value;
|
| +
|
| + // Unpack content::PlatformNotificationData.
|
| + if (!iterator.ReadString16(¬ification_data->title) ||
|
| + !iterator.ReadInt(&direction_value) ||
|
| + !iterator.ReadString(¬ification_data->lang) ||
|
| + !iterator.ReadString16(¬ification_data->body) ||
|
| + !iterator.ReadString16(¬ification_data->tag) ||
|
| + !iterator.ReadString(&icon_url)) {
|
| + return false;
|
| + }
|
| +
|
| + notification_data->direction =
|
| + static_cast<content::PlatformNotificationData::NotificationDirection>(
|
| + direction_value);
|
| + notification_data->icon = GURL(icon_url);
|
| +
|
| + // Unpack the origin which displayed this notification.
|
| + if (!iterator.ReadString(&origin_url))
|
| + return false;
|
| +
|
| + *origin = GURL(origin_url);
|
| +
|
| + // Unpack the Service Worker registration id.
|
| + if (!iterator.ReadInt64(service_worker_registration_id))
|
| + return false;
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// Called when the "notificationclick" event in the Service Worker has finished
|
| +// executing for a notification that was created in a previous instance of the
|
| +// browser.
|
| +void OnEventDispatchComplete(content::PersistentNotificationStatus status) {
|
| + // TODO(peter): Add UMA statistics based on |status|.
|
| + // TODO(peter): Decide if we want to forcefully shut down the browser process
|
| + // if we're confident it was created for delivering this event.
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| // Called by the Java side when a notification event has been received, but the
|
| // NotificationUIManager has not been initialized yet. Enforce initialization of
|
| // the class.
|
| @@ -46,18 +149,55 @@ NotificationUIManagerAndroid::~NotificationUIManagerAndroid() {
|
| }
|
|
|
| bool NotificationUIManagerAndroid::OnNotificationClicked(
|
| - JNIEnv* env, jobject java_object, jstring notification_id) {
|
| + JNIEnv* env,
|
| + jobject java_object,
|
| + jstring notification_id,
|
| + jbyteArray serialized_notification_data) {
|
| std::string id = ConvertJavaStringToUTF8(env, notification_id);
|
|
|
| auto iter = profile_notifications_.find(id);
|
| - if (iter == profile_notifications_.end()) {
|
| - // TODO(peter): Handle the "click" event for notifications which have
|
| - // outlived the browser process that created them.
|
| + if (iter != profile_notifications_.end()) {
|
| + const Notification& notification = iter->second->notification();
|
| + notification.delegate()->Click();
|
| +
|
| + return true;
|
| + }
|
| +
|
| + // If the Notification were not found, it may be a persistent notification
|
| + // that outlived the Chrome browser process. In this case, try to
|
| + // unserialize the notification's serialized data and trigger the click
|
| + // event manually.
|
| +
|
| + std::vector<uint8> bytes;
|
| + base::android::JavaByteArrayToByteVector(env, serialized_notification_data,
|
| + &bytes);
|
| + if (!bytes.size())
|
| + return false;
|
| +
|
| + content::PlatformNotificationData notification_data;
|
| + GURL origin;
|
| + int64 service_worker_registration_id;
|
| +
|
| + Pickle pickle(reinterpret_cast<const char*>(&bytes[0]), bytes.size());
|
| + if (!UnserializePersistentNotification(pickle, ¬ification_data, &origin,
|
| + &service_worker_registration_id)) {
|
| return false;
|
| }
|
|
|
| - const Notification& notification = iter->second->notification();
|
| - notification.delegate()->Click();
|
| + PlatformNotificationServiceImpl* service =
|
| + PlatformNotificationServiceImpl::GetInstance();
|
| +
|
| + // TODO(peter): Rather than assuming that the last used profile is the
|
| + // appropriate one for this notification, the used profile should be
|
| + // stored as part of the notification's data. See https://crbug.com/437574.
|
| + service->OnPersistentNotificationClick(
|
| + ProfileManager::GetLastUsedProfile(),
|
| + service_worker_registration_id,
|
| + id,
|
| + origin,
|
| + notification_data,
|
| + base::Bind(&OnEventDispatchComplete));
|
| +
|
| return true;
|
| }
|
|
|
| @@ -102,9 +242,29 @@ void NotificationUIManagerAndroid::Add(const Notification& notification,
|
| if (!icon_bitmap.isNull())
|
| icon = gfx::ConvertToJavaBitmap(&icon_bitmap);
|
|
|
| + ScopedJavaLocalRef<jbyteArray> notification_data;
|
| + if (true /** is_persistent_notification **/) {
|
| + PersistentNotificationDelegate* delegate =
|
| + static_cast<PersistentNotificationDelegate*>(notification.delegate());
|
| + scoped_ptr<Pickle> pickle = SerializePersistentNotification(
|
| + delegate->notification_data(),
|
| + notification.origin_url(),
|
| + delegate->service_worker_registration_id());
|
| +
|
| + if (!pickle) {
|
| + LOG(ERROR) <<
|
| + "Unable to serialize the notification, payload too large (max 1MB).";
|
| + RemoveProfileNotification(profile_notification);
|
| + return;
|
| + }
|
| +
|
| + notification_data = base::android::ToJavaByteArray(
|
| + env, static_cast<const uint8*>(pickle->data()), pickle->size());
|
| + }
|
| +
|
| int platform_id = Java_NotificationUIManager_displayNotification(
|
| env, java_object_.obj(), id.obj(), title.obj(), body.obj(), icon.obj(),
|
| - origin.obj());
|
| + origin.obj(), notification_data.obj());
|
|
|
| std::string notification_id = profile_notification->notification().id();
|
| platform_notifications_[notification_id] = platform_id;
|
|
|