1// Copyright (c) 2012 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// This code glues the RLZ library DLL with Chrome. It allows Chrome to work
6// with or without the DLL being present. If the DLL is not present the
7// functions do nothing and just return false.
8
9#include "chrome/browser/rlz/rlz.h"
10
11#include <algorithm>
12
13#include "base/bind.h"
14#include "base/command_line.h"
15#include "base/debug/trace_event.h"
16#include "base/message_loop/message_loop.h"
17#include "base/prefs/pref_service.h"
18#include "base/strings/string_util.h"
19#include "base/strings/utf_string_conversions.h"
20#include "chrome/browser/browser_process.h"
21#include "chrome/browser/chrome_notification_types.h"
22#include "chrome/browser/google/google_brand.h"
23#include "chrome/browser/omnibox/omnibox_log.h"
24#include "chrome/browser/prefs/session_startup_pref.h"
25#include "chrome/browser/search_engines/template_url_service_factory.h"
26#include "chrome/browser/ui/startup/startup_browser_creator.h"
27#include "chrome/common/chrome_switches.h"
28#include "chrome/common/pref_names.h"
29#include "components/google/core/browser/google_util.h"
30#include "components/search_engines/template_url.h"
31#include "components/search_engines/template_url_service.h"
32#include "content/public/browser/browser_thread.h"
33#include "content/public/browser/navigation_entry.h"
34#include "content/public/browser/notification_service.h"
35#include "net/http/http_util.h"
36
37#if defined(OS_WIN)
38#include "chrome/installer/util/google_update_settings.h"
39#else
40namespace GoogleUpdateSettings {
41static bool GetLanguage(base::string16* language) {
42  // TODO(thakis): Implement.
43  NOTIMPLEMENTED();
44  return false;
45}
46
47// The referral program is defunct and not used. No need to implement these
48// functions on non-Win platforms.
49static bool GetReferral(base::string16* referral) {
50  return true;
51}
52static bool ClearReferral() {
53  return true;
54}
55}  // namespace GoogleUpdateSettings
56#endif
57
58using content::BrowserThread;
59using content::NavigationEntry;
60
61namespace {
62
63// Maximum and minimum delay for financial ping we would allow to be set through
64// master preferences. Somewhat arbitrary, may need to be adjusted in future.
65const base::TimeDelta kMaxInitDelay = base::TimeDelta::FromSeconds(200);
66const base::TimeDelta kMinInitDelay = base::TimeDelta::FromSeconds(20);
67
68bool IsBrandOrganic(const std::string& brand) {
69  return brand.empty() || google_brand::IsOrganic(brand);
70}
71
72void RecordProductEvents(bool first_run,
73                         bool is_google_default_search,
74                         bool is_google_homepage,
75                         bool is_google_in_startpages,
76                         bool already_ran,
77                         bool omnibox_used,
78                         bool homepage_used,
79                         bool app_list_used) {
80  TRACE_EVENT0("RLZ", "RecordProductEvents");
81  // Record the installation of chrome. We call this all the time but the rlz
82  // lib should ignore all but the first one.
83  rlz_lib::RecordProductEvent(rlz_lib::CHROME,
84                              RLZTracker::ChromeOmnibox(),
85                              rlz_lib::INSTALL);
86#if !defined(OS_IOS)
87  rlz_lib::RecordProductEvent(rlz_lib::CHROME,
88                              RLZTracker::ChromeHomePage(),
89                              rlz_lib::INSTALL);
90  rlz_lib::RecordProductEvent(rlz_lib::CHROME,
91                              RLZTracker::ChromeAppList(),
92                              rlz_lib::INSTALL);
93#endif  // !defined(OS_IOS)
94
95  if (!already_ran) {
96    // Do the initial event recording if is the first run or if we have an
97    // empty rlz which means we haven't got a chance to do it.
98    char omnibox_rlz[rlz_lib::kMaxRlzLength + 1];
99    if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), omnibox_rlz,
100                                    rlz_lib::kMaxRlzLength)) {
101      omnibox_rlz[0] = 0;
102    }
103
104    // Record if google is the initial search provider and/or home page.
105    if ((first_run || omnibox_rlz[0] == 0) && is_google_default_search) {
106      rlz_lib::RecordProductEvent(rlz_lib::CHROME,
107                                  RLZTracker::ChromeOmnibox(),
108                                  rlz_lib::SET_TO_GOOGLE);
109    }
110
111#if !defined(OS_IOS)
112    char homepage_rlz[rlz_lib::kMaxRlzLength + 1];
113    if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeHomePage(), homepage_rlz,
114                                    rlz_lib::kMaxRlzLength)) {
115      homepage_rlz[0] = 0;
116    }
117
118    if ((first_run || homepage_rlz[0] == 0) &&
119        (is_google_homepage || is_google_in_startpages)) {
120      rlz_lib::RecordProductEvent(rlz_lib::CHROME,
121                                  RLZTracker::ChromeHomePage(),
122                                  rlz_lib::SET_TO_GOOGLE);
123    }
124
125    char app_list_rlz[rlz_lib::kMaxRlzLength + 1];
126    if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeAppList(), app_list_rlz,
127                                    rlz_lib::kMaxRlzLength)) {
128      app_list_rlz[0] = 0;
129    }
130
131    // Record if google is the initial search provider and/or home page.
132    if ((first_run || app_list_rlz[0] == 0) && is_google_default_search) {
133      rlz_lib::RecordProductEvent(rlz_lib::CHROME,
134                                  RLZTracker::ChromeAppList(),
135                                  rlz_lib::SET_TO_GOOGLE);
136    }
137#endif  // !defined(OS_IOS)
138  }
139
140  // Record first user interaction with the omnibox. We call this all the
141  // time but the rlz lib should ingore all but the first one.
142  if (omnibox_used) {
143    rlz_lib::RecordProductEvent(rlz_lib::CHROME,
144                                RLZTracker::ChromeOmnibox(),
145                                rlz_lib::FIRST_SEARCH);
146  }
147
148#if !defined(OS_IOS)
149  // Record first user interaction with the home page. We call this all the
150  // time but the rlz lib should ingore all but the first one.
151  if (homepage_used || is_google_in_startpages) {
152    rlz_lib::RecordProductEvent(rlz_lib::CHROME,
153                                RLZTracker::ChromeHomePage(),
154                                rlz_lib::FIRST_SEARCH);
155  }
156
157  // Record first user interaction with the app list. We call this all the
158  // time but the rlz lib should ingore all but the first one.
159  if (app_list_used) {
160    rlz_lib::RecordProductEvent(rlz_lib::CHROME,
161                                RLZTracker::ChromeAppList(),
162                                rlz_lib::FIRST_SEARCH);
163  }
164#endif  // !defined(OS_IOS)
165}
166
167bool SendFinancialPing(const std::string& brand,
168                       const base::string16& lang,
169                       const base::string16& referral) {
170  rlz_lib::AccessPoint points[] = {RLZTracker::ChromeOmnibox(),
171#if !defined(OS_IOS)
172                                   RLZTracker::ChromeHomePage(),
173                                   RLZTracker::ChromeAppList(),
174#endif
175                                   rlz_lib::NO_ACCESS_POINT};
176  std::string lang_ascii(base::UTF16ToASCII(lang));
177  std::string referral_ascii(base::UTF16ToASCII(referral));
178  std::string product_signature;
179#if defined(OS_CHROMEOS)
180  product_signature = "chromeos";
181#else
182  product_signature = "chrome";
183#endif
184  return rlz_lib::SendFinancialPing(rlz_lib::CHROME, points,
185                                    product_signature.c_str(),
186                                    brand.c_str(), referral_ascii.c_str(),
187                                    lang_ascii.c_str(), false, true);
188}
189
190}  // namespace
191
192RLZTracker* RLZTracker::tracker_ = NULL;
193
194// static
195RLZTracker* RLZTracker::GetInstance() {
196  return tracker_ ? tracker_ : Singleton<RLZTracker>::get();
197}
198
199RLZTracker::RLZTracker()
200    : first_run_(false),
201      send_ping_immediately_(false),
202      is_google_default_search_(false),
203      is_google_homepage_(false),
204      is_google_in_startpages_(false),
205      worker_pool_token_(BrowserThread::GetBlockingPool()->GetSequenceToken()),
206      already_ran_(false),
207      omnibox_used_(false),
208      homepage_used_(false),
209      app_list_used_(false),
210      min_init_delay_(kMinInitDelay) {
211}
212
213RLZTracker::~RLZTracker() {
214}
215
216// static
217bool RLZTracker::InitRlzDelayed(bool first_run,
218                                bool send_ping_immediately,
219                                base::TimeDelta delay,
220                                bool is_google_default_search,
221                                bool is_google_homepage,
222                                bool is_google_in_startpages) {
223  return GetInstance()->Init(first_run, send_ping_immediately, delay,
224                             is_google_default_search, is_google_homepage,
225                             is_google_in_startpages);
226}
227
228// static
229bool RLZTracker::InitRlzFromProfileDelayed(Profile* profile,
230                                           bool first_run,
231                                           bool send_ping_immediately,
232                                           base::TimeDelta delay) {
233  bool is_google_default_search = false;
234  TemplateURLService* template_url_service =
235      TemplateURLServiceFactory::GetForProfile(profile);
236  if (template_url_service) {
237    const TemplateURL* url_template =
238        template_url_service->GetDefaultSearchProvider();
239    is_google_default_search =
240        url_template && url_template->url_ref().HasGoogleBaseURLs(
241            template_url_service->search_terms_data());
242  }
243
244  PrefService* pref_service = profile->GetPrefs();
245  bool is_google_homepage = google_util::IsGoogleHomePageUrl(
246      GURL(pref_service->GetString(prefs::kHomePage)));
247
248  bool is_google_in_startpages = false;
249#if !defined(OS_IOS)
250  // iOS does not have a notion of startpages.
251  SessionStartupPref session_startup_prefs =
252      StartupBrowserCreator::GetSessionStartupPref(
253          *CommandLine::ForCurrentProcess(), profile);
254  if (session_startup_prefs.type == SessionStartupPref::URLS) {
255    is_google_in_startpages =
256        std::count_if(session_startup_prefs.urls.begin(),
257                      session_startup_prefs.urls.end(),
258                      google_util::IsGoogleHomePageUrl) > 0;
259  }
260#endif
261
262  if (!InitRlzDelayed(first_run, send_ping_immediately, delay,
263                      is_google_default_search, is_google_homepage,
264                      is_google_in_startpages)) {
265    return false;
266  }
267
268#if !defined(OS_IOS)
269  // Prime the RLZ cache for the home page access point so that its avaiable
270  // for the startup page if needed (i.e., when the startup page is set to
271  // the home page).
272  GetAccessPointRlz(ChromeHomePage(), NULL);
273#endif  // !defined(OS_IOS)
274
275  return true;
276}
277
278bool RLZTracker::Init(bool first_run,
279                      bool send_ping_immediately,
280                      base::TimeDelta delay,
281                      bool is_google_default_search,
282                      bool is_google_homepage,
283                      bool is_google_in_startpages) {
284  first_run_ = first_run;
285  is_google_default_search_ = is_google_default_search;
286  is_google_homepage_ = is_google_homepage;
287  is_google_in_startpages_ = is_google_in_startpages;
288  send_ping_immediately_ = send_ping_immediately;
289
290  // Enable zero delays for testing.
291  if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType))
292    EnableZeroDelayForTesting();
293
294  delay = std::min(kMaxInitDelay, std::max(min_init_delay_, delay));
295
296  if (google_brand::GetBrand(&brand_) && !IsBrandOrganic(brand_)) {
297    // Register for notifications from the omnibox so that we can record when
298    // the user performs a first search.
299    registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
300                   content::NotificationService::AllSources());
301
302#if !defined(OS_IOS)
303    // Register for notifications from navigations, to see if the user has used
304    // the home page.
305    registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
306                   content::NotificationService::AllSources());
307#endif  // !defined(OS_IOS)
308  }
309  google_brand::GetReactivationBrand(&reactivation_brand_);
310
311  net::URLRequestContextGetter* context_getter =
312      g_browser_process->system_request_context();
313
314  // Could be NULL; don't run if so.  RLZ will try again next restart.
315  if (context_getter) {
316    rlz_lib::SetURLRequestContext(context_getter);
317    ScheduleDelayedInit(delay);
318  }
319
320  return true;
321}
322
323void RLZTracker::ScheduleDelayedInit(base::TimeDelta delay) {
324  // The RLZTracker is a singleton object that outlives any runnable tasks
325  // that will be queued up.
326  BrowserThread::GetBlockingPool()->PostDelayedSequencedWorkerTask(
327      worker_pool_token_,
328      FROM_HERE,
329      base::Bind(&RLZTracker::DelayedInit, base::Unretained(this)),
330      delay);
331}
332
333void RLZTracker::DelayedInit() {
334  bool schedule_ping = false;
335
336  // For organic brandcodes do not use rlz at all. Empty brandcode usually
337  // means a chromium install. This is ok.
338  if (!IsBrandOrganic(brand_)) {
339    RecordProductEvents(first_run_, is_google_default_search_,
340                        is_google_homepage_, is_google_in_startpages_,
341                        already_ran_, omnibox_used_, homepage_used_,
342                        app_list_used_);
343    schedule_ping = true;
344  }
345
346  // If chrome has been reactivated, record the events for this brand
347  // as well.
348  if (!IsBrandOrganic(reactivation_brand_)) {
349    rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
350    RecordProductEvents(first_run_, is_google_default_search_,
351                        is_google_homepage_, is_google_in_startpages_,
352                        already_ran_, omnibox_used_, homepage_used_,
353                        app_list_used_);
354    schedule_ping = true;
355  }
356
357  already_ran_ = true;
358
359  if (schedule_ping)
360    ScheduleFinancialPing();
361}
362
363void RLZTracker::ScheduleFinancialPing() {
364  BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
365      worker_pool_token_,
366      FROM_HERE,
367      base::Bind(&RLZTracker::PingNowImpl, base::Unretained(this)),
368      base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
369}
370
371void RLZTracker::PingNowImpl() {
372  TRACE_EVENT0("RLZ", "RLZTracker::PingNowImpl");
373  base::string16 lang;
374  GoogleUpdateSettings::GetLanguage(&lang);
375  if (lang.empty())
376    lang = base::ASCIIToUTF16("en");
377  base::string16 referral;
378  GoogleUpdateSettings::GetReferral(&referral);
379
380  if (!IsBrandOrganic(brand_) && SendFinancialPing(brand_, lang, referral)) {
381    GoogleUpdateSettings::ClearReferral();
382
383    {
384      base::AutoLock lock(cache_lock_);
385      rlz_cache_.clear();
386    }
387
388    // Prime the RLZ cache for the access points we are interested in.
389    GetAccessPointRlz(RLZTracker::ChromeOmnibox(), NULL);
390#if !defined(OS_IOS)
391    GetAccessPointRlz(RLZTracker::ChromeHomePage(), NULL);
392    GetAccessPointRlz(RLZTracker::ChromeAppList(), NULL);
393#endif  // !defined(OS_IOS)
394  }
395
396  if (!IsBrandOrganic(reactivation_brand_)) {
397    rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
398    SendFinancialPing(reactivation_brand_, lang, referral);
399  }
400}
401
402bool RLZTracker::SendFinancialPing(const std::string& brand,
403                                   const base::string16& lang,
404                                   const base::string16& referral) {
405  return ::SendFinancialPing(brand, lang, referral);
406}
407
408void RLZTracker::Observe(int type,
409                         const content::NotificationSource& source,
410                         const content::NotificationDetails& details) {
411  switch (type) {
412    case chrome::NOTIFICATION_OMNIBOX_OPENED_URL:
413      // In M-36, we made NOTIFICATION_OMNIBOX_OPENED_URL fire more often than
414      // it did previously.  The RLZ folks want RLZ's "first search" detection
415      // to remain as unaffected as possible by this change.  This test is
416      // there to keep the old behavior.
417      if (!content::Details<OmniboxLog>(details).ptr()->is_popup_open)
418        break;
419      RecordFirstSearch(ChromeOmnibox());
420      registrar_.Remove(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
421                        content::NotificationService::AllSources());
422      break;
423#if !defined(OS_IOS)
424    case content::NOTIFICATION_NAV_ENTRY_PENDING: {
425      const NavigationEntry* entry =
426          content::Details<content::NavigationEntry>(details).ptr();
427      if (entry != NULL &&
428          ((entry->GetTransitionType() &
429            ui::PAGE_TRANSITION_HOME_PAGE) != 0)) {
430        RecordFirstSearch(ChromeHomePage());
431        registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
432                          content::NotificationService::AllSources());
433      }
434      break;
435    }
436#endif  // !defined(OS_IOS)
437    default:
438      NOTREACHED();
439      break;
440  }
441}
442
443// static
444bool RLZTracker::RecordProductEvent(rlz_lib::Product product,
445                                    rlz_lib::AccessPoint point,
446                                    rlz_lib::Event event_id) {
447  return GetInstance()->RecordProductEventImpl(product, point, event_id);
448}
449
450bool RLZTracker::RecordProductEventImpl(rlz_lib::Product product,
451                                        rlz_lib::AccessPoint point,
452                                        rlz_lib::Event event_id) {
453  // Make sure we don't access disk outside of the I/O thread.
454  // In such case we repost the task on the right thread and return error.
455  if (ScheduleRecordProductEvent(product, point, event_id))
456    return true;
457
458  bool ret = rlz_lib::RecordProductEvent(product, point, event_id);
459
460  // If chrome has been reactivated, record the event for this brand as well.
461  if (!reactivation_brand_.empty()) {
462    rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
463    ret &= rlz_lib::RecordProductEvent(product, point, event_id);
464  }
465
466  return ret;
467}
468
469bool RLZTracker::ScheduleRecordProductEvent(rlz_lib::Product product,
470                                            rlz_lib::AccessPoint point,
471                                            rlz_lib::Event event_id) {
472  if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
473    return false;
474
475  BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
476      worker_pool_token_,
477      FROM_HERE,
478      base::Bind(base::IgnoreResult(&RLZTracker::RecordProductEvent),
479                 product, point, event_id),
480      base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
481
482  return true;
483}
484
485void RLZTracker::RecordFirstSearch(rlz_lib::AccessPoint point) {
486  // Make sure we don't access disk outside of the I/O thread.
487  // In such case we repost the task on the right thread and return error.
488  if (ScheduleRecordFirstSearch(point))
489    return;
490
491  bool* record_used = GetAccessPointRecord(point);
492
493  // Try to record event now, else set the flag to try later when we
494  // attempt the ping.
495  if (!RecordProductEvent(rlz_lib::CHROME, point, rlz_lib::FIRST_SEARCH))
496    *record_used = true;
497  else if (send_ping_immediately_ && point == ChromeOmnibox())
498    ScheduleDelayedInit(base::TimeDelta());
499}
500
501bool RLZTracker::ScheduleRecordFirstSearch(rlz_lib::AccessPoint point) {
502  if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
503    return false;
504  BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
505      worker_pool_token_,
506      FROM_HERE,
507      base::Bind(&RLZTracker::RecordFirstSearch,
508                 base::Unretained(this), point),
509      base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
510  return true;
511}
512
513bool* RLZTracker::GetAccessPointRecord(rlz_lib::AccessPoint point) {
514  if (point == ChromeOmnibox())
515    return &omnibox_used_;
516#if !defined(OS_IOS)
517  if (point == ChromeHomePage())
518    return &homepage_used_;
519  if (point == ChromeAppList())
520    return &app_list_used_;
521#endif  // !defined(OS_IOS)
522  NOTREACHED();
523  return NULL;
524}
525
526// static
527std::string RLZTracker::GetAccessPointHttpHeader(rlz_lib::AccessPoint point) {
528  TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointHttpHeader");
529  std::string extra_headers;
530  base::string16 rlz_string;
531  RLZTracker::GetAccessPointRlz(point, &rlz_string);
532  if (!rlz_string.empty()) {
533    net::HttpUtil::AppendHeaderIfMissing("X-Rlz-String",
534                                         base::UTF16ToUTF8(rlz_string),
535                                         &extra_headers);
536  }
537
538  return extra_headers;
539}
540
541// GetAccessPointRlz() caches RLZ strings for all access points. If we had
542// a successful ping, then we update the cached value.
543bool RLZTracker::GetAccessPointRlz(rlz_lib::AccessPoint point,
544                                   base::string16* rlz) {
545  TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointRlz");
546  return GetInstance()->GetAccessPointRlzImpl(point, rlz);
547}
548
549// GetAccessPointRlz() caches RLZ strings for all access points. If we had
550// a successful ping, then we update the cached value.
551bool RLZTracker::GetAccessPointRlzImpl(rlz_lib::AccessPoint point,
552                                       base::string16* rlz) {
553  // If the RLZ string for the specified access point is already cached,
554  // simply return its value.
555  {
556    base::AutoLock lock(cache_lock_);
557    if (rlz_cache_.find(point) != rlz_cache_.end()) {
558      if (rlz)
559        *rlz = rlz_cache_[point];
560      return true;
561    }
562  }
563
564  // Make sure we don't access disk outside of the I/O thread.
565  // In such case we repost the task on the right thread and return error.
566  if (ScheduleGetAccessPointRlz(point))
567    return false;
568
569  char str_rlz[rlz_lib::kMaxRlzLength + 1];
570  if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength))
571    return false;
572
573  base::string16 rlz_local(base::ASCIIToUTF16(std::string(str_rlz)));
574  if (rlz)
575    *rlz = rlz_local;
576
577  base::AutoLock lock(cache_lock_);
578  rlz_cache_[point] = rlz_local;
579  return true;
580}
581
582bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) {
583  if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
584    return false;
585
586  base::string16* not_used = NULL;
587  BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
588      worker_pool_token_,
589      FROM_HERE,
590      base::Bind(base::IgnoreResult(&RLZTracker::GetAccessPointRlz), point,
591                 not_used),
592      base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
593  return true;
594}
595
596#if defined(OS_CHROMEOS)
597// static
598void RLZTracker::ClearRlzState() {
599  GetInstance()->ClearRlzStateImpl();
600}
601
602void RLZTracker::ClearRlzStateImpl() {
603  if (ScheduleClearRlzState())
604    return;
605  rlz_lib::ClearAllProductEvents(rlz_lib::CHROME);
606}
607
608bool RLZTracker::ScheduleClearRlzState() {
609  if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
610    return false;
611
612  BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
613      worker_pool_token_,
614      FROM_HERE,
615      base::Bind(&RLZTracker::ClearRlzStateImpl,
616                 base::Unretained(this)),
617      base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
618  return true;
619}
620#endif
621
622// static
623void RLZTracker::CleanupRlz() {
624  GetInstance()->rlz_cache_.clear();
625  GetInstance()->registrar_.RemoveAll();
626  rlz_lib::SetURLRequestContext(NULL);
627}
628
629// static
630void RLZTracker::EnableZeroDelayForTesting() {
631  GetInstance()->min_init_delay_ = base::TimeDelta();
632}
633
634#if !defined(OS_IOS)
635// static
636void RLZTracker::RecordAppListSearch() {
637  GetInstance()->RecordFirstSearch(RLZTracker::ChromeAppList());
638}
639#endif
640