app_list_service_impl.cc revision 010d83a9304c5a91596085d917d248abff47903a
1// Copyright 2013 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/ui/app_list/app_list_service_impl.h" 6 7#include <string> 8 9#include "apps/pref_names.h" 10#include "base/bind.h" 11#include "base/command_line.h" 12#include "base/metrics/histogram.h" 13#include "base/prefs/pref_service.h" 14#include "base/strings/string16.h" 15#include "base/time/time.h" 16#include "chrome/browser/apps/shortcut_manager.h" 17#include "chrome/browser/apps/shortcut_manager_factory.h" 18#include "chrome/browser/browser_process.h" 19#include "chrome/browser/browser_shutdown.h" 20#include "chrome/browser/profiles/profile_manager.h" 21#include "chrome/browser/ui/app_list/profile_loader.h" 22#include "chrome/browser/ui/app_list/profile_store.h" 23#include "chrome/common/chrome_constants.h" 24#include "chrome/common/chrome_switches.h" 25#include "chrome/common/pref_names.h" 26 27namespace { 28 29const int kDiscoverabilityTimeoutMinutes = 60; 30 31void SendAppListAppLaunch(int count) { 32 UMA_HISTOGRAM_CUSTOM_COUNTS( 33 "Apps.AppListDailyAppLaunches", count, 1, 1000, 50); 34 if (count > 0) 35 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppToday", 1, 2); 36} 37 38void SendAppListLaunch(int count) { 39 UMA_HISTOGRAM_CUSTOM_COUNTS( 40 "Apps.AppListDailyLaunches", count, 1, 1000, 50); 41 if (count > 0) 42 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppListToday", 1, 2); 43} 44 45bool SendDailyEventFrequency( 46 const char* last_ping_pref, 47 const char* count_pref, 48 void (*send_callback)(int count)) { 49 PrefService* local_state = g_browser_process->local_state(); 50 51 base::Time now = base::Time::Now(); 52 base::Time last = base::Time::FromInternalValue(local_state->GetInt64( 53 last_ping_pref)); 54 int days = (now - last).InDays(); 55 if (days > 0) { 56 send_callback(local_state->GetInteger(count_pref)); 57 local_state->SetInt64( 58 last_ping_pref, 59 (last + base::TimeDelta::FromDays(days)).ToInternalValue()); 60 local_state->SetInteger(count_pref, 0); 61 return true; 62 } 63 return false; 64} 65 66void RecordDailyEventFrequency( 67 const char* last_ping_pref, 68 const char* count_pref, 69 void (*send_callback)(int count)) { 70 if (!g_browser_process) 71 return; // In a unit test. 72 73 PrefService* local_state = g_browser_process->local_state(); 74 if (!local_state) 75 return; // In a unit test. 76 77 int count = local_state->GetInteger(count_pref); 78 local_state->SetInteger(count_pref, count + 1); 79 if (SendDailyEventFrequency(last_ping_pref, count_pref, send_callback)) { 80 local_state->SetInteger(count_pref, 1); 81 } 82} 83 84class ProfileStoreImpl : public ProfileStore { 85 public: 86 explicit ProfileStoreImpl(ProfileManager* profile_manager) 87 : profile_manager_(profile_manager), 88 weak_factory_(this) { 89 } 90 91 virtual void AddProfileObserver(ProfileInfoCacheObserver* observer) OVERRIDE { 92 profile_manager_->GetProfileInfoCache().AddObserver(observer); 93 } 94 95 virtual void LoadProfileAsync( 96 const base::FilePath& path, 97 base::Callback<void(Profile*)> callback) OVERRIDE { 98 profile_manager_->CreateProfileAsync( 99 path, 100 base::Bind(&ProfileStoreImpl::OnProfileCreated, 101 weak_factory_.GetWeakPtr(), 102 callback), 103 base::string16(), 104 base::string16(), 105 std::string()); 106 } 107 108 void OnProfileCreated(base::Callback<void(Profile*)> callback, 109 Profile* profile, 110 Profile::CreateStatus status) { 111 switch (status) { 112 case Profile::CREATE_STATUS_CREATED: 113 break; 114 case Profile::CREATE_STATUS_INITIALIZED: 115 callback.Run(profile); 116 break; 117 case Profile::CREATE_STATUS_LOCAL_FAIL: 118 case Profile::CREATE_STATUS_REMOTE_FAIL: 119 case Profile::CREATE_STATUS_CANCELED: 120 break; 121 case Profile::MAX_CREATE_STATUS: 122 NOTREACHED(); 123 break; 124 } 125 } 126 127 virtual Profile* GetProfileByPath(const base::FilePath& path) OVERRIDE { 128 return profile_manager_->GetProfileByPath(path); 129 } 130 131 virtual base::FilePath GetUserDataDir() OVERRIDE { 132 return profile_manager_->user_data_dir(); 133 } 134 135 virtual bool IsProfileManaged(const base::FilePath& profile_path) OVERRIDE { 136 ProfileInfoCache& profile_info = 137 g_browser_process->profile_manager()->GetProfileInfoCache(); 138 size_t profile_index = profile_info.GetIndexOfProfileWithPath(profile_path); 139 return profile_info.ProfileIsManagedAtIndex(profile_index); 140 } 141 142 private: 143 ProfileManager* profile_manager_; 144 base::WeakPtrFactory<ProfileStoreImpl> weak_factory_; 145}; 146 147void RecordAppListDiscoverability(PrefService* local_state, 148 bool is_startup_check) { 149 // Since this task may be delayed, ensure it does not interfere with shutdown 150 // when they unluckily coincide. 151 if (browser_shutdown::IsTryingToQuit()) 152 return; 153 154 int64 enable_time_value = local_state->GetInt64(prefs::kAppListEnableTime); 155 if (enable_time_value == 0) 156 return; // Already recorded or never enabled. 157 158 base::Time app_list_enable_time = 159 base::Time::FromInternalValue(enable_time_value); 160 if (is_startup_check) { 161 // When checking at startup, only clear and record the "timeout" case, 162 // otherwise wait for a timeout. 163 base::TimeDelta time_remaining = 164 app_list_enable_time + 165 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes) - 166 base::Time::Now(); 167 if (time_remaining > base::TimeDelta()) { 168 base::MessageLoop::current()->PostDelayedTask( 169 FROM_HERE, 170 base::Bind(&RecordAppListDiscoverability, 171 base::Unretained(local_state), 172 false), 173 time_remaining); 174 return; 175 } 176 } 177 178 local_state->SetInt64(prefs::kAppListEnableTime, 0); 179 180 AppListService::AppListEnableSource enable_source = 181 static_cast<AppListService::AppListEnableSource>( 182 local_state->GetInteger(prefs::kAppListEnableMethod)); 183 if (enable_source == AppListService::ENABLE_FOR_APP_INSTALL) { 184 base::TimeDelta time_taken = base::Time::Now() - app_list_enable_time; 185 // This means the user "discovered" the app launcher naturally, after it was 186 // enabled on the first app install. Record how long it took to discover. 187 // Note that the last bucket is essentially "not discovered": subtract 1 188 // minute to account for clock inaccuracy. 189 UMA_HISTOGRAM_CUSTOM_TIMES( 190 "Apps.AppListTimeToDiscover", 191 time_taken, 192 base::TimeDelta::FromSeconds(1), 193 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes - 1), 194 10 /* bucket_count */); 195 } 196 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHowEnabled", 197 enable_source, 198 AppListService::ENABLE_NUM_ENABLE_SOURCES); 199} 200 201} // namespace 202 203void AppListServiceImpl::RecordAppListLaunch() { 204 RecordDailyEventFrequency(prefs::kLastAppListLaunchPing, 205 prefs::kAppListLaunchCount, 206 &SendAppListLaunch); 207 RecordAppListDiscoverability(local_state_, false); 208} 209 210// static 211void AppListServiceImpl::RecordAppListAppLaunch() { 212 RecordDailyEventFrequency(prefs::kLastAppListAppLaunchPing, 213 prefs::kAppListAppLaunchCount, 214 &SendAppListAppLaunch); 215} 216 217// static 218void AppListServiceImpl::SendAppListStats() { 219 if (!g_browser_process || g_browser_process->IsShuttingDown()) 220 return; 221 222 SendDailyEventFrequency(prefs::kLastAppListLaunchPing, 223 prefs::kAppListLaunchCount, 224 &SendAppListLaunch); 225 SendDailyEventFrequency(prefs::kLastAppListAppLaunchPing, 226 prefs::kAppListAppLaunchCount, 227 &SendAppListAppLaunch); 228} 229 230AppListServiceImpl::AppListServiceImpl() 231 : profile_store_( 232 new ProfileStoreImpl(g_browser_process->profile_manager())), 233 weak_factory_(this), 234 command_line_(*CommandLine::ForCurrentProcess()), 235 local_state_(g_browser_process->local_state()), 236 profile_loader_(new ProfileLoader(profile_store_.get())) { 237 profile_store_->AddProfileObserver(this); 238} 239 240AppListServiceImpl::AppListServiceImpl(const CommandLine& command_line, 241 PrefService* local_state, 242 scoped_ptr<ProfileStore> profile_store) 243 : profile_store_(profile_store.Pass()), 244 weak_factory_(this), 245 command_line_(command_line), 246 local_state_(local_state), 247 profile_loader_(new ProfileLoader(profile_store_.get())) { 248 profile_store_->AddProfileObserver(this); 249} 250 251AppListServiceImpl::~AppListServiceImpl() {} 252 253void AppListServiceImpl::SetAppListNextPaintCallback(void (*callback)()) {} 254 255void AppListServiceImpl::HandleFirstRun() {} 256 257void AppListServiceImpl::Init(Profile* initial_profile) {} 258 259base::FilePath AppListServiceImpl::GetProfilePath( 260 const base::FilePath& user_data_dir) { 261 std::string app_list_profile; 262 if (local_state_->HasPrefPath(prefs::kAppListProfile)) 263 app_list_profile = local_state_->GetString(prefs::kAppListProfile); 264 265 // If the user has no profile preference for the app launcher, default to the 266 // last browser profile used. 267 if (app_list_profile.empty() && 268 local_state_->HasPrefPath(prefs::kProfileLastUsed)) { 269 app_list_profile = local_state_->GetString(prefs::kProfileLastUsed); 270 } 271 272 // If there is no last used profile recorded, use the initial profile. 273 if (app_list_profile.empty()) 274 app_list_profile = chrome::kInitialProfile; 275 276 return user_data_dir.AppendASCII(app_list_profile); 277} 278 279void AppListServiceImpl::SetProfilePath(const base::FilePath& profile_path) { 280 // Ensure we don't set the pref to a managed user's profile path. 281 // TODO(calamity): Filter out managed profiles from the settings app so this 282 // can't get hit, so we can remove it. 283 if (profile_store_->IsProfileManaged(profile_path)) 284 return; 285 286 local_state_->SetString( 287 prefs::kAppListProfile, 288 profile_path.BaseName().MaybeAsASCII()); 289} 290 291void AppListServiceImpl::CreateShortcut() {} 292 293// We need to watch for profile removal to keep kAppListProfile updated. 294void AppListServiceImpl::OnProfileWillBeRemoved( 295 const base::FilePath& profile_path) { 296 // If the profile the app list uses just got deleted, reset it to the last 297 // used profile. 298 std::string app_list_last_profile = local_state_->GetString( 299 prefs::kAppListProfile); 300 if (profile_path.BaseName().MaybeAsASCII() == app_list_last_profile) { 301 local_state_->SetString(prefs::kAppListProfile, 302 local_state_->GetString(prefs::kProfileLastUsed)); 303 } 304} 305 306void AppListServiceImpl::Show() { 307 profile_loader_->LoadProfileInvalidatingOtherLoads( 308 GetProfilePath(profile_store_->GetUserDataDir()), 309 base::Bind(&AppListServiceImpl::ShowForProfile, 310 weak_factory_.GetWeakPtr())); 311} 312 313void AppListServiceImpl::AutoShowForProfile(Profile* requested_profile) { 314 if (local_state_->GetInt64(prefs::kAppListEnableTime) != 0) { 315 // User has not yet discovered the app launcher. Update the enable method to 316 // indicate this. It will then be recorded in UMA. 317 local_state_->SetInteger(prefs::kAppListEnableMethod, 318 ENABLE_SHOWN_UNDISCOVERED); 319 } 320 ShowForProfile(requested_profile); 321} 322 323void AppListServiceImpl::EnableAppList(Profile* initial_profile, 324 AppListEnableSource enable_source) { 325 SetProfilePath(initial_profile->GetPath()); 326 // Always allow the webstore "enable" button to re-run the install flow. 327 if (enable_source != AppListService::ENABLE_VIA_WEBSTORE_LINK && 328 local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled)) { 329 return; 330 } 331 332 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, true); 333 CreateShortcut(); 334 335 // UMA for launcher discoverability. 336 local_state_->SetInt64(prefs::kAppListEnableTime, 337 base::Time::Now().ToInternalValue()); 338 local_state_->SetInteger(prefs::kAppListEnableMethod, enable_source); 339 if (base::MessageLoop::current()) { 340 // Ensure a value is recorded if the user "never" shows the app list. Note 341 // there is no message loop in unit tests. 342 base::MessageLoop::current()->PostDelayedTask( 343 FROM_HERE, 344 base::Bind(&RecordAppListDiscoverability, 345 base::Unretained(local_state_), 346 false), 347 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes)); 348 } 349 350 AppShortcutManager* shortcut_manager = 351 AppShortcutManagerFactory::GetForProfile(initial_profile); 352 if (shortcut_manager) 353 shortcut_manager->OnceOffCreateShortcuts(); 354} 355 356void AppListServiceImpl::InvalidatePendingProfileLoads() { 357 profile_loader_->InvalidatePendingProfileLoads(); 358} 359 360void AppListServiceImpl::PerformStartupChecks(Profile* initial_profile) { 361 // Except in rare, once-off cases, this just checks that a pref is "0" and 362 // returns. 363 RecordAppListDiscoverability(local_state_, true); 364 365 if (command_line_.HasSwitch(switches::kResetAppListInstallState)) 366 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false); 367 368 if (command_line_.HasSwitch(switches::kEnableAppList)) 369 EnableAppList(initial_profile, ENABLE_VIA_COMMAND_LINE); 370 371 if (!base::MessageLoop::current()) 372 return; // In a unit test. 373 374 // Send app list usage stats after a delay. 375 const int kSendUsageStatsDelay = 5; 376 base::MessageLoop::current()->PostDelayedTask( 377 FROM_HERE, 378 base::Bind(&AppListServiceImpl::SendAppListStats), 379 base::TimeDelta::FromSeconds(kSendUsageStatsDelay)); 380} 381