user_experiment.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/user_experiment.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#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"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
14a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/launch.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/rand_util.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/scoped_handle.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/windows_version.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/attrition_experiments.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (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"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/google_update_constants.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/google_update_settings.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/helper.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/install_util.h"
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/product.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/common/result_codes.h"
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#pragma comment(lib, "wtsapi32.lib")
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace installer {
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The following strings are the possible outcomes of the toast experiment
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// as recorded in the |client| field.
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpControlGroup[] =        L"01";
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpCancelGroup[] =         L"02";
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpUninstallGroup[] =      L"04";
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpTriesOkGroup[] =        L"18";
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpTriesErrorGroup[] =     L"28";
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastActiveGroup[] =            L"40";
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastUDDirFailure[] =           L"40";
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpBaseGroup[] =           L"80";
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Substitute the locale parameter in uninstall URL with whatever
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Google Update tells us is the locale. In case we fail to find
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the locale, we use US English.
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 LocalizeUrl(const wchar_t* url) {
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 language;
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!GoogleUpdateSettings::GetLanguage(&language))
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    language = L"en-US";  // Default to US English.
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ReplaceStringPlaceholders(url, language.c_str(), NULL);
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 GetWelcomeBackUrl() {
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const wchar_t kWelcomeUrl[] = L"http://www.google.com/chrome/intl/$1/"
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                L"welcomeback-new.html";
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return LocalizeUrl(kWelcomeUrl);
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Converts FILETIME to hours. FILETIME times are absolute times in
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 100 nanosecond units. For example 5:30 pm of June 15, 2009 is 3580464.
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int FileTimeToHours(const FILETIME& time) {
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const ULONGLONG k100sNanoSecsToHours = 10000000LL * 60 * 60;
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ULARGE_INTEGER uli = {time.dwLowDateTime, time.dwHighDateTime};
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return static_cast<int>(uli.QuadPart / k100sNanoSecsToHours);
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns the directory last write time in hours since January 1, 1601.
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns -1 if there was an error retrieving the directory time.
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int GetDirectoryWriteTimeInHours(const wchar_t* path) {
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // To open a directory you need to pass FILE_FLAG_BACKUP_SEMANTICS.
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::win::ScopedHandle file(::CreateFileW(path, 0, share, NULL,
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL));
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!file.IsValid())
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return -1;
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FILETIME time;
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ::GetFileTime(file, NULL, NULL, &time) ? FileTimeToHours(time) : -1;
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns the directory last-write time age in hours, relative to current
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// time, so if it returns 14 it means that the directory was last written 14
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// hours ago. Returns -1 if there was an error retrieving the directory.
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int GetDirectoryWriteAgeInHours(const wchar_t* path) {
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int dir_time = GetDirectoryWriteTimeInHours(path);
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (dir_time < 0)
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return dir_time;
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FILETIME time;
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GetSystemTimeAsFileTime(&time);
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  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)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // 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;
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options.inherit_handles = true;
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return base::LaunchProcess(*cmd_line, options, NULL);
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (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
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// is normally c:\windows\temp. In many cases files inside this folder
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// are not accessible for execution by regular user accounts.
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 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
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // new ACE to the string d) convert sddl string back to DACL and finally
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // e) write new dacl.
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  char buff[1024];
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DWORD len = sizeof(buff);
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  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))
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 new_sddl(sddl);
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ::LocalFree(sddl);
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sd = NULL;
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See MSDN for the  security descriptor definition language (SDDL) syntax,
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // in our case we add "A;" generic read 'GR' and generic execute 'GX' for
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // 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.
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (base::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
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // are the very first one.
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t pos_insert = new_sddl.find(L"(");
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (base::string16::npos == pos_insert)
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // All good, time to change the dacl.
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  new_sddl.insert(pos_insert, kAllowACE);
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(new_sddl.c_str(),
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      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;
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ::LocalFree(sd);
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return rv;
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This function launches setup as the currently logged-in interactive
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// user that is the user whose logon session is attached to winsta0\default.
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 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.
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool LaunchSetupAsConsoleUser(CommandLine* cmd_line) {
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // 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);
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Propagate --verbose-logging to the invoked setup.exe.
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kVerboseLogging))
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cmd_line->AppendSwitch(switches::kVerboseLogging);
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Get the Google Update results key, and pass it on the command line to
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the child process.
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int key = GoogleUpdateSettings::DuplicateGoogleUpdateSystemClientKey();
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  cmd_line->AppendSwitchASCII(switches::kToastResultsKey,
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              base::IntToString(key));
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (base::win::GetVersion() > base::win::VERSION_XP) {
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Make sure that in Vista and Above we have the proper DACLs so
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // the interactive user can launch it.
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!FixDACLsForExecute(cmd_line->GetProgram()))
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED();
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DWORD console_id = ::WTSGetActiveConsoleSessionId();
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (console_id == 0xFFFFFFFF) {
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    PLOG(ERROR) << __FUNCTION__ << " failed to get active session id";
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HANDLE user_token;
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!::WTSQueryUserToken(console_id, &user_token)) {
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    PLOG(ERROR) << __FUNCTION__ << " failed to get user token for console_id "
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                << console_id;
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Note: Handle inheritance must be true in order for the child process to be
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // able to use the duplicated handle above (Google Update results).
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::LaunchOptions options;
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.as_user = user_token;
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.inherit_handles = true;
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.empty_desktop_name = true;
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  VLOG(1) << __FUNCTION__ << " launching " << cmd_line->GetCommandLineString();
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool launched = base::LaunchProcess(*cmd_line, options, NULL);
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ::CloseHandle(user_token);
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  VLOG(1) << __FUNCTION__ << "   result: " << launched;
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return launched;
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// A helper function that writes to HKLM if the handle was passed through the
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// command line, but HKCU otherwise. |experiment_group| is the value to write
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// and |last_write| is used when writing to HKLM to determine whether to close
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the handle when done.
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SetClient(const base::string16& experiment_group, bool last_write) {
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static int reg_key_handle = -1;
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (reg_key_handle == -1) {
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // If a specific Toast Results key handle (presumably to our HKLM key) was
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // passed in to the command line (such as for system level installs), we use
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // it. Otherwise, we write to the key under HKCU.
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (cmd_line.HasSwitch(switches::kToastResultsKey)) {
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Get the handle to the key under HKLM.
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::StringToInt(
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          cmd_line.GetSwitchValueNative(switches::kToastResultsKey),
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          &reg_key_handle);
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      reg_key_handle = 0;
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (reg_key_handle) {
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Use it to write the experiment results.
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GoogleUpdateSettings::WriteGoogleUpdateSystemClientKey(
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        reg_key_handle, google_update::kRegClientField, experiment_group);
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (last_write)
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CloseHandle((HANDLE) reg_key_handle);
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Write to HKCU.
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GoogleUpdateSettings::SetClient(experiment_group);
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool CreateExperimentDetails(int flavor, ExperimentDetails* experiment) {
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  struct FlavorDetails {
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int heading_id;
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int flags;
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  };
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Maximum number of experiment flavors we support.
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static const int kMax = 4;
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This struct determines which experiment flavors we show for each locale and
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // brand.
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  //
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Plugin infobar experiment:
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in 2011 used PIxx codes.
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  //
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Inactive user toast experiment:
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Dec 2009 used TGxx and THxx.
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Feb 2010 used TKxx and TLxx.
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Apr 2010 used TMxx and TNxx.
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Oct 2010 used TVxx TWxx TXxx TYxx.
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Feb 2011 used SJxx SKxx SLxx SMxx.
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Mar 2012 used ZAxx ZBxx ZCxx.
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Jan 2013 uses DAxx.
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  using namespace attrition_experiments;
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static const struct UserExperimentSpecs {
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const wchar_t* locale;  // Locale to show this experiment for (* for all).
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const wchar_t* brands;  // Brand codes show this experiment for (* for all).
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int control_group;      // Size of the control group, in percentages.
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const wchar_t* prefix;  // The two letter experiment code. The second letter
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            // will be incremented with the flavor.
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FlavorDetails flavors[kMax];
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } kExperiments[] = {
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The first match from top to bottom is used so this list should be ordered
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // most-specific rule first.
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    { L"*", L"GGRV",  // All locales, GGRV is enterprise.
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      0,              // 0 percent control group.
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      L"EA",          // Experiment is EAxx, EBxx, etc.
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // No flavors means no experiment.
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      { { 0, 0 },
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 },
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 },
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 }
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    },
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    { L"*", L"*",     // All locales, all brands.
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      5,              // 5 percent control group.
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      L"DA",          // Experiment is DAxx.
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // One single flavor.
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      { { IDS_TRY_TOAST_HEADING3, kToastUiMakeDefault },
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 },
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 },
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 }
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  };
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 locale;
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GoogleUpdateSettings::GetLanguage(&locale);
3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (locale.empty() || (locale == base::ASCIIToWide("en")))
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    locale = base::ASCIIToWide("en-US");
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 brand;
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!GoogleUpdateSettings::GetBrand(&brand))
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    brand = base::ASCIIToWide("");  // Could still be viable for catch-all rules
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = 0; i < arraysize(kExperiments); ++i) {
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (kExperiments[i].locale != locale &&
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        kExperiments[i].locale != base::ASCIIToWide("*"))
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::vector<base::string16> brand_codes;
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::SplitString(kExperiments[i].brands, L',', &brand_codes);
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (brand_codes.empty())
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return false;
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (std::vector<base::string16>::iterator it = brand_codes.begin();
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         it != brand_codes.end(); ++it) {
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (*it != brand && *it != L"*")
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        continue;
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // We have found our match.
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const UserExperimentSpecs& match = kExperiments[i];
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Find out how many flavors we have. Zero means no experiment.
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int num_flavors = 0;
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      while (match.flavors[num_flavors].heading_id) { ++num_flavors; }
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!num_flavors)
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return false;
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (flavor < 0)
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        flavor = base::RandInt(0, num_flavors - 1);
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->flavor = flavor;
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->heading = match.flavors[flavor].heading_id;
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->control_group = match.control_group;
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const wchar_t prefix[] = { match.prefix[0], match.prefix[1] + flavor, 0 };
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->prefix = prefix;
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->flags = match.flavors[flavor].flags;
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Currently we only have one experiment: the inactive user toast. Which only
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// applies for users doing upgrades.
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// There are three scenarios when this function is called:
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 1- Is a per-user-install and it updated: perform the experiment
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 2- Is a system-install and it updated : relaunch as the interactive user
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 3- It has been re-launched from the #2 case. In this case we enter
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//    this function with |system_install| true and a REENTRY_SYS_UPDATE status.
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void LaunchBrowserUserExperiment(const CommandLine& base_cmd_line,
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 InstallStatus status,
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 bool system_level) {
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (system_level) {
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (NEW_VERSION_UPDATED == status) {
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CommandLine cmd_line(base_cmd_line);
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      cmd_line.AppendSwitch(switches::kSystemLevelToast);
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // We need to relaunch as the interactive user.
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      LaunchSetupAsConsoleUser(&cmd_line);
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
398c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (status != NEW_VERSION_UPDATED && status != REENTRY_SYS_UPDATE) {
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // We are not updating or in re-launch. Exit.
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The |flavor| value ends up being processed by TryChromeDialogView to show
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // different experiments.
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExperimentDetails experiment;
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!CreateExperimentDetails(-1, &experiment)) {
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    VLOG(1) << "Failed to get experiment details.";
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int flavor = experiment.flavor;
4125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 base_group = experiment.prefix;
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 brand;
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (GoogleUpdateSettings::GetBrand(&brand) && (brand == L"CHXX")) {
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Testing only: the user automatically qualifies for the experiment.
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    VLOG(1) << "Experiment qualification bypass";
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Check that the user was not already drafted in this experiment.
4205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::string16 client;
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GoogleUpdateSettings::GetClient(&client);
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (client.size() > 2) {
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (base_group == client.substr(0, 2)) {
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        VLOG(1) << "User already participated in this experiment";
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Check browser usage inactivity by the age of the last-write time of the
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // most recently-used chrome user data directory.
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::vector<base::FilePath> user_data_dirs;
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        BrowserDistribution::CHROME_BROWSER);
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GetChromeUserDataPaths(dist, &user_data_dirs);
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int dir_age_hours = -1;
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (size_t i = 0; i < user_data_dirs.size(); ++i) {
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int this_age = GetDirectoryWriteAgeInHours(
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          user_data_dirs[i].value().c_str());
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (this_age >= 0 && (dir_age_hours < 0 || this_age < dir_age_hours))
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        dir_age_hours = this_age;
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const bool experiment_enabled = false;
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int kThirtyDays = 30 * 24;
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!experiment_enabled) {
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      VLOG(1) << "Toast experiment is disabled.";
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (dir_age_hours < 0) {
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // This means that we failed to find the user data dir. The most likely
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // cause is that this user has not ever used chrome at all which can
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // happen in a system-level install.
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SetClient(base_group + kToastUDDirFailure, true);
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (dir_age_hours < kThirtyDays) {
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // An active user, so it does not qualify.
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      VLOG(1) << "Chrome used in last " << dir_age_hours << " hours";
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SetClient(base_group + kToastActiveGroup, true);
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Check to see if this user belongs to the control group.
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    double control_group = 1.0 * (100 - experiment.control_group) / 100;
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (base::RandDouble() > control_group) {
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SetClient(base_group + kToastExpControlGroup, true);
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      VLOG(1) << "User is control group";
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  VLOG(1) << "User drafted for toast experiment " << flavor;
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetClient(base_group + kToastExpBaseGroup, false);
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // User level: The experiment needs to be performed in a different process
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // because google_update expects the upgrade process to be quick and nimble.
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // System level: We have already been relaunched, so we don't need to be
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // quick, but we relaunch to follow the exact same codepath.
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CommandLine cmd_line(base_cmd_line);
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  cmd_line.AppendSwitchASCII(switches::kInactiveUserToast,
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             base::IntToString(flavor));
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  cmd_line.AppendSwitchASCII(switches::kExperimentGroup,
479a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                             base::UTF16ToASCII(base_group));
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  LaunchSetup(&cmd_line, system_level);
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// User qualifies for the experiment. To test, use --try-chrome-again=|flavor|
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// as a parameter to chrome.exe.
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void InactiveUserToastExperiment(int flavor,
4865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 const base::string16& experiment_group,
487c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 const Product& product,
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 const base::FilePath& application_path) {
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Add the 'welcome back' url for chrome to show.
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CommandLine options(CommandLine::NO_PROGRAM);
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.AppendSwitchNative(::switches::kTryChromeAgain,
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::IntToString16(flavor));
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Prepend the url with a space.
4945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 url(GetWelcomeBackUrl());
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.AppendArg("--");
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.AppendArgNative(url);
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The command line should now have the url added as:
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // "chrome.exe -- <url>"
4995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_NE(base::string16::npos,
5005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            options.GetCommandLineString().find(L" -- " + url));
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Launch chrome now. It will show the toast UI.
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int32 exit_code = 0;
504c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!product.LaunchChromeAndWait(application_path, options, &exit_code))
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The chrome process has exited, figure out what happened.
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const wchar_t* outcome = NULL;
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (exit_code) {
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case content::RESULT_CODE_NORMAL_EXIT:
5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      outcome = kToastExpTriesOkGroup;
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case chrome::RESULT_CODE_NORMAL_EXIT_CANCEL:
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      outcome = kToastExpCancelGroup;
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case chrome::RESULT_CODE_NORMAL_EXIT_EXP2:
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      outcome = kToastExpUninstallGroup;
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      outcome = kToastExpTriesErrorGroup;
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  };
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Write to the |client| key for the last time.
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetClient(experiment_group + outcome, true);
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (outcome != kToastExpUninstallGroup)
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The user wants to uninstall. This is a best effort operation. Note that
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // we waited for chrome to exit so the uninstall would not detect chrome
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // running.
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool system_level_toast = CommandLine::ForCurrentProcess()->HasSwitch(
5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      switches::kSystemLevelToast);
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
533c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CommandLine cmd(InstallUtil::GetChromeUninstallCmd(
534c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      system_level_toast, product.distribution()->GetType()));
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::LaunchProcess(cmd, base::LaunchOptions(), NULL);
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace installer
539