Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1690)

Side by Side Diff: chrome/browser/notifications/notification_ui_manager_android.cc

Issue 839533004: Store serialized notification data as an Intent extra on Android. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/notifications/notification_ui_manager_android.h" 5 #include "chrome/browser/notifications/notification_ui_manager_android.h"
6 6
7 #include "base/android/jni_array.h"
7 #include "base/android/jni_string.h" 8 #include "base/android/jni_string.h"
8 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/pickle.h"
9 #include "base/strings/string16.h" 11 #include "base/strings/string16.h"
10 #include "chrome/browser/browser_process.h" 12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/notifications/persistent_notification_delegate.h"
14 #include "chrome/browser/notifications/platform_notification_service_impl.h"
11 #include "chrome/browser/notifications/profile_notification.h" 15 #include "chrome/browser/notifications/profile_notification.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "content/public/common/persistent_notification_status.h"
18 #include "content/public/common/platform_notification_data.h"
12 #include "jni/NotificationUIManager_jni.h" 19 #include "jni/NotificationUIManager_jni.h"
13 #include "ui/gfx/android/java_bitmap.h" 20 #include "ui/gfx/android/java_bitmap.h"
14 #include "ui/gfx/image/image.h" 21 #include "ui/gfx/image/image.h"
15 22
16 using base::android::AttachCurrentThread; 23 using base::android::AttachCurrentThread;
17 using base::android::ConvertJavaStringToUTF8; 24 using base::android::ConvertJavaStringToUTF8;
18 using base::android::ConvertUTF16ToJavaString; 25 using base::android::ConvertUTF16ToJavaString;
19 using base::android::ConvertUTF8ToJavaString; 26 using base::android::ConvertUTF8ToJavaString;
20 27
28 namespace {
29
30 // The maximum size of the serialized pickle that carries a notification's meta
31 // information. Notifications carrying more data will be silently dropped - with
32 // an error being written to the log.
33 const int kMaximumSerializedNotificationSizeBytes = 1024 * 1024;
34
35 // Persistent notifications are likely to outlive the browser process they were
36 // created by on Android. In order to be able to re-surface the notification
37 // when the user interacts with them, all relevant notification data needs to
38 // be serialized with the notification itself.
39 //
40 // In the near future, as more features get added to Chrome's notification
41 // implementation, this will be done by storing the persistent notification data
42 // in a database. However, to support launching Chrome for Android from a
43 // notification event until that exists, serialize the data in the Intent.
44 //
45 // TODO(peter): Move towards storing notification data in a database rather than
46 // as a serialized Intent extra.
47
48 scoped_ptr<Pickle> SerializePersistentNotification(
49 const content::PlatformNotificationData& notification_data,
50 const GURL& origin,
51 int64 service_worker_registration_id) {
52 scoped_ptr<Pickle> pickle(new Pickle);
53
54 // content::PlatformNotificationData
55 pickle->WriteString16(notification_data.title);
56 pickle->WriteInt(static_cast<int>(notification_data.direction));
57 pickle->WriteString(notification_data.lang);
58 pickle->WriteString16(notification_data.body);
59 pickle->WriteString16(notification_data.tag);
60 pickle->WriteString(notification_data.icon.spec());
61
62 // The origin which is displaying the notification.
63 pickle->WriteString(origin.spec());
64
65 // The Service Worker registration this notification is associated with.
66 pickle->WriteInt64(service_worker_registration_id);
67
68 if (pickle->size() > kMaximumSerializedNotificationSizeBytes)
69 return nullptr;
70
71 return pickle.Pass();
72 }
73
74 bool UnserializePersistentNotification(
75 const Pickle& pickle,
76 content::PlatformNotificationData* notification_data,
77 GURL* origin,
78 int64* service_worker_registration_id) {
79 DCHECK(notification_data && origin && service_worker_registration_id);
80 PickleIterator iterator(pickle);
81
82 std::string icon_url, origin_url;
83 int direction_value;
84
85 // Unpack content::PlatformNotificationData.
86 if (!iterator.ReadString16(&notification_data->title) ||
87 !iterator.ReadInt(&direction_value) ||
88 !iterator.ReadString(&notification_data->lang) ||
89 !iterator.ReadString16(&notification_data->body) ||
90 !iterator.ReadString16(&notification_data->tag) ||
91 !iterator.ReadString(&icon_url)) {
92 return false;
93 }
94
95 notification_data->direction =
96 static_cast<content::PlatformNotificationData::NotificationDirection>(
97 direction_value);
98 notification_data->icon = GURL(icon_url);
99
100 // Unpack the origin which displayed this notification.
101 if (!iterator.ReadString(&origin_url))
102 return false;
103
104 *origin = GURL(origin_url);
105
106 // Unpack the Service Worker registration id.
107 if (!iterator.ReadInt64(service_worker_registration_id))
108 return false;
109
110 return true;
111 }
112
113 // Called when the "notificationclick" event in the Service Worker has finished
114 // executing for a notification that was created in a previous instance of the
115 // browser.
116 void OnEventDispatchComplete(content::PersistentNotificationStatus status) {
117 // TODO(peter): Add UMA statistics based on |status|.
118 // TODO(peter): Decide if we want to forcefully shut down the browser process
119 // if we're confident it was created for delivering this event.
120 }
121
122 } // namespace
123
21 // Called by the Java side when a notification event has been received, but the 124 // Called by the Java side when a notification event has been received, but the
22 // NotificationUIManager has not been initialized yet. Enforce initialization of 125 // NotificationUIManager has not been initialized yet. Enforce initialization of
23 // the class. 126 // the class.
24 static void InitializeNotificationUIManager(JNIEnv* env, jclass clazz) { 127 static void InitializeNotificationUIManager(JNIEnv* env, jclass clazz) {
25 g_browser_process->notification_ui_manager(); 128 g_browser_process->notification_ui_manager();
26 } 129 }
27 130
28 // static 131 // static
29 NotificationUIManager* NotificationUIManager::Create(PrefService* local_state) { 132 NotificationUIManager* NotificationUIManager::Create(PrefService* local_state) {
30 return new NotificationUIManagerAndroid(); 133 return new NotificationUIManagerAndroid();
31 } 134 }
32 135
33 NotificationUIManagerAndroid::NotificationUIManagerAndroid() { 136 NotificationUIManagerAndroid::NotificationUIManagerAndroid() {
34 java_object_.Reset( 137 java_object_.Reset(
35 Java_NotificationUIManager_create( 138 Java_NotificationUIManager_create(
36 AttachCurrentThread(), 139 AttachCurrentThread(),
37 reinterpret_cast<intptr_t>(this), 140 reinterpret_cast<intptr_t>(this),
38 base::android::GetApplicationContext())); 141 base::android::GetApplicationContext()));
39 142
40 // TODO(peter): Synchronize notifications with the Java side. 143 // TODO(peter): Synchronize notifications with the Java side.
41 } 144 }
42 145
43 NotificationUIManagerAndroid::~NotificationUIManagerAndroid() { 146 NotificationUIManagerAndroid::~NotificationUIManagerAndroid() {
44 Java_NotificationUIManager_destroy(AttachCurrentThread(), 147 Java_NotificationUIManager_destroy(AttachCurrentThread(),
45 java_object_.obj()); 148 java_object_.obj());
46 } 149 }
47 150
48 bool NotificationUIManagerAndroid::OnNotificationClicked( 151 bool NotificationUIManagerAndroid::OnNotificationClicked(
49 JNIEnv* env, jobject java_object, jstring notification_id) { 152 JNIEnv* env,
153 jobject java_object,
154 jstring notification_id,
155 jbyteArray serialized_notification_data) {
50 std::string id = ConvertJavaStringToUTF8(env, notification_id); 156 std::string id = ConvertJavaStringToUTF8(env, notification_id);
51 157
52 auto iter = profile_notifications_.find(id); 158 auto iter = profile_notifications_.find(id);
53 if (iter == profile_notifications_.end()) { 159 if (iter != profile_notifications_.end()) {
54 // TODO(peter): Handle the "click" event for notifications which have 160 const Notification& notification = iter->second->notification();
55 // outlived the browser process that created them. 161 notification.delegate()->Click();
162
163 return true;
164 }
165
166 // If the Notification were not found, it may be a persistent notification
167 // that outlived the Chrome browser process. In this case, try to
168 // unserialize the notification's serialized data and trigger the click
169 // event manually.
170
171 std::vector<uint8> bytes;
172 base::android::JavaByteArrayToByteVector(env, serialized_notification_data,
173 &bytes);
174 if (!bytes.size())
175 return false;
176
177 content::PlatformNotificationData notification_data;
178 GURL origin;
179 int64 service_worker_registration_id;
180
181 Pickle pickle(reinterpret_cast<const char*>(&bytes[0]), bytes.size());
182 if (!UnserializePersistentNotification(pickle, &notification_data, &origin,
183 &service_worker_registration_id)) {
56 return false; 184 return false;
57 } 185 }
58 186
59 const Notification& notification = iter->second->notification(); 187 PlatformNotificationServiceImpl* service =
60 notification.delegate()->Click(); 188 PlatformNotificationServiceImpl::GetInstance();
189
190 // TODO(peter): Rather than assuming that the last used profile is the
191 // appropriate one for this notification, the used profile should be
192 // stored as part of the notification's data. See https://crbug.com/437574.
193 service->OnPersistentNotificationClick(
194 ProfileManager::GetLastUsedProfile(),
195 service_worker_registration_id,
196 id,
197 origin,
198 notification_data,
199 base::Bind(&OnEventDispatchComplete));
200
61 return true; 201 return true;
62 } 202 }
63 203
64 bool NotificationUIManagerAndroid::OnNotificationClosed( 204 bool NotificationUIManagerAndroid::OnNotificationClosed(
65 JNIEnv* env, jobject java_object, jstring notification_id) { 205 JNIEnv* env, jobject java_object, jstring notification_id) {
66 std::string id = ConvertJavaStringToUTF8(env, notification_id); 206 std::string id = ConvertJavaStringToUTF8(env, notification_id);
67 207
68 auto iter = profile_notifications_.find(id); 208 auto iter = profile_notifications_.find(id);
69 if (iter == profile_notifications_.end()) 209 if (iter == profile_notifications_.end())
70 return false; 210 return false;
(...skipping 24 matching lines...) Expand all
95 env, notification.message()); 235 env, notification.message());
96 ScopedJavaLocalRef<jstring> origin = ConvertUTF8ToJavaString( 236 ScopedJavaLocalRef<jstring> origin = ConvertUTF8ToJavaString(
97 env, notification.origin_url().GetOrigin().spec()); 237 env, notification.origin_url().GetOrigin().spec());
98 238
99 ScopedJavaLocalRef<jobject> icon; 239 ScopedJavaLocalRef<jobject> icon;
100 240
101 SkBitmap icon_bitmap = notification.icon().AsBitmap(); 241 SkBitmap icon_bitmap = notification.icon().AsBitmap();
102 if (!icon_bitmap.isNull()) 242 if (!icon_bitmap.isNull())
103 icon = gfx::ConvertToJavaBitmap(&icon_bitmap); 243 icon = gfx::ConvertToJavaBitmap(&icon_bitmap);
104 244
245 ScopedJavaLocalRef<jbyteArray> notification_data;
246 if (true /** is_persistent_notification **/) {
247 PersistentNotificationDelegate* delegate =
248 static_cast<PersistentNotificationDelegate*>(notification.delegate());
249 scoped_ptr<Pickle> pickle = SerializePersistentNotification(
250 delegate->notification_data(),
251 notification.origin_url(),
252 delegate->service_worker_registration_id());
253
254 if (!pickle) {
255 LOG(ERROR) <<
256 "Unable to serialize the notification, payload too large (max 1MB).";
257 RemoveProfileNotification(profile_notification);
258 return;
259 }
260
261 notification_data = base::android::ToJavaByteArray(
262 env, static_cast<const uint8*>(pickle->data()), pickle->size());
263 }
264
105 int platform_id = Java_NotificationUIManager_displayNotification( 265 int platform_id = Java_NotificationUIManager_displayNotification(
106 env, java_object_.obj(), id.obj(), title.obj(), body.obj(), icon.obj(), 266 env, java_object_.obj(), id.obj(), title.obj(), body.obj(), icon.obj(),
107 origin.obj()); 267 origin.obj(), notification_data.obj());
108 268
109 std::string notification_id = profile_notification->notification().id(); 269 std::string notification_id = profile_notification->notification().id();
110 platform_notifications_[notification_id] = platform_id; 270 platform_notifications_[notification_id] = platform_id;
111 271
112 notification.delegate()->Display(); 272 notification.delegate()->Display();
113 } 273 }
114 274
115 bool NotificationUIManagerAndroid::Update(const Notification& notification, 275 bool NotificationUIManagerAndroid::Update(const Notification& notification,
116 Profile* profile) { 276 Profile* profile) {
117 const base::string16& replace_id = notification.replace_id(); 277 const base::string16& replace_id = notification.replace_id();
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 432
273 ProfileNotification* NotificationUIManagerAndroid::FindProfileNotification( 433 ProfileNotification* NotificationUIManagerAndroid::FindProfileNotification(
274 const std::string& id) const { 434 const std::string& id) const {
275 auto iter = profile_notifications_.find(id); 435 auto iter = profile_notifications_.find(id);
276 if (iter == profile_notifications_.end()) 436 if (iter == profile_notifications_.end())
277 return nullptr; 437 return nullptr;
278 438
279 return iter->second; 439 return iter->second;
280 } 440 }
281 441
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698