1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/api/alarms/alarm_manager.h" 6 7 #include "base/bind.h" 8 #include "base/json/json_writer.h" 9 #include "base/lazy_instance.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/time/clock.h" 12 #include "base/time/default_clock.h" 13 #include "base/time/time.h" 14 #include "base/value_conversions.h" 15 #include "base/values.h" 16 #include "chrome/browser/chrome_notification_types.h" 17 #include "chrome/browser/extensions/event_router.h" 18 #include "chrome/browser/extensions/extension_service.h" 19 #include "chrome/browser/extensions/extension_system.h" 20 #include "chrome/browser/extensions/state_store.h" 21 #include "chrome/browser/profiles/profile.h" 22 #include "content/public/browser/notification_service.h" 23 24 namespace extensions { 25 26 namespace { 27 28 const char kOnAlarmEvent[] = "alarms.onAlarm"; 29 30 // A list of alarms that this extension has set. 31 const char kRegisteredAlarms[] = "alarms"; 32 const char kAlarmGranularity[] = "granularity"; 33 34 // The minimum period between polling for alarms to run. 35 const base::TimeDelta kDefaultMinPollPeriod = base::TimeDelta::FromDays(1); 36 37 class DefaultAlarmDelegate : public AlarmManager::Delegate { 38 public: 39 explicit DefaultAlarmDelegate(Profile* profile) : profile_(profile) {} 40 virtual ~DefaultAlarmDelegate() {} 41 42 virtual void OnAlarm(const std::string& extension_id, 43 const Alarm& alarm) OVERRIDE { 44 scoped_ptr<base::ListValue> args(new base::ListValue()); 45 args->Append(alarm.js_alarm->ToValue().release()); 46 scoped_ptr<Event> event(new Event(kOnAlarmEvent, args.Pass())); 47 ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension( 48 extension_id, event.Pass()); 49 } 50 51 private: 52 Profile* profile_; 53 }; 54 55 // Creates a TimeDelta from a delay as specified in the API. 56 base::TimeDelta TimeDeltaFromDelay(double delay_in_minutes) { 57 return base::TimeDelta::FromMicroseconds( 58 delay_in_minutes * base::Time::kMicrosecondsPerMinute); 59 } 60 61 std::vector<Alarm> AlarmsFromValue(const base::ListValue* list) { 62 std::vector<Alarm> alarms; 63 for (size_t i = 0; i < list->GetSize(); ++i) { 64 const base::DictionaryValue* alarm_dict = NULL; 65 Alarm alarm; 66 if (list->GetDictionary(i, &alarm_dict) && 67 api::alarms::Alarm::Populate(*alarm_dict, alarm.js_alarm.get())) { 68 const base::Value* time_value = NULL; 69 if (alarm_dict->Get(kAlarmGranularity, &time_value)) 70 base::GetValueAsTimeDelta(*time_value, &alarm.granularity); 71 alarms.push_back(alarm); 72 } 73 } 74 return alarms; 75 } 76 77 scoped_ptr<base::ListValue> AlarmsToValue(const std::vector<Alarm>& alarms) { 78 scoped_ptr<base::ListValue> list(new base::ListValue()); 79 for (size_t i = 0; i < alarms.size(); ++i) { 80 scoped_ptr<base::DictionaryValue> alarm = 81 alarms[i].js_alarm->ToValue().Pass(); 82 alarm->Set(kAlarmGranularity, 83 base::CreateTimeDeltaValue(alarms[i].granularity)); 84 list->Append(alarm.release()); 85 } 86 return list.Pass(); 87 } 88 89 90 } // namespace 91 92 // AlarmManager 93 94 AlarmManager::AlarmManager(Profile* profile) 95 : profile_(profile), 96 clock_(new base::DefaultClock()), 97 delegate_(new DefaultAlarmDelegate(profile)) { 98 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, 99 content::Source<Profile>(profile_)); 100 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED, 101 content::Source<Profile>(profile_)); 102 103 StateStore* storage = ExtensionSystem::Get(profile_)->state_store(); 104 if (storage) 105 storage->RegisterKey(kRegisteredAlarms); 106 } 107 108 AlarmManager::~AlarmManager() { 109 } 110 111 void AlarmManager::AddAlarm(const std::string& extension_id, 112 const Alarm& alarm) { 113 AddAlarmImpl(extension_id, alarm); 114 WriteToStorage(extension_id); 115 } 116 117 const Alarm* AlarmManager::GetAlarm( 118 const std::string& extension_id, const std::string& name) { 119 AlarmIterator it = GetAlarmIterator(extension_id, name); 120 if (it.first == alarms_.end()) 121 return NULL; 122 return &*it.second; 123 } 124 125 const AlarmManager::AlarmList* AlarmManager::GetAllAlarms( 126 const std::string& extension_id) { 127 AlarmMap::iterator list = alarms_.find(extension_id); 128 if (list == alarms_.end()) 129 return NULL; 130 return &list->second; 131 } 132 133 AlarmManager::AlarmIterator AlarmManager::GetAlarmIterator( 134 const std::string& extension_id, const std::string& name) { 135 AlarmMap::iterator list = alarms_.find(extension_id); 136 if (list == alarms_.end()) 137 return make_pair(alarms_.end(), AlarmList::iterator()); 138 139 for (AlarmList::iterator it = list->second.begin(); 140 it != list->second.end(); ++it) { 141 if (it->js_alarm->name == name) 142 return make_pair(list, it); 143 } 144 145 return make_pair(alarms_.end(), AlarmList::iterator()); 146 } 147 148 bool AlarmManager::RemoveAlarm(const std::string& extension_id, 149 const std::string& name) { 150 AlarmIterator it = GetAlarmIterator(extension_id, name); 151 if (it.first == alarms_.end()) 152 return false; 153 154 RemoveAlarmIterator(it); 155 WriteToStorage(extension_id); 156 return true; 157 } 158 159 void AlarmManager::RemoveAllAlarms(const std::string& extension_id) { 160 AlarmMap::iterator list = alarms_.find(extension_id); 161 if (list == alarms_.end()) 162 return; 163 164 // Note: I'm using indices rather than iterators here because 165 // RemoveAlarmIterator will delete the list when it becomes empty. 166 for (size_t i = 0, size = list->second.size(); i < size; ++i) 167 RemoveAlarmIterator(AlarmIterator(list, list->second.begin())); 168 169 CHECK(alarms_.find(extension_id) == alarms_.end()); 170 WriteToStorage(extension_id); 171 } 172 173 void AlarmManager::SetClockForTesting(base::Clock* clock) { 174 clock_.reset(clock); 175 } 176 177 static base::LazyInstance<ProfileKeyedAPIFactory<AlarmManager> > 178 g_factory = LAZY_INSTANCE_INITIALIZER; 179 180 // static 181 ProfileKeyedAPIFactory<AlarmManager>* AlarmManager::GetFactoryInstance() { 182 return &g_factory.Get(); 183 } 184 185 // static 186 AlarmManager* AlarmManager::Get(Profile* profile) { 187 return ProfileKeyedAPIFactory<AlarmManager>::GetForProfile(profile); 188 } 189 190 void AlarmManager::RemoveAlarmIterator(const AlarmIterator& iter) { 191 AlarmList& list = iter.first->second; 192 list.erase(iter.second); 193 if (list.empty()) 194 alarms_.erase(iter.first); 195 196 // Cancel the timer if there are no more alarms. 197 // We don't need to reschedule the poll otherwise, because in 198 // the worst case we would just poll one extra time. 199 if (alarms_.empty()) 200 timer_.Stop(); 201 } 202 203 void AlarmManager::OnAlarm(AlarmIterator it) { 204 CHECK(it.first != alarms_.end()); 205 Alarm& alarm = *it.second; 206 std::string extension_id_copy(it.first->first); 207 delegate_->OnAlarm(extension_id_copy, alarm); 208 209 // Update our scheduled time for the next alarm. 210 if (double* period_in_minutes = 211 alarm.js_alarm->period_in_minutes.get()) { 212 alarm.js_alarm->scheduled_time = 213 (last_poll_time_ + 214 TimeDeltaFromDelay(*period_in_minutes)).ToJsTime(); 215 } else { 216 RemoveAlarmIterator(it); 217 } 218 WriteToStorage(extension_id_copy); 219 } 220 221 void AlarmManager::AddAlarmImpl(const std::string& extension_id, 222 const Alarm& alarm) { 223 // Override any old alarm with the same name. 224 AlarmIterator old_alarm = GetAlarmIterator(extension_id, 225 alarm.js_alarm->name); 226 if (old_alarm.first != alarms_.end()) 227 RemoveAlarmIterator(old_alarm); 228 229 alarms_[extension_id].push_back(alarm); 230 231 ScheduleNextPoll(); 232 } 233 234 void AlarmManager::WriteToStorage(const std::string& extension_id) { 235 StateStore* storage = ExtensionSystem::Get(profile_)->state_store(); 236 if (!storage) 237 return; 238 239 scoped_ptr<base::Value> alarms; 240 AlarmMap::iterator list = alarms_.find(extension_id); 241 if (list != alarms_.end()) 242 alarms.reset(AlarmsToValue(list->second).release()); 243 else 244 alarms.reset(AlarmsToValue(std::vector<Alarm>()).release()); 245 storage->SetExtensionValue(extension_id, kRegisteredAlarms, alarms.Pass()); 246 } 247 248 void AlarmManager::ReadFromStorage(const std::string& extension_id, 249 scoped_ptr<base::Value> value) { 250 base::ListValue* list = NULL; 251 if (!value.get() || !value->GetAsList(&list)) 252 return; 253 254 std::vector<Alarm> alarm_states = AlarmsFromValue(list); 255 for (size_t i = 0; i < alarm_states.size(); ++i) { 256 AddAlarmImpl(extension_id, alarm_states[i]); 257 } 258 } 259 260 void AlarmManager::ScheduleNextPoll() { 261 // 0. If there are no alarms, stop the timer. 262 if (alarms_.empty()) { 263 timer_.Stop(); 264 return; 265 } 266 267 // TODO(yoz): Try not to reschedule every single time if we're adding 268 // a lot of alarms. 269 270 // Find the soonest alarm that is scheduled to run and the smallest 271 // granularity of any alarm. 272 // alarms_ guarantees that none of its contained lists are empty. 273 base::Time soonest_alarm_time = base::Time::FromJsTime( 274 alarms_.begin()->second.begin()->js_alarm->scheduled_time); 275 base::TimeDelta min_granularity = kDefaultMinPollPeriod; 276 for (AlarmMap::const_iterator m_it = alarms_.begin(), m_end = alarms_.end(); 277 m_it != m_end; ++m_it) { 278 for (AlarmList::const_iterator l_it = m_it->second.begin(); 279 l_it != m_it->second.end(); ++l_it) { 280 base::Time cur_alarm_time = 281 base::Time::FromJsTime(l_it->js_alarm->scheduled_time); 282 if (cur_alarm_time < soonest_alarm_time) 283 soonest_alarm_time = cur_alarm_time; 284 if (l_it->granularity < min_granularity) 285 min_granularity = l_it->granularity; 286 } 287 } 288 289 base::Time next_poll(last_poll_time_ + min_granularity); 290 // If the next alarm is more than min_granularity in the future, wait for it. 291 // Otherwise, only poll as often as min_granularity. 292 // As a special case, if we've never checked for an alarm before 293 // (e.g. during startup), let alarms fire asap. 294 if (last_poll_time_.is_null() || next_poll < soonest_alarm_time) 295 next_poll = soonest_alarm_time; 296 297 // Schedule the poll. 298 next_poll_time_ = next_poll; 299 base::TimeDelta delay = std::max(base::TimeDelta::FromSeconds(0), 300 next_poll - clock_->Now()); 301 timer_.Start(FROM_HERE, 302 delay, 303 this, 304 &AlarmManager::PollAlarms); 305 } 306 307 void AlarmManager::PollAlarms() { 308 last_poll_time_ = clock_->Now(); 309 310 // Run any alarms scheduled in the past. OnAlarm uses vector::erase to remove 311 // elements from the AlarmList, and map::erase to remove AlarmLists from the 312 // AlarmMap. 313 for (AlarmMap::iterator m_it = alarms_.begin(), m_end = alarms_.end(); 314 m_it != m_end;) { 315 AlarmMap::iterator cur_extension = m_it++; 316 317 // Iterate (a) backwards so that removing elements doesn't affect 318 // upcoming iterations, and (b) with indices so that if the last 319 // iteration destroys the AlarmList, I'm not about to use the end 320 // iterator that the destruction invalidates. 321 for (size_t i = cur_extension->second.size(); i > 0; --i) { 322 AlarmList::iterator cur_alarm = cur_extension->second.begin() + i - 1; 323 if (base::Time::FromJsTime(cur_alarm->js_alarm->scheduled_time) <= 324 next_poll_time_) { 325 OnAlarm(make_pair(cur_extension, cur_alarm)); 326 } 327 } 328 } 329 330 ScheduleNextPoll(); 331 } 332 333 void AlarmManager::Observe( 334 int type, 335 const content::NotificationSource& source, 336 const content::NotificationDetails& details) { 337 switch (type) { 338 case chrome::NOTIFICATION_EXTENSION_LOADED: { 339 const Extension* extension = 340 content::Details<const Extension>(details).ptr(); 341 StateStore* storage = ExtensionSystem::Get(profile_)->state_store(); 342 if (storage) { 343 storage->GetExtensionValue(extension->id(), kRegisteredAlarms, 344 base::Bind(&AlarmManager::ReadFromStorage, 345 AsWeakPtr(), extension->id())); 346 } 347 break; 348 } 349 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: { 350 const Extension* extension = 351 content::Details<const Extension>(details).ptr(); 352 RemoveAllAlarms(extension->id()); 353 break; 354 } 355 default: 356 NOTREACHED(); 357 break; 358 } 359 } 360 361 // AlarmManager::Alarm 362 363 Alarm::Alarm() 364 : js_alarm(new api::alarms::Alarm()) { 365 } 366 367 Alarm::Alarm(const std::string& name, 368 const api::alarms::AlarmCreateInfo& create_info, 369 base::TimeDelta min_granularity, 370 base::Time now) 371 : js_alarm(new api::alarms::Alarm()) { 372 js_alarm->name = name; 373 374 if (create_info.when.get()) { 375 // Absolute scheduling. 376 js_alarm->scheduled_time = *create_info.when; 377 granularity = base::Time::FromJsTime(js_alarm->scheduled_time) - now; 378 } else { 379 // Relative scheduling. 380 double* delay_in_minutes = create_info.delay_in_minutes.get(); 381 if (delay_in_minutes == NULL) 382 delay_in_minutes = create_info.period_in_minutes.get(); 383 CHECK(delay_in_minutes != NULL) 384 << "ValidateAlarmCreateInfo in alarms_api.cc should have " 385 << "prevented this call."; 386 base::TimeDelta delay = TimeDeltaFromDelay(*delay_in_minutes); 387 js_alarm->scheduled_time = (now + delay).ToJsTime(); 388 granularity = delay; 389 } 390 391 if (granularity < min_granularity) 392 granularity = min_granularity; 393 394 // Check for repetition. 395 if (create_info.period_in_minutes.get()) { 396 js_alarm->period_in_minutes.reset( 397 new double(*create_info.period_in_minutes)); 398 } 399 } 400 401 Alarm::~Alarm() { 402 } 403 404 } // namespace extensions 405