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

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

Issue 1026853002: Integrate the notification database with the normal code path. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@n-db-ConfirmShow
Patch Set: android part Created 5 years, 8 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 <utility> 7 #include <utility>
8 8
9 #include "base/android/jni_array.h" 9 #include "base/android/jni_array.h"
10 #include "base/android/jni_string.h" 10 #include "base/android/jni_string.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/pickle.h" 12 #include "base/pickle.h"
johnme 2015/04/08 17:33:32 Unused now.
Peter Beverloo 2015/04/08 18:57:47 Done.
13 #include "base/strings/string_number_conversions.h"
13 #include "chrome/browser/browser_process.h" 14 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/notifications/notification.h" 15 #include "chrome/browser/notifications/notification.h"
15 #include "chrome/browser/notifications/persistent_notification_delegate.h" 16 #include "chrome/browser/notifications/persistent_notification_delegate.h"
16 #include "chrome/browser/notifications/platform_notification_service_impl.h" 17 #include "chrome/browser/notifications/platform_notification_service_impl.h"
17 #include "chrome/browser/notifications/profile_notification.h" 18 #include "chrome/browser/notifications/profile_notification.h"
18 #include "chrome/browser/profiles/profile_manager.h" 19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "content/public/browser/notification_event_dispatcher.h"
19 #include "content/public/common/persistent_notification_status.h" 21 #include "content/public/common/persistent_notification_status.h"
20 #include "content/public/common/platform_notification_data.h" 22 #include "content/public/common/platform_notification_data.h"
21 #include "jni/NotificationUIManager_jni.h" 23 #include "jni/NotificationUIManager_jni.h"
22 #include "ui/gfx/android/java_bitmap.h" 24 #include "ui/gfx/android/java_bitmap.h"
23 #include "ui/gfx/image/image.h" 25 #include "ui/gfx/image/image.h"
24 26
25 using base::android::AttachCurrentThread; 27 using base::android::AttachCurrentThread;
26 using base::android::ConvertJavaStringToUTF8; 28 using base::android::ConvertJavaStringToUTF8;
27 using base::android::ConvertUTF16ToJavaString; 29 using base::android::ConvertUTF16ToJavaString;
28 using base::android::ConvertUTF8ToJavaString; 30 using base::android::ConvertUTF8ToJavaString;
29 31
30 namespace { 32 namespace {
31 33
32 // The maximum size of the serialized pickle that carries a notification's meta
33 // information. Notifications carrying more data will be silently dropped - with
34 // an error being written to the log.
35 const int kMaximumSerializedNotificationSizeBytes = 1024 * 1024;
36
37 // Persistent notifications are likely to outlive the browser process they were
38 // created by on Android. In order to be able to re-surface the notification
39 // when the user interacts with them, all relevant notification data needs to
40 // be serialized with the notification itself.
41 //
42 // In the near future, as more features get added to Chrome's notification
43 // implementation, this will be done by storing the persistent notification data
44 // in a database. However, to support launching Chrome for Android from a
45 // notification event until that exists, serialize the data in the Intent.
46 //
47 // TODO(peter): Move towards storing notification data in a database rather than
48 // as a serialized Intent extra.
49
50 scoped_ptr<Pickle> SerializePersistentNotification(
51 const content::PlatformNotificationData& notification_data,
52 const GURL& origin,
53 int64 service_worker_registration_id) {
54 scoped_ptr<Pickle> pickle(new Pickle);
55
56 // content::PlatformNotificationData
57 pickle->WriteString16(notification_data.title);
58 pickle->WriteInt(static_cast<int>(notification_data.direction));
59 pickle->WriteString(notification_data.lang);
60 pickle->WriteString16(notification_data.body);
61 pickle->WriteString(notification_data.tag);
62 pickle->WriteString(notification_data.icon.spec());
63 pickle->WriteBool(notification_data.silent);
64
65 // The origin which is displaying the notification.
66 pickle->WriteString(origin.spec());
67
68 // The Service Worker registration this notification is associated with.
69 pickle->WriteInt64(service_worker_registration_id);
70
71 if (pickle->size() > kMaximumSerializedNotificationSizeBytes)
72 return nullptr;
73
74 return pickle.Pass();
75 }
76
77 bool UnserializePersistentNotification(
78 const Pickle& pickle,
79 content::PlatformNotificationData* notification_data,
80 GURL* origin,
81 int64* service_worker_registration_id) {
82 DCHECK(notification_data && origin && service_worker_registration_id);
83 PickleIterator iterator(pickle);
84
85 std::string icon_url, origin_url;
86 int direction_value;
87
88 // Unpack content::PlatformNotificationData.
89 if (!iterator.ReadString16(&notification_data->title) ||
90 !iterator.ReadInt(&direction_value) ||
91 !iterator.ReadString(&notification_data->lang) ||
92 !iterator.ReadString16(&notification_data->body) ||
93 !iterator.ReadString(&notification_data->tag) ||
94 !iterator.ReadString(&icon_url) ||
95 !iterator.ReadBool(&notification_data->silent)) {
96 return false;
97 }
98
99 notification_data->direction =
100 static_cast<content::PlatformNotificationData::NotificationDirection>(
101 direction_value);
102 notification_data->icon = GURL(icon_url);
103
104 // Unpack the origin which displayed this notification.
105 if (!iterator.ReadString(&origin_url))
106 return false;
107
108 *origin = GURL(origin_url);
109
110 // Unpack the Service Worker registration id.
111 if (!iterator.ReadInt64(service_worker_registration_id))
112 return false;
113
114 return true;
115 }
116
117 // Called when the "notificationclick" event in the Service Worker has finished 34 // Called when the "notificationclick" event in the Service Worker has finished
118 // executing for a notification that was created in a previous instance of the 35 // executing for a notification that was created in a previous instance of the
119 // browser. 36 // browser.
120 void OnEventDispatchComplete(content::PersistentNotificationStatus status) { 37 void OnEventDispatchComplete(content::PersistentNotificationStatus status) {
121 // TODO(peter): Add UMA statistics based on |status|. 38 // TODO(peter): Add UMA statistics based on |status|.
122 // TODO(peter): Decide if we want to forcefully shut down the browser process 39 // TODO(peter): Decide if we want to forcefully shut down the browser process
123 // if we're confident it was created for delivering this event. 40 // if we're confident it was created for delivering this event.
124 } 41 }
125 42
126 } // namespace 43 } // namespace
127 44
128 // Called by the Java side when a notification event has been received, but the 45 // Called by the Java side when a notification event has been received, but the
129 // NotificationUIManager has not been initialized yet. Enforce initialization of 46 // NotificationUIManager has not been initialized yet. Enforce initialization of
130 // the class. 47 // the class.
131 static void InitializeNotificationUIManager(JNIEnv* env, jclass clazz) { 48 static void InitializeNotificationUIManager(JNIEnv* env, jclass clazz) {
132 g_browser_process->notification_ui_manager(); 49 g_browser_process->notification_ui_manager();
133 } 50 }
134 51
135 // static 52 // static
136 NotificationUIManager* NotificationUIManager::Create(PrefService* local_state) { 53 NotificationUIManager* NotificationUIManager::Create(PrefService* local_state) {
137 return new NotificationUIManagerAndroid(); 54 return new NotificationUIManagerAndroid();
138 } 55 }
139 56
140 NotificationUIManagerAndroid::NotificationUIManagerAndroid() { 57 NotificationUIManagerAndroid::NotificationUIManagerAndroid() {
141 java_object_.Reset( 58 java_object_.Reset(
142 Java_NotificationUIManager_create( 59 Java_NotificationUIManager_create(
143 AttachCurrentThread(), 60 AttachCurrentThread(),
144 reinterpret_cast<intptr_t>(this), 61 reinterpret_cast<intptr_t>(this),
145 base::android::GetApplicationContext())); 62 base::android::GetApplicationContext()));
146
147 // TODO(peter): Synchronize notifications with the Java side.
148 } 63 }
149 64
150 NotificationUIManagerAndroid::~NotificationUIManagerAndroid() { 65 NotificationUIManagerAndroid::~NotificationUIManagerAndroid() {
151 Java_NotificationUIManager_destroy(AttachCurrentThread(), 66 Java_NotificationUIManager_destroy(AttachCurrentThread(),
152 java_object_.obj()); 67 java_object_.obj());
153 } 68 }
154 69
155 bool NotificationUIManagerAndroid::OnNotificationClicked( 70 bool NotificationUIManagerAndroid::OnNotificationClicked(
156 JNIEnv* env, 71 JNIEnv* env,
157 jobject java_object, 72 jobject java_object,
158 jstring notification_id, 73 jlong persistent_notification_id,
159 jbyteArray serialized_notification_data) { 74 jstring java_origin,
160 std::string id = ConvertJavaStringToUTF8(env, notification_id); 75 jstring java_tag) {
76 GURL origin(ConvertJavaStringToUTF8(env, java_origin));
77 std::string tag = ConvertJavaStringToUTF8(env, java_tag);
johnme 2015/04/08 17:33:32 Is java_tag definitely non-null here? ConvertJavaS
Peter Beverloo 2015/04/08 18:57:47 Yes.
161 78
162 auto iter = profile_notifications_.find(id); 79 regenerated_notification_infos_[persistent_notification_id] =
163 if (iter != profile_notifications_.end()) { 80 std::make_pair(origin.spec(), tag);
164 const Notification& notification = iter->second->notification();
165 notification.delegate()->Click();
166
167 return true;
168 }
169
170 // If the Notification were not found, it may be a persistent notification
171 // that outlived the Chrome browser process. In this case, try to
172 // unserialize the notification's serialized data and trigger the click
173 // event manually.
174
175 std::vector<uint8> bytes;
176 base::android::JavaByteArrayToByteVector(env, serialized_notification_data,
177 &bytes);
178 if (!bytes.size())
179 return false;
180
181 content::PlatformNotificationData notification_data;
182 GURL origin;
183 int64 service_worker_registration_id;
184
185 Pickle pickle(reinterpret_cast<const char*>(&bytes[0]), bytes.size());
186 if (!UnserializePersistentNotification(pickle, &notification_data, &origin,
187 &service_worker_registration_id)) {
188 return false;
189 }
190
191 // Store the tag and origin of this notification so that it can later be
192 // closed using these details.
193 regenerated_notification_infos_[id] =
194 std::make_pair(notification_data.tag, origin.spec());
195
196 PlatformNotificationServiceImpl* service =
197 PlatformNotificationServiceImpl::GetInstance();
198 81
199 // TODO(peter): Rather than assuming that the last used profile is the 82 // TODO(peter): Rather than assuming that the last used profile is the
200 // appropriate one for this notification, the used profile should be 83 // appropriate one for this notification, the used profile should be
201 // stored as part of the notification's data. See https://crbug.com/437574. 84 // stored as part of the notification's data. See https://crbug.com/437574.
202 service->OnPersistentNotificationClick( 85 content::NotificationEventDispatcher::GetInstance()
203 ProfileManager::GetLastUsedProfile(), 86 ->DispatchNotificationClickEvent(
204 service_worker_registration_id, 87 ProfileManager::GetLastUsedProfile(),
205 id, 88 static_cast<int64_t>(persistent_notification_id),
206 origin, 89 origin,
207 notification_data, 90 base::Bind(&OnEventDispatchComplete));
208 base::Bind(&OnEventDispatchComplete));
209 91
210 return true; 92 return true;
211 } 93 }
212 94
213 bool NotificationUIManagerAndroid::OnNotificationClosed( 95 bool NotificationUIManagerAndroid::OnNotificationClosed(
214 JNIEnv* env, jobject java_object, jstring notification_id) { 96 JNIEnv* env,
215 std::string id = ConvertJavaStringToUTF8(env, notification_id); 97 jobject java_object,
216 98 jlong persistent_notification_id,
217 auto iter = profile_notifications_.find(id); 99 jstring java_origin,
218 if (iter == profile_notifications_.end()) 100 jstring java_tag) {
219 return false; 101 // TODO(peter): Implement handling when a notification has been closed. The
johnme 2015/04/08 17:33:32 Do you have a follow-up patch for this? Ideally we
Peter Beverloo 2015/04/08 18:57:47 Not immediately. I already have a series of patch
220 102 // notification database has to reflect this in its own state.
221 const Notification& notification = iter->second->notification();
222 notification.delegate()->Close(true /** by_user **/);
223 RemoveProfileNotification(iter->second, true /* close */);
224 return true; 103 return true;
225 } 104 }
226 105
227 void NotificationUIManagerAndroid::Add(const Notification& notification, 106 void NotificationUIManagerAndroid::Add(const Notification& notification,
228 Profile* profile) { 107 Profile* profile) {
229 // If the given notification is replacing an older one, drop its associated 108 // If the given notification is replacing an older one, drop its associated
230 // profile notification object without closing the platform notification. 109 // profile notification object without closing the platform notification.
231 // We'll use the native Android system to perform a smoother replacement. 110 // We'll use the native Android system to perform a smoother replacement.
232 ProfileNotification* notification_to_replace = 111 ProfileNotification* notification_to_replace =
233 FindNotificationToReplace(notification, profile); 112 FindNotificationToReplace(notification, profile);
234 if (notification_to_replace) 113 if (notification_to_replace)
235 RemoveProfileNotification(notification_to_replace, false /* close */); 114 RemoveProfileNotification(notification_to_replace, false /* close */);
236 115
237 ProfileNotification* profile_notification = 116 ProfileNotification* profile_notification =
238 new ProfileNotification(profile, notification); 117 new ProfileNotification(profile, notification);
239 118
240 // Takes ownership of |profile_notification|. 119 // Takes ownership of |profile_notification|.
241 AddProfileNotification(profile_notification); 120 AddProfileNotification(profile_notification);
242 121
243 JNIEnv* env = AttachCurrentThread(); 122 JNIEnv* env = AttachCurrentThread();
244 123
124 GURL origin_url(notification.origin_url().GetOrigin());
johnme 2015/04/08 17:33:32 Micro-nit: move this after `int64_t persistent_not
Peter Beverloo 2015/04/08 18:57:47 Done.
125
126 PersistentNotificationDelegate* delegate =
127 static_cast<PersistentNotificationDelegate*>(notification.delegate());
128 DCHECK(delegate);
129
130 int64_t persistent_notification_id = delegate->persistent_notification_id();
131
132 ScopedJavaLocalRef<jstring> origin = ConvertUTF8ToJavaString(
133 env, origin_url.spec());
245 ScopedJavaLocalRef<jstring> tag = 134 ScopedJavaLocalRef<jstring> tag =
246 ConvertUTF8ToJavaString(env, notification.tag()); 135 ConvertUTF8ToJavaString(env, notification.tag());
247 ScopedJavaLocalRef<jstring> id = ConvertUTF8ToJavaString(
248 env, profile_notification->notification().id());
249 ScopedJavaLocalRef<jstring> title = ConvertUTF16ToJavaString( 136 ScopedJavaLocalRef<jstring> title = ConvertUTF16ToJavaString(
250 env, notification.title()); 137 env, notification.title());
251 ScopedJavaLocalRef<jstring> body = ConvertUTF16ToJavaString( 138 ScopedJavaLocalRef<jstring> body = ConvertUTF16ToJavaString(
252 env, notification.message()); 139 env, notification.message());
253 ScopedJavaLocalRef<jstring> origin = ConvertUTF8ToJavaString(
254 env, notification.origin_url().GetOrigin().spec());
255 140
256 ScopedJavaLocalRef<jobject> icon; 141 ScopedJavaLocalRef<jobject> icon;
257 142
258 SkBitmap icon_bitmap = notification.icon().AsBitmap(); 143 SkBitmap icon_bitmap = notification.icon().AsBitmap();
259 if (!icon_bitmap.isNull()) 144 if (!icon_bitmap.isNull())
260 icon = gfx::ConvertToJavaBitmap(&icon_bitmap); 145 icon = gfx::ConvertToJavaBitmap(&icon_bitmap);
261 146
262 ScopedJavaLocalRef<jbyteArray> notification_data;
263 if (true /** is_persistent_notification **/) {
264 PersistentNotificationDelegate* delegate =
265 static_cast<PersistentNotificationDelegate*>(notification.delegate());
266 scoped_ptr<Pickle> pickle = SerializePersistentNotification(
267 delegate->notification_data(),
268 notification.origin_url(),
269 delegate->service_worker_registration_id());
270
271 if (!pickle) {
272 LOG(ERROR) <<
273 "Unable to serialize the notification, payload too large (max 1MB).";
274 RemoveProfileNotification(profile_notification, true /* close */);
275 return;
276 }
277
278 notification_data = base::android::ToJavaByteArray(
279 env, static_cast<const uint8*>(pickle->data()), pickle->size());
280 }
281
282 Java_NotificationUIManager_displayNotification( 147 Java_NotificationUIManager_displayNotification(
283 env, 148 env,
284 java_object_.obj(), 149 java_object_.obj(),
150 persistent_notification_id,
151 origin.obj(),
285 tag.obj(), 152 tag.obj(),
286 id.obj(),
287 title.obj(), 153 title.obj(),
288 body.obj(), 154 body.obj(),
289 icon.obj(), 155 icon.obj(),
290 origin.obj(), 156 notification.silent());
291 notification.silent(),
292 notification_data.obj());
293 157
294 regenerated_notification_infos_[profile_notification->notification().id()] = 158 regenerated_notification_infos_[persistent_notification_id] =
295 std::make_pair(notification.tag(), 159 std::make_pair(origin_url.spec(), notification.tag());
296 notification.origin_url().GetOrigin().spec());
297 160
298 notification.delegate()->Display(); 161 notification.delegate()->Display();
299 } 162 }
300 163
301 bool NotificationUIManagerAndroid::Update(const Notification& notification, 164 bool NotificationUIManagerAndroid::Update(const Notification& notification,
302 Profile* profile) { 165 Profile* profile) {
303 // This method is currently only called from extensions and local discovery, 166 // This method is currently only called from extensions and local discovery,
304 // both of which are not supported on Android. 167 // both of which are not supported on Android.
305 NOTIMPLEMENTED(); 168 NOTIMPLEMENTED();
306 return false; 169 return false;
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 264
402 profile_notifications_.clear(); 265 profile_notifications_.clear();
403 } 266 }
404 267
405 bool NotificationUIManagerAndroid::RegisterNotificationUIManager(JNIEnv* env) { 268 bool NotificationUIManagerAndroid::RegisterNotificationUIManager(JNIEnv* env) {
406 return RegisterNativesImpl(env); 269 return RegisterNativesImpl(env);
407 } 270 }
408 271
409 void NotificationUIManagerAndroid::PlatformCloseNotification( 272 void NotificationUIManagerAndroid::PlatformCloseNotification(
410 const std::string& notification_id) { 273 const std::string& notification_id) {
411 auto iterator = regenerated_notification_infos_.find(notification_id); 274 int64_t persistent_notification_id = 0;
275 if (!base::StringToInt64(notification_id, &persistent_notification_id))
johnme 2015/04/08 17:33:32 How does this work? Isn't notification_id here a d
Peter Beverloo 2015/04/08 18:57:47 This was broken before this patch as well :(. As s
johnme 2015/04/09 10:06:11 Hmm, so this patch breaks PlatformNotificationServ
276 return;
277
278 const auto iterator =
279 regenerated_notification_infos_.find(persistent_notification_id);
412 if (iterator == regenerated_notification_infos_.end()) 280 if (iterator == regenerated_notification_infos_.end())
413 return; 281 return;
414 282
415 RegeneratedNotificationInfo notification_info = iterator->second; 283 RegeneratedNotificationInfo notification_info = iterator->second;
416 JNIEnv* env = AttachCurrentThread(); 284 JNIEnv* env = AttachCurrentThread();
417 285
286 ScopedJavaLocalRef<jstring> origin =
287 ConvertUTF8ToJavaString(env, notification_info.first);
418 ScopedJavaLocalRef<jstring> tag = 288 ScopedJavaLocalRef<jstring> tag =
419 ConvertUTF8ToJavaString(env, notification_info.first);
420 ScopedJavaLocalRef<jstring> origin =
421 ConvertUTF8ToJavaString(env, notification_info.second); 289 ConvertUTF8ToJavaString(env, notification_info.second);
422 ScopedJavaLocalRef<jstring> java_notification_id =
423 ConvertUTF8ToJavaString(env, notification_id);
424 290
425 regenerated_notification_infos_.erase(notification_id); 291 regenerated_notification_infos_.erase(iterator);
426 292
427 Java_NotificationUIManager_closeNotification( 293 Java_NotificationUIManager_closeNotification(env,
428 env, java_object_.obj(), tag.obj(), java_notification_id.obj(), 294 java_object_.obj(),
429 origin.obj()); 295 persistent_notification_id,
296 origin.obj(),
297 tag.obj());
430 } 298 }
431 299
432 void NotificationUIManagerAndroid::AddProfileNotification( 300 void NotificationUIManagerAndroid::AddProfileNotification(
433 ProfileNotification* profile_notification) { 301 ProfileNotification* profile_notification) {
434 std::string id = profile_notification->notification().id(); 302 std::string id = profile_notification->notification().id();
435 303
436 // Notification ids should be unique. 304 // Notification ids should be unique.
437 DCHECK(profile_notifications_.find(id) == profile_notifications_.end()); 305 DCHECK(profile_notifications_.find(id) == profile_notifications_.end());
438 306
439 profile_notifications_[id] = profile_notification; 307 profile_notifications_[id] = profile_notification;
(...skipping 30 matching lines...) Expand all
470 for (const auto& iterator : profile_notifications_) { 338 for (const auto& iterator : profile_notifications_) {
471 ProfileNotification* profile_notification = iterator.second; 339 ProfileNotification* profile_notification = iterator.second;
472 if (profile_notification->notification().tag() == tag || 340 if (profile_notification->notification().tag() == tag ||
473 profile_notification->notification().origin_url() == origin_url || 341 profile_notification->notification().origin_url() == origin_url ||
474 profile_notification->profile() == profile) { 342 profile_notification->profile() == profile) {
475 return profile_notification; 343 return profile_notification;
476 } 344 }
477 } 345 }
478 return nullptr; 346 return nullptr;
479 } 347 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698