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 25 namespace { 26 27 const int kDiscoverabilityTimeoutMinutes = 60; 28 29 void 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 36 void 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 43 bool 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 64 void 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 82 class 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 IsProfileSupervised( 134 const base::FilePath& profile_path) OVERRIDE { 135 ProfileInfoCache& profile_info = 136 g_browser_process->profile_manager()->GetProfileInfoCache(); 137 size_t profile_index = profile_info.GetIndexOfProfileWithPath(profile_path); 138 return profile_index != std::string::npos && 139 profile_info.ProfileIsSupervisedAtIndex(profile_index); 140 } 141 142 private: 143 ProfileManager* profile_manager_; 144 base::WeakPtrFactory<ProfileStoreImpl> weak_factory_; 145 }; 146 147 void 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 203 void AppListServiceImpl::RecordAppListLaunch() { 204 RecordDailyEventFrequency(prefs::kLastAppListLaunchPing, 205 prefs::kAppListLaunchCount, 206 &SendAppListLaunch); 207 RecordAppListDiscoverability(local_state_, false); 208 } 209 210 // static 211 void AppListServiceImpl::RecordAppListAppLaunch() { 212 RecordDailyEventFrequency(prefs::kLastAppListAppLaunchPing, 213 prefs::kAppListAppLaunchCount, 214 &SendAppListAppLaunch); 215 } 216 217 // static 218 void 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 230 AppListServiceImpl::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 240 AppListServiceImpl::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 251 AppListServiceImpl::~AppListServiceImpl() {} 252 253 void AppListServiceImpl::SetAppListNextPaintCallback(void (*callback)()) {} 254 255 void AppListServiceImpl::HandleFirstRun() {} 256 257 void AppListServiceImpl::Init(Profile* initial_profile) {} 258 259 base::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 279 void AppListServiceImpl::SetProfilePath(const base::FilePath& profile_path) { 280 // Ensure we don't set the pref to a supervised user's profile path. 281 // TODO(calamity): Filter out supervised profiles from the settings app so 282 // this can't get hit, so we can remove it. 283 if (profile_store_->IsProfileSupervised(profile_path)) 284 return; 285 286 local_state_->SetString( 287 prefs::kAppListProfile, 288 profile_path.BaseName().MaybeAsASCII()); 289 } 290 291 void AppListServiceImpl::CreateShortcut() {} 292 293 // We need to watch for profile removal to keep kAppListProfile updated. 294 void 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 306 void AppListServiceImpl::Show() { 307 profile_loader_->LoadProfileInvalidatingOtherLoads( 308 GetProfilePath(profile_store_->GetUserDataDir()), 309 base::Bind(&AppListServiceImpl::ShowForProfile, 310 weak_factory_.GetWeakPtr())); 311 } 312 313 void 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 323 void 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 351 void AppListServiceImpl::InvalidatePendingProfileLoads() { 352 profile_loader_->InvalidatePendingProfileLoads(); 353 } 354 355 void AppListServiceImpl::PerformStartupChecks(Profile* initial_profile) { 356 // Except in rare, once-off cases, this just checks that a pref is "0" and 357 // returns. 358 RecordAppListDiscoverability(local_state_, true); 359 360 if (command_line_.HasSwitch(switches::kResetAppListInstallState)) 361 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false); 362 363 if (command_line_.HasSwitch(switches::kEnableAppList)) 364 EnableAppList(initial_profile, ENABLE_VIA_COMMAND_LINE); 365 366 if (!base::MessageLoop::current()) 367 return; // In a unit test. 368 369 // Send app list usage stats after a delay. 370 const int kSendUsageStatsDelay = 5; 371 base::MessageLoop::current()->PostDelayedTask( 372 FROM_HERE, 373 base::Bind(&AppListServiceImpl::SendAppListStats), 374 base::TimeDelta::FromSeconds(kSendUsageStatsDelay)); 375 } 376