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