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.
5 #include "chrome/browser/ui/app_list/app_list_service_impl.h"
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/keep_alive_service.h"
22 #include "chrome/browser/ui/app_list/keep_alive_service_impl.h"
23 #include "chrome/browser/ui/app_list/profile_loader.h"
24 #include "chrome/browser/ui/app_list/profile_store.h"
25 #include "chrome/common/chrome_constants.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/common/pref_names.h"
31 const int kDiscoverabilityTimeoutMinutes = 60;
33 void SendAppListAppLaunch(int count) {
34 UMA_HISTOGRAM_CUSTOM_COUNTS(
35 "Apps.AppListDailyAppLaunches", count, 1, 1000, 50);
37 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppToday", 1, 2);
40 void SendAppListLaunch(int count) {
41 UMA_HISTOGRAM_CUSTOM_COUNTS(
42 "Apps.AppListDailyLaunches", count, 1, 1000, 50);
44 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppListToday", 1, 2);
47 bool SendDailyEventFrequency(
48 const char* last_ping_pref,
49 const char* count_pref,
50 void (*send_callback)(int count)) {
51 PrefService* local_state = g_browser_process->local_state();
53 base::Time now = base::Time::Now();
54 base::Time last = base::Time::FromInternalValue(local_state->GetInt64(
56 int days = (now - last).InDays();
58 send_callback(local_state->GetInteger(count_pref));
59 local_state->SetInt64(
61 (last + base::TimeDelta::FromDays(days)).ToInternalValue());
62 local_state->SetInteger(count_pref, 0);
68 void RecordDailyEventFrequency(
69 const char* last_ping_pref,
70 const char* count_pref,
71 void (*send_callback)(int count)) {
72 if (!g_browser_process)
73 return; // In a unit test.
75 PrefService* local_state = g_browser_process->local_state();
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);
84 class ProfileStoreImpl : public ProfileStore {
86 explicit ProfileStoreImpl(ProfileManager* profile_manager)
87 : profile_manager_(profile_manager),
91 virtual void AddProfileObserver(ProfileInfoCacheObserver* observer) OVERRIDE {
92 profile_manager_->GetProfileInfoCache().AddObserver(observer);
95 virtual void LoadProfileAsync(
96 const base::FilePath& path,
97 base::Callback<void(Profile*)> callback) OVERRIDE {
98 profile_manager_->CreateProfileAsync(
100 base::Bind(&ProfileStoreImpl::OnProfileCreated,
101 weak_factory_.GetWeakPtr(),
108 void OnProfileCreated(base::Callback<void(Profile*)> callback,
110 Profile::CreateStatus status) {
112 case Profile::CREATE_STATUS_CREATED:
114 case Profile::CREATE_STATUS_INITIALIZED:
115 callback.Run(profile);
117 case Profile::CREATE_STATUS_LOCAL_FAIL:
118 case Profile::CREATE_STATUS_REMOTE_FAIL:
119 case Profile::CREATE_STATUS_CANCELED:
121 case Profile::MAX_CREATE_STATUS:
127 virtual Profile* GetProfileByPath(const base::FilePath& path) OVERRIDE {
128 return profile_manager_->GetProfileByPath(path);
131 virtual base::FilePath GetUserDataDir() OVERRIDE {
132 return profile_manager_->user_data_dir();
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);
143 ProfileManager* profile_manager_;
144 base::WeakPtrFactory<ProfileStoreImpl> weak_factory_;
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())
154 int64 enable_time_value = local_state->GetInt64(prefs::kAppListEnableTime);
155 if (enable_time_value == 0)
156 return; // Already recorded or never enabled.
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) -
167 if (time_remaining > base::TimeDelta()) {
168 base::MessageLoop::current()->PostDelayedTask(
170 base::Bind(&RecordAppListDiscoverability,
171 base::Unretained(local_state),
178 local_state->SetInt64(prefs::kAppListEnableTime, 0);
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",
192 base::TimeDelta::FromSeconds(1),
193 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes - 1),
194 10 /* bucket_count */);
196 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHowEnabled",
198 AppListService::ENABLE_NUM_ENABLE_SOURCES);
203 void AppListServiceImpl::RecordAppListLaunch() {
204 RecordDailyEventFrequency(prefs::kLastAppListLaunchPing,
205 prefs::kAppListLaunchCount,
207 RecordAppListDiscoverability(local_state_, false);
211 void AppListServiceImpl::RecordAppListAppLaunch() {
212 RecordDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
213 prefs::kAppListAppLaunchCount,
214 &SendAppListAppLaunch);
218 void AppListServiceImpl::SendAppListStats() {
219 if (!g_browser_process || g_browser_process->IsShuttingDown())
222 SendDailyEventFrequency(prefs::kLastAppListLaunchPing,
223 prefs::kAppListLaunchCount,
225 SendDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
226 prefs::kAppListAppLaunchCount,
227 &SendAppListAppLaunch);
230 AppListServiceImpl::AppListServiceImpl()
231 : profile_store_(new ProfileStoreImpl(
232 g_browser_process->profile_manager())),
234 command_line_(*CommandLine::ForCurrentProcess()),
235 local_state_(g_browser_process->local_state()),
236 profile_loader_(new ProfileLoader(
237 profile_store_.get(),
238 scoped_ptr<KeepAliveService>(new KeepAliveServiceImpl))) {
239 profile_store_->AddProfileObserver(this);
242 AppListServiceImpl::AppListServiceImpl(
243 const CommandLine& command_line,
244 PrefService* local_state,
245 scoped_ptr<ProfileStore> profile_store,
246 scoped_ptr<KeepAliveService> keep_alive_service)
247 : profile_store_(profile_store.Pass()),
249 command_line_(command_line),
250 local_state_(local_state),
251 profile_loader_(new ProfileLoader(
252 profile_store_.get(), keep_alive_service.Pass())) {
253 profile_store_->AddProfileObserver(this);
256 AppListServiceImpl::~AppListServiceImpl() {}
258 void AppListServiceImpl::SetAppListNextPaintCallback(void (*callback)()) {}
260 void AppListServiceImpl::HandleFirstRun() {}
262 void AppListServiceImpl::Init(Profile* initial_profile) {}
264 base::FilePath AppListServiceImpl::GetProfilePath(
265 const base::FilePath& user_data_dir) {
266 std::string app_list_profile;
267 if (local_state_->HasPrefPath(prefs::kAppListProfile))
268 app_list_profile = local_state_->GetString(prefs::kAppListProfile);
270 // If the user has no profile preference for the app launcher, default to the
271 // last browser profile used.
272 if (app_list_profile.empty() &&
273 local_state_->HasPrefPath(prefs::kProfileLastUsed)) {
274 app_list_profile = local_state_->GetString(prefs::kProfileLastUsed);
277 // If there is no last used profile recorded, use the initial profile.
278 if (app_list_profile.empty())
279 app_list_profile = chrome::kInitialProfile;
281 return user_data_dir.AppendASCII(app_list_profile);
284 void AppListServiceImpl::SetProfilePath(const base::FilePath& profile_path) {
285 // Ensure we don't set the pref to a managed user's profile path.
286 // TODO(calamity): Filter out managed profiles from the settings app so this
287 // can't get hit, so we can remove it.
288 if (profile_store_->IsProfileManaged(profile_path))
291 local_state_->SetString(
292 prefs::kAppListProfile,
293 profile_path.BaseName().MaybeAsASCII());
296 void AppListServiceImpl::CreateShortcut() {}
298 // We need to watch for profile removal to keep kAppListProfile updated.
299 void AppListServiceImpl::OnProfileWillBeRemoved(
300 const base::FilePath& profile_path) {
301 // If the profile the app list uses just got deleted, reset it to the last
303 std::string app_list_last_profile = local_state_->GetString(
304 prefs::kAppListProfile);
305 if (profile_path.BaseName().MaybeAsASCII() == app_list_last_profile) {
306 local_state_->SetString(prefs::kAppListProfile,
307 local_state_->GetString(prefs::kProfileLastUsed));
311 void AppListServiceImpl::Show() {
312 profile_loader_->LoadProfileInvalidatingOtherLoads(
313 GetProfilePath(profile_store_->GetUserDataDir()),
314 base::Bind(&AppListServiceImpl::ShowForProfile,
315 weak_factory_.GetWeakPtr()));
318 void AppListServiceImpl::AutoShowForProfile(Profile* requested_profile) {
319 if (local_state_->GetInt64(prefs::kAppListEnableTime) != 0) {
320 // User has not yet discovered the app launcher. Update the enable method to
321 // indicate this. It will then be recorded in UMA.
322 local_state_->SetInteger(prefs::kAppListEnableMethod,
323 ENABLE_SHOWN_UNDISCOVERED);
325 ShowForProfile(requested_profile);
328 void AppListServiceImpl::EnableAppList(Profile* initial_profile,
329 AppListEnableSource enable_source) {
330 SetProfilePath(initial_profile->GetPath());
331 if (local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled))
334 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, true);
337 // UMA for launcher discoverability.
338 local_state_->SetInt64(prefs::kAppListEnableTime,
339 base::Time::Now().ToInternalValue());
340 local_state_->SetInteger(prefs::kAppListEnableMethod, enable_source);
341 if (base::MessageLoop::current()) {
342 // Ensure a value is recorded if the user "never" shows the app list. Note
343 // there is no message loop in unit tests.
344 base::MessageLoop::current()->PostDelayedTask(
346 base::Bind(&RecordAppListDiscoverability,
347 base::Unretained(local_state_),
349 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes));
352 AppShortcutManager* shortcut_manager =
353 AppShortcutManagerFactory::GetForProfile(initial_profile);
354 if (shortcut_manager)
355 shortcut_manager->OnceOffCreateShortcuts();
358 void AppListServiceImpl::InvalidatePendingProfileLoads() {
359 profile_loader_->InvalidatePendingProfileLoads();
362 void AppListServiceImpl::PerformStartupChecks(Profile* initial_profile) {
363 // Except in rare, once-off cases, this just checks that a pref is "0" and
365 RecordAppListDiscoverability(local_state_, true);
367 if (command_line_.HasSwitch(switches::kResetAppListInstallState))
368 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false);
370 if (command_line_.HasSwitch(switches::kEnableAppList))
371 EnableAppList(initial_profile, ENABLE_VIA_COMMAND_LINE);
373 if (!base::MessageLoop::current())
374 return; // In a unit test.
376 // Send app list usage stats after a delay.
377 const int kSendUsageStatsDelay = 5;
378 base::MessageLoop::current()->PostDelayedTask(
380 base::Bind(&AppListServiceImpl::SendAppListStats),
381 base::TimeDelta::FromSeconds(kSendUsageStatsDelay));