Home | History | Annotate | Download | only in rlz
      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.h"
     26 #include "chrome/browser/search_engines/template_url_service.h"
     27 #include "chrome/browser/search_engines/template_url_service_factory.h"
     28 #include "chrome/browser/ui/startup/startup_browser_creator.h"
     29 #include "chrome/common/chrome_switches.h"
     30 #include "chrome/common/pref_names.h"
     31 #include "components/google/core/browser/google_util.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
     40 namespace GoogleUpdateSettings {
     41 static 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.
     49 static bool GetReferral(base::string16* referral) {
     50   return true;
     51 }
     52 static bool ClearReferral() {
     53   return true;
     54 }
     55 }  // namespace GoogleUpdateSettings
     56 #endif
     57 
     58 using content::BrowserThread;
     59 using content::NavigationEntry;
     60 
     61 namespace {
     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.
     65 const base::TimeDelta kMaxInitDelay = base::TimeDelta::FromSeconds(200);
     66 const base::TimeDelta kMinInitDelay = base::TimeDelta::FromSeconds(20);
     67 
     68 bool IsBrandOrganic(const std::string& brand) {
     69   return brand.empty() || google_brand::IsOrganic(brand);
     70 }
     71 
     72 void 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 
    167 bool 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 
    192 RLZTracker* RLZTracker::tracker_ = NULL;
    193 
    194 // static
    195 RLZTracker* RLZTracker::GetInstance() {
    196   return tracker_ ? tracker_ : Singleton<RLZTracker>::get();
    197 }
    198 
    199 RLZTracker::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 
    213 RLZTracker::~RLZTracker() {
    214 }
    215 
    216 // static
    217 bool 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
    229 bool 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 
    278 bool 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 
    323 void 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 
    333 void 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 
    363 void 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 
    371 void 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 
    402 bool RLZTracker::SendFinancialPing(const std::string& brand,
    403                                    const base::string16& lang,
    404                                    const base::string16& referral) {
    405   return ::SendFinancialPing(brand, lang, referral);
    406 }
    407 
    408 void 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             content::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
    444 bool 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 
    450 bool 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 
    469 bool 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 
    485 void 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 
    501 bool 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 
    513 bool* 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
    527 std::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.
    543 bool 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.
    551 bool 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 
    582 bool 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
    598 void RLZTracker::ClearRlzState() {
    599   GetInstance()->ClearRlzStateImpl();
    600 }
    601 
    602 void RLZTracker::ClearRlzStateImpl() {
    603   if (ScheduleClearRlzState())
    604     return;
    605   rlz_lib::ClearAllProductEvents(rlz_lib::CHROME);
    606 }
    607 
    608 bool 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
    623 void RLZTracker::CleanupRlz() {
    624   GetInstance()->rlz_cache_.clear();
    625   GetInstance()->registrar_.RemoveAll();
    626   rlz_lib::SetURLRequestContext(NULL);
    627 }
    628 
    629 // static
    630 void RLZTracker::EnableZeroDelayForTesting() {
    631   GetInstance()->min_init_delay_ = base::TimeDelta();
    632 }
    633 
    634 #if !defined(OS_IOS)
    635 // static
    636 void RLZTracker::RecordAppListSearch() {
    637   GetInstance()->RecordFirstSearch(RLZTracker::ChromeAppList());
    638 }
    639 #endif
    640