user_experiment.cc revision a3f7b4e666c476898878fa745f637129375cd889
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#include "chrome/installer/util/user_experiment.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
70529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include <windows.h>
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <sddl.h>
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <wtsapi32.h>
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <vector>
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/command_line.h"
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/files/file_path.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/process/launch.h"
15effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/rand_util.h"
16e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#include "base/strings/string_number_conversions.h"
17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/strings/string_split.h"
18e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#include "base/strings/string_util.h"
190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include "base/strings/utf_string_conversions.h"
200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include "base/win/scoped_handle.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/windows_version.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/attrition_experiments.h"
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/chrome_result_codes.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/chrome_switches.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/browser_distribution.h"
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/installer/util/google_update_constants.h"
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/installer/util/google_update_settings.h"
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/installer/util/helper.h"
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/installer/util/install_util.h"
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/installer/util/product.h"
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/public/common/result_codes.h"
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#pragma comment(lib, "wtsapi32.lib")
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace installer {
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// The following strings are the possible outcomes of the toast experiment
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// as recorded in the |client| field.
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const wchar_t kToastExpControlGroup[] =        L"01";
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const wchar_t kToastExpCancelGroup[] =         L"02";
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const wchar_t kToastExpUninstallGroup[] =      L"04";
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const wchar_t kToastExpTriesOkGroup[] =        L"18";
455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuconst wchar_t kToastExpTriesErrorGroup[] =     L"28";
465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuconst wchar_t kToastActiveGroup[] =            L"40";
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const wchar_t kToastUDDirFailure[] =           L"40";
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const wchar_t kToastExpBaseGroup[] =           L"80";
495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Substitute the locale parameter in uninstall URL with whatever
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Google Update tells us is the locale. In case we fail to find
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the locale, we use US English.
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)string16 LocalizeUrl(const wchar_t* url) {
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  string16 language;
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!GoogleUpdateSettings::GetLanguage(&language))
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    language = L"en-US";  // Default to US English.
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return ReplaceStringPlaceholders(url, language.c_str(), NULL);
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)string16 GetWelcomeBackUrl() {
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const wchar_t kWelcomeUrl[] = L"http://www.google.com/chrome/intl/$1/"
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                L"welcomeback-new.html";
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return LocalizeUrl(kWelcomeUrl);
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Converts FILETIME to hours. FILETIME times are absolute times in
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// 100 nanosecond units. For example 5:30 pm of June 15, 2009 is 3580464.
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)int FileTimeToHours(const FILETIME& time) {
695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  const ULONGLONG k100sNanoSecsToHours = 10000000LL * 60 * 60;
70010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  ULARGE_INTEGER uli = {time.dwLowDateTime, time.dwHighDateTime};
71010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  return static_cast<int>(uli.QuadPart / k100sNanoSecsToHours);
72010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
73010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Returns the directory last write time in hours since January 1, 1601.
75010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Returns -1 if there was an error retrieving the directory time.
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)int GetDirectoryWriteTimeInHours(const wchar_t* path) {
77010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // To open a directory you need to pass FILE_FLAG_BACKUP_SEMANTICS.
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
79010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  base::win::ScopedHandle file(::CreateFileW(path, 0, share, NULL,
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL));
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!file.IsValid())
825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return -1;
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  FILETIME time;
855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return ::GetFileTime(file, NULL, NULL, &time) ? FileTimeToHours(time) : -1;
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Returns the directory last-write time age in hours, relative to current
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// time, so if it returns 14 it means that the directory was last written 14
90e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// hours ago. Returns -1 if there was an error retrieving the directory.
91e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochint GetDirectoryWriteAgeInHours(const wchar_t* path) {
92e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  int dir_time = GetDirectoryWriteTimeInHours(path);
93e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (dir_time < 0)
94e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    return dir_time;
95e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  FILETIME time;
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GetSystemTimeAsFileTime(&time);
97e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  int now_time = FileTimeToHours(time);
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (dir_time >= now_time)
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 0;
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (now_time - dir_time);
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Launches setup.exe (located at |setup_path|) with |cmd_line|.
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// If system_level_toast is true, appends --system-level-toast.
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// If handle to experiment result key was given at startup, re-add it.
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Does not wait for the process to terminate.
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |cmd_line| may be modified as a result of this call.
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool LaunchSetup(CommandLine* cmd_line, bool system_level_toast) {
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Propagate --verbose-logging to the invoked setup.exe.
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (current_cmd_line.HasSwitch(switches::kVerboseLogging))
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cmd_line->AppendSwitch(switches::kVerboseLogging);
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Re-add the system level toast flag.
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (system_level_toast) {
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cmd_line->AppendSwitch(switches::kSystemLevel);
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cmd_line->AppendSwitch(switches::kSystemLevelToast);
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
120e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // Re-add the toast result key. We need to do this because Setup running as
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // system passes the key to Setup running as user, but that child process
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // does not perform the actual toasting, it launches another Setup (as user)
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // to do so. That is the process that needs the key.
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string key(switches::kToastResultsKey);
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string toast_key = current_cmd_line.GetSwitchValueASCII(key);
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!toast_key.empty()) {
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      cmd_line->AppendSwitchASCII(key, toast_key);
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Use handle inheritance to make sure the duplicated toast results key
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // gets inherited by the child process.
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::LaunchOptions options;
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      options.inherit_handles = true;
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return base::LaunchProcess(*cmd_line, options, NULL);
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return base::LaunchProcess(*cmd_line, base::LaunchOptions(), NULL);
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// For System level installs, setup.exe lives in the system temp, which
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// is normally c:\windows\temp. In many cases files inside this folder
142e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// are not accessible for execution by regular user accounts.
143e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// This function changes the permissions so that any authenticated user
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// can launch |exe| later on. This function should only be called if the
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// code is running at the system level.
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool FixDACLsForExecute(const base::FilePath& exe) {
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The general strategy to is to add an ACE to the exe DACL the quick
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // and dirty way: a) read the DACL b) convert it to sddl string c) add the
149e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // new ACE to the string d) convert sddl string back to DACL and finally
150e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // e) write new dacl.
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  char buff[1024];
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DWORD len = sizeof(buff);
153e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  PSECURITY_DESCRIPTOR sd = reinterpret_cast<PSECURITY_DESCRIPTOR>(buff);
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!::GetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION,
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          sd, len, &len)) {
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  wchar_t* sddl = 0;
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(sd,
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddl, NULL))
161e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    return false;
162e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  string16 new_sddl(sddl);
163e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  ::LocalFree(sddl);
164e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  sd = NULL;
165e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // See MSDN for the  security descriptor definition language (SDDL) syntax,
166e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // in our case we add "A;" generic read 'GR' and generic execute 'GX' for
167e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // the nt\authenticated_users 'AU' group, that becomes:
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const wchar_t kAllowACE[] = L"(A;;GRGX;;;AU)";
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We should check that there are no special ACES for the group we
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // are interested, which is nt\authenticated_users.
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (string16::npos != new_sddl.find(L";AU)"))
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Specific ACEs (not inherited) need to go to the front. It is ok if we
1744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // are the very first one.
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t pos_insert = new_sddl.find(L"(");
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (string16::npos == pos_insert)
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // All good, time to change the dacl.
179e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  new_sddl.insert(pos_insert, kAllowACE);
180e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(new_sddl.c_str(),
181e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      SDDL_REVISION_1, &sd, NULL))
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool rv = ::SetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION,
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               sd) == TRUE;
185e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  ::LocalFree(sd);
186e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  return rv;
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This function launches setup as the currently logged-in interactive
190e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// user that is the user whose logon session is attached to winsta0\default.
191e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// It assumes that currently we are running as SYSTEM in a non-interactive
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// windowstation.
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The function fails if there is no interactive session active, basically
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the computer is on but nobody has logged in locally.
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Remote Desktop sessions do not count as interactive sessions; running this
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// method as a user logged in via remote desktop will do nothing.
197e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochbool LaunchSetupAsConsoleUser(CommandLine* cmd_line) {
198e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // Convey to the invoked setup.exe that it's operating on a system-level
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // installation.
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  cmd_line->AppendSwitch(switches::kSystemLevel);
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
202e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // Propagate --verbose-logging to the invoked setup.exe.
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kVerboseLogging))
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    cmd_line->AppendSwitch(switches::kVerboseLogging);
2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Get the Google Update results key, and pass it on the command line to
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // the child process.
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int key = GoogleUpdateSettings::DuplicateGoogleUpdateSystemClientKey();
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  cmd_line->AppendSwitchASCII(switches::kToastResultsKey,
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                              base::IntToString(key));
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (base::win::GetVersion() > base::win::VERSION_XP) {
2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Make sure that in Vista and Above we have the proper DACLs so
2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // the interactive user can launch it.
2155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!FixDACLsForExecute(cmd_line->GetProgram()))
2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      NOTREACHED();
2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DWORD console_id = ::WTSGetActiveConsoleSessionId();
2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (console_id == 0xFFFFFFFF) {
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    PLOG(ERROR) << __FUNCTION__ << " failed to get active session id";
2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  HANDLE user_token;
2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!::WTSQueryUserToken(console_id, &user_token)) {
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    PLOG(ERROR) << __FUNCTION__ << " failed to get user token for console_id "
2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                << console_id;
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Note: Handle inheritance must be true in order for the child process to be
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // able to use the duplicated handle above (Google Update results).
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::LaunchOptions options;
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  options.as_user = user_token;
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  options.inherit_handles = true;
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  options.empty_desktop_name = true;
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  VLOG(1) << __FUNCTION__ << " launching " << cmd_line->GetCommandLineString();
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  bool launched = base::LaunchProcess(*cmd_line, options, NULL);
2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ::CloseHandle(user_token);
2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  VLOG(1) << __FUNCTION__ << "   result: " << launched;
2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return launched;
2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
243e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// A helper function that writes to HKLM if the handle was passed through the
2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// command line, but HKCU otherwise. |experiment_group| is the value to write
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// and |last_write| is used when writing to HKLM to determine whether to close
2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the handle when done.
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SetClient(const string16& experiment_group, bool last_write) {
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  static int reg_key_handle = -1;
2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (reg_key_handle == -1) {
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If a specific Toast Results key handle (presumably to our HKLM key) was
251010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    // passed in to the command line (such as for system level installs), we use
252010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    // it. Otherwise, we write to the key under HKCU.
253010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
254010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (cmd_line.HasSwitch(switches::kToastResultsKey)) {
255010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      // Get the handle to the key under HKLM.
256010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      base::StringToInt(
257010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          cmd_line.GetSwitchValueNative(switches::kToastResultsKey),
258010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          &reg_key_handle);
2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    } else {
2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      reg_key_handle = 0;
2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
2625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (reg_key_handle) {
2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Use it to write the experiment results.
266e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    GoogleUpdateSettings::WriteGoogleUpdateSystemClientKey(
2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        reg_key_handle, google_update::kRegClientField, experiment_group);
2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (last_write)
2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      CloseHandle((HANDLE) reg_key_handle);
2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
2715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Write to HKCU.
2725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    GoogleUpdateSettings::SetClient(experiment_group);
2735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
275e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
2765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
2775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool CreateExperimentDetails(int flavor, ExperimentDetails* experiment) {
2795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  struct FlavorDetails {
2805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int heading_id;
2815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int flags;
2825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  };
283e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // Maximum number of experiment flavors we support.
284e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  static const int kMax = 4;
2855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // This struct determines which experiment flavors we show for each locale and
2865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // brand.
2875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  //
2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Plugin infobar experiment:
2895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The experiment in 2011 used PIxx codes.
2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  //
2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Inactive user toast experiment:
2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The experiment in Dec 2009 used TGxx and THxx.
2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The experiment in Feb 2010 used TKxx and TLxx.
2945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The experiment in Apr 2010 used TMxx and TNxx.
2955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The experiment in Oct 2010 used TVxx TWxx TXxx TYxx.
2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The experiment in Feb 2011 used SJxx SKxx SLxx SMxx.
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The experiment in Mar 2012 used ZAxx ZBxx ZCxx.
2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The experiment in Jan 2013 uses DAxx.
2995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  using namespace attrition_experiments;
3005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  static const struct UserExperimentSpecs {
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const wchar_t* locale;  // Locale to show this experiment for (* for all).
3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const wchar_t* brands;  // Brand codes show this experiment for (* for all).
3045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int control_group;      // Size of the control group, in percentages.
3055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const wchar_t* prefix;  // The two letter experiment code. The second letter
3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            // will be incremented with the flavor.
3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    FlavorDetails flavors[kMax];
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } kExperiments[] = {
3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // The first match from top to bottom is used so this list should be ordered
3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // most-specific rule first.
3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    { L"*", L"GGRV",  // All locales, GGRV is enterprise.
3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      0,              // 0 percent control group.
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      L"EA",          // Experiment is EAxx, EBxx, etc.
3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // No flavors means no experiment.
3155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      { { 0, 0 },
3165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        { 0, 0 },
3175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        { 0, 0 },
318e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        { 0, 0 }
319e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      }
3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    },
3215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    { L"*", L"*",     // All locales, all brands.
3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      5,              // 5 percent control group.
323e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      L"DA",          // Experiment is DAxx.
324e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      // One single flavor.
3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      { { IDS_TRY_TOAST_HEADING3, kToastUiMakeDefault },
3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        { 0, 0 },
3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        { 0, 0 },
3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        { 0, 0 }
329e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      }
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  };
3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
333e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  string16 locale;
3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  GoogleUpdateSettings::GetLanguage(&locale);
3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (locale.empty() || (locale == ASCIIToWide("en")))
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    locale = ASCIIToWide("en-US");
3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  string16 brand;
339c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!GoogleUpdateSettings::GetBrand(&brand))
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    brand = ASCIIToWide("");  // Could still be viable for catch-all rules.
341e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
342e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  for (int i = 0; i < arraysize(kExperiments); ++i) {
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (kExperiments[i].locale != locale &&
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        kExperiments[i].locale != ASCIIToWide("*"))
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::vector<string16> brand_codes;
3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::SplitString(kExperiments[i].brands, L',', &brand_codes);
3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (brand_codes.empty())
350e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      return false;
351e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    for (std::vector<string16>::iterator it = brand_codes.begin();
352e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch         it != brand_codes.end(); ++it) {
353e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      if (*it != brand && *it != L"*")
3545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        continue;
3550529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      // We have found our match.
3560529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      const UserExperimentSpecs& match = kExperiments[i];
3570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      // Find out how many flavors we have. Zero means no experiment.
3580529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      int num_flavors = 0;
3590529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      while (match.flavors[num_flavors].heading_id) { ++num_flavors; }
3600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      if (!num_flavors)
3610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        return false;
3620529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
3630529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      if (flavor < 0)
3640529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        flavor = base::RandInt(0, num_flavors - 1);
3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      experiment->flavor = flavor;
3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      experiment->heading = match.flavors[flavor].heading_id;
3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      experiment->control_group = match.control_group;
3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      const wchar_t prefix[] = { match.prefix[0], match.prefix[1] + flavor, 0 };
369e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      experiment->prefix = prefix;
370e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      experiment->flags = match.flavors[flavor].flags;
371e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      return true;
3725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
374e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
375e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  return false;
3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Currently we only have one experiment: the inactive user toast. Which only
3795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// applies for users doing upgrades.
380e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
3815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// There are three scenarios when this function is called:
3825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// 1- Is a per-user-install and it updated: perform the experiment
3835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// 2- Is a system-install and it updated : relaunch as the interactive user
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// 3- It has been re-launched from the #2 case. In this case we enter
3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//    this function with |system_install| true and a REENTRY_SYS_UPDATE status.
3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void LaunchBrowserUserExperiment(const CommandLine& base_cmd_line,
3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 InstallStatus status,
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 bool system_level) {
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (system_level) {
390e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    if (NEW_VERSION_UPDATED == status) {
391e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      CommandLine cmd_line(base_cmd_line);
3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      cmd_line.AppendSwitch(switches::kSystemLevelToast);
3935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // We need to relaunch as the interactive user.
3945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      LaunchSetupAsConsoleUser(&cmd_line);
3955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
3965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
3985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (status != NEW_VERSION_UPDATED && status != REENTRY_SYS_UPDATE) {
3995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // We are not updating or in re-launch. Exit.
4005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
4015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
4025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
404  // The |flavor| value ends up being processed by TryChromeDialogView to show
405  // different experiments.
406  ExperimentDetails experiment;
407  if (!CreateExperimentDetails(-1, &experiment)) {
408    VLOG(1) << "Failed to get experiment details.";
409    return;
410  }
411  int flavor = experiment.flavor;
412  string16 base_group = experiment.prefix;
413
414  string16 brand;
415  if (GoogleUpdateSettings::GetBrand(&brand) && (brand == L"CHXX")) {
416    // Testing only: the user automatically qualifies for the experiment.
417    VLOG(1) << "Experiment qualification bypass";
418  } else {
419    // Check that the user was not already drafted in this experiment.
420    string16 client;
421    GoogleUpdateSettings::GetClient(&client);
422    if (client.size() > 2) {
423      if (base_group == client.substr(0, 2)) {
424        VLOG(1) << "User already participated in this experiment";
425        return;
426      }
427    }
428    // Check browser usage inactivity by the age of the last-write time of the
429    // most recently-used chrome user data directory.
430    std::vector<base::FilePath> user_data_dirs;
431    BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
432        BrowserDistribution::CHROME_BROWSER);
433    GetChromeUserDataPaths(dist, &user_data_dirs);
434    int dir_age_hours = -1;
435    for (size_t i = 0; i < user_data_dirs.size(); ++i) {
436      int this_age = GetDirectoryWriteAgeInHours(
437          user_data_dirs[i].value().c_str());
438      if (this_age >= 0 && (dir_age_hours < 0 || this_age < dir_age_hours))
439        dir_age_hours = this_age;
440    }
441
442    const bool experiment_enabled = false;
443    const int kThirtyDays = 30 * 24;
444
445    if (!experiment_enabled) {
446      VLOG(1) << "Toast experiment is disabled.";
447      return;
448    } else if (dir_age_hours < 0) {
449      // This means that we failed to find the user data dir. The most likely
450      // cause is that this user has not ever used chrome at all which can
451      // happen in a system-level install.
452      SetClient(base_group + kToastUDDirFailure, true);
453      return;
454    } else if (dir_age_hours < kThirtyDays) {
455      // An active user, so it does not qualify.
456      VLOG(1) << "Chrome used in last " << dir_age_hours << " hours";
457      SetClient(base_group + kToastActiveGroup, true);
458      return;
459    }
460    // Check to see if this user belongs to the control group.
461    double control_group = 1.0 * (100 - experiment.control_group) / 100;
462    if (base::RandDouble() > control_group) {
463      SetClient(base_group + kToastExpControlGroup, true);
464      VLOG(1) << "User is control group";
465      return;
466    }
467  }
468
469  VLOG(1) << "User drafted for toast experiment " << flavor;
470  SetClient(base_group + kToastExpBaseGroup, false);
471  // User level: The experiment needs to be performed in a different process
472  // because google_update expects the upgrade process to be quick and nimble.
473  // System level: We have already been relaunched, so we don't need to be
474  // quick, but we relaunch to follow the exact same codepath.
475  CommandLine cmd_line(base_cmd_line);
476  cmd_line.AppendSwitchASCII(switches::kInactiveUserToast,
477                             base::IntToString(flavor));
478  cmd_line.AppendSwitchASCII(switches::kExperimentGroup,
479                             WideToASCII(base_group));
480  LaunchSetup(&cmd_line, system_level);
481}
482
483// User qualifies for the experiment. To test, use --try-chrome-again=|flavor|
484// as a parameter to chrome.exe.
485void InactiveUserToastExperiment(int flavor,
486                                 const string16& experiment_group,
487                                 const Product& product,
488                                 const base::FilePath& application_path) {
489  // Add the 'welcome back' url for chrome to show.
490  CommandLine options(CommandLine::NO_PROGRAM);
491  options.AppendSwitchNative(::switches::kTryChromeAgain,
492      base::IntToString16(flavor));
493  // Prepend the url with a space.
494  string16 url(GetWelcomeBackUrl());
495  options.AppendArg("--");
496  options.AppendArgNative(url);
497  // The command line should now have the url added as:
498  // "chrome.exe -- <url>"
499  DCHECK_NE(string16::npos,
500      options.GetCommandLineString().find(L" -- " + url));
501
502  // Launch chrome now. It will show the toast UI.
503  int32 exit_code = 0;
504  if (!product.LaunchChromeAndWait(application_path, options, &exit_code))
505    return;
506
507  // The chrome process has exited, figure out what happened.
508  const wchar_t* outcome = NULL;
509  switch (exit_code) {
510    case content::RESULT_CODE_NORMAL_EXIT:
511      outcome = kToastExpTriesOkGroup;
512      break;
513    case chrome::RESULT_CODE_NORMAL_EXIT_CANCEL:
514      outcome = kToastExpCancelGroup;
515      break;
516    case chrome::RESULT_CODE_NORMAL_EXIT_EXP2:
517      outcome = kToastExpUninstallGroup;
518      break;
519    default:
520      outcome = kToastExpTriesErrorGroup;
521  };
522  // Write to the |client| key for the last time.
523  SetClient(experiment_group + outcome, true);
524
525  if (outcome != kToastExpUninstallGroup)
526    return;
527  // The user wants to uninstall. This is a best effort operation. Note that
528  // we waited for chrome to exit so the uninstall would not detect chrome
529  // running.
530  bool system_level_toast = CommandLine::ForCurrentProcess()->HasSwitch(
531      switches::kSystemLevelToast);
532
533  CommandLine cmd(InstallUtil::GetChromeUninstallCmd(
534                      system_level_toast, product.distribution()->GetType()));
535  base::LaunchProcess(cmd, base::LaunchOptions(), NULL);
536}
537
538}  // namespace installer
539