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/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"
27 const int kDiscoverabilityTimeoutMinutes
= 60;
29 void SendAppListAppLaunch(int count
) {
30 UMA_HISTOGRAM_CUSTOM_COUNTS(
31 "Apps.AppListDailyAppLaunches", count
, 1, 1000, 50);
33 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppToday", 1, 2);
36 void SendAppListLaunch(int count
) {
37 UMA_HISTOGRAM_CUSTOM_COUNTS(
38 "Apps.AppListDailyLaunches", count
, 1, 1000, 50);
40 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppListToday", 1, 2);
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();
49 base::Time now
= base::Time::Now();
50 base::Time last
= base::Time::FromInternalValue(local_state
->GetInt64(
52 int days
= (now
- last
).InDays();
54 send_callback(local_state
->GetInteger(count_pref
));
55 local_state
->SetInt64(
57 (last
+ base::TimeDelta::FromDays(days
)).ToInternalValue());
58 local_state
->SetInteger(count_pref
, 0);
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.
71 PrefService
* local_state
= g_browser_process
->local_state();
73 return; // In a unit test.
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);
82 class ProfileStoreImpl
: public ProfileStore
{
84 explicit ProfileStoreImpl(ProfileManager
* profile_manager
)
85 : profile_manager_(profile_manager
),
89 virtual void AddProfileObserver(ProfileInfoCacheObserver
* observer
) OVERRIDE
{
90 profile_manager_
->GetProfileInfoCache().AddObserver(observer
);
93 virtual void LoadProfileAsync(
94 const base::FilePath
& path
,
95 base::Callback
<void(Profile
*)> callback
) OVERRIDE
{
96 profile_manager_
->CreateProfileAsync(
98 base::Bind(&ProfileStoreImpl::OnProfileCreated
,
99 weak_factory_
.GetWeakPtr(),
106 void OnProfileCreated(base::Callback
<void(Profile
*)> callback
,
108 Profile::CreateStatus status
) {
110 case Profile::CREATE_STATUS_CREATED
:
112 case Profile::CREATE_STATUS_INITIALIZED
:
113 callback
.Run(profile
);
115 case Profile::CREATE_STATUS_LOCAL_FAIL
:
116 case Profile::CREATE_STATUS_REMOTE_FAIL
:
117 case Profile::CREATE_STATUS_CANCELED
:
119 case Profile::MAX_CREATE_STATUS
:
125 virtual Profile
* GetProfileByPath(const base::FilePath
& path
) OVERRIDE
{
126 return profile_manager_
->GetProfileByPath(path
);
129 virtual base::FilePath
GetUserDataDir() OVERRIDE
{
130 return profile_manager_
->user_data_dir();
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
);
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()
232 new ProfileStoreImpl(g_browser_process
->profile_manager())),
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);
240 AppListServiceImpl::AppListServiceImpl(const CommandLine
& command_line
,
241 PrefService
* local_state
,
242 scoped_ptr
<ProfileStore
> profile_store
)
243 : profile_store_(profile_store
.Pass()),
245 command_line_(command_line
),
246 local_state_(local_state
),
247 profile_loader_(new ProfileLoader(profile_store_
.get())) {
248 profile_store_
->AddProfileObserver(this);
251 AppListServiceImpl::~AppListServiceImpl() {}
253 void AppListServiceImpl::SetAppListNextPaintCallback(void (*callback
)()) {}
255 void AppListServiceImpl::HandleFirstRun() {}
257 void AppListServiceImpl::Init(Profile
* initial_profile
) {}
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
);
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
);
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
;
276 return user_data_dir
.AppendASCII(app_list_profile
);
279 void AppListServiceImpl::SetProfilePath(const base::FilePath
& profile_path
) {
280 local_state_
->SetString(
281 prefs::kAppListProfile
,
282 profile_path
.BaseName().MaybeAsASCII());
285 void AppListServiceImpl::CreateShortcut() {}
287 // We need to watch for profile removal to keep kAppListProfile updated.
288 void AppListServiceImpl::OnProfileWillBeRemoved(
289 const base::FilePath
& profile_path
) {
290 // If the profile the app list uses just got deleted, reset it to the last
292 std::string app_list_last_profile
= local_state_
->GetString(
293 prefs::kAppListProfile
);
294 if (profile_path
.BaseName().MaybeAsASCII() == app_list_last_profile
) {
295 local_state_
->SetString(prefs::kAppListProfile
,
296 local_state_
->GetString(prefs::kProfileLastUsed
));
300 void AppListServiceImpl::Show() {
301 profile_loader_
->LoadProfileInvalidatingOtherLoads(
302 GetProfilePath(profile_store_
->GetUserDataDir()),
303 base::Bind(&AppListServiceImpl::ShowForProfile
,
304 weak_factory_
.GetWeakPtr()));
307 void AppListServiceImpl::AutoShowForProfile(Profile
* requested_profile
) {
308 if (local_state_
->GetInt64(prefs::kAppListEnableTime
) != 0) {
309 // User has not yet discovered the app launcher. Update the enable method to
310 // indicate this. It will then be recorded in UMA.
311 local_state_
->SetInteger(prefs::kAppListEnableMethod
,
312 ENABLE_SHOWN_UNDISCOVERED
);
314 ShowForProfile(requested_profile
);
317 void AppListServiceImpl::EnableAppList(Profile
* initial_profile
,
318 AppListEnableSource enable_source
) {
319 SetProfilePath(initial_profile
->GetPath());
320 // Always allow the webstore "enable" button to re-run the install flow.
321 if (enable_source
!= AppListService::ENABLE_VIA_WEBSTORE_LINK
&&
322 local_state_
->GetBoolean(prefs::kAppLauncherHasBeenEnabled
)) {
326 local_state_
->SetBoolean(prefs::kAppLauncherHasBeenEnabled
, true);
329 // UMA for launcher discoverability.
330 local_state_
->SetInt64(prefs::kAppListEnableTime
,
331 base::Time::Now().ToInternalValue());
332 local_state_
->SetInteger(prefs::kAppListEnableMethod
, enable_source
);
333 if (base::MessageLoop::current()) {
334 // Ensure a value is recorded if the user "never" shows the app list. Note
335 // there is no message loop in unit tests.
336 base::MessageLoop::current()->PostDelayedTask(
338 base::Bind(&RecordAppListDiscoverability
,
339 base::Unretained(local_state_
),
341 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes
));
345 void AppListServiceImpl::InvalidatePendingProfileLoads() {
346 profile_loader_
->InvalidatePendingProfileLoads();
349 void AppListServiceImpl::PerformStartupChecks(Profile
* initial_profile
) {
350 // Except in rare, once-off cases, this just checks that a pref is "0" and
352 RecordAppListDiscoverability(local_state_
, true);
354 if (command_line_
.HasSwitch(switches::kResetAppListInstallState
))
355 local_state_
->SetBoolean(prefs::kAppLauncherHasBeenEnabled
, false);
357 if (command_line_
.HasSwitch(switches::kEnableAppList
))
358 EnableAppList(initial_profile
, ENABLE_VIA_COMMAND_LINE
);
360 if (!base::MessageLoop::current())
361 return; // In a unit test.
363 // Send app list usage stats after a delay.
364 const int kSendUsageStatsDelay
= 5;
365 base::MessageLoop::current()->PostDelayedTask(
367 base::Bind(&AppListServiceImpl::SendAppListStats
),
368 base::TimeDelta::FromSeconds(kSendUsageStatsDelay
));