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"
14010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "base/path_service.h"
15a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/launch.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/rand_util.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
20868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/scoped_handle.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/windows_version.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/attrition_experiments.h"
24010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "chrome/common/chrome_paths.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/chrome_result_codes.h"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/chrome_switches.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/browser_distribution.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/google_update_constants.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/google_update_settings.h"
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/helper.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/install_util.h"
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/installer/util/product.h"
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/common/result_codes.h"
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#pragma comment(lib, "wtsapi32.lib")
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace installer {
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The following strings are the possible outcomes of the toast experiment
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// as recorded in the |client| field.
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpControlGroup[] =        L"01";
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpCancelGroup[] =         L"02";
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpUninstallGroup[] =      L"04";
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpTriesOkGroup[] =        L"18";
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpTriesErrorGroup[] =     L"28";
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastActiveGroup[] =            L"40";
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastUDDirFailure[] =           L"40";
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kToastExpBaseGroup[] =           L"80";
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Substitute the locale parameter in uninstall URL with whatever
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Google Update tells us is the locale. In case we fail to find
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the locale, we use US English.
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 LocalizeUrl(const wchar_t* url) {
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 language;
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!GoogleUpdateSettings::GetLanguage(&language))
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    language = L"en-US";  // Default to US English.
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ReplaceStringPlaceholders(url, language.c_str(), NULL);
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 GetWelcomeBackUrl() {
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const wchar_t kWelcomeUrl[] = L"http://www.google.com/chrome/intl/$1/"
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                L"welcomeback-new.html";
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return LocalizeUrl(kWelcomeUrl);
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Converts FILETIME to hours. FILETIME times are absolute times in
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 100 nanosecond units. For example 5:30 pm of June 15, 2009 is 3580464.
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int FileTimeToHours(const FILETIME& time) {
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const ULONGLONG k100sNanoSecsToHours = 10000000LL * 60 * 60;
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ULARGE_INTEGER uli = {time.dwLowDateTime, time.dwHighDateTime};
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return static_cast<int>(uli.QuadPart / k100sNanoSecsToHours);
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns the directory last write time in hours since January 1, 1601.
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns -1 if there was an error retrieving the directory time.
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int GetDirectoryWriteTimeInHours(const wchar_t* path) {
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // To open a directory you need to pass FILE_FLAG_BACKUP_SEMANTICS.
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::win::ScopedHandle file(::CreateFileW(path, 0, share, NULL,
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL));
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!file.IsValid())
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return -1;
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FILETIME time;
871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return ::GetFileTime(file.Get(), NULL, NULL, &time) ?
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      FileTimeToHours(time) : -1;
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
91010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Returns the time in hours since the last write to the user data directory.
92010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// A return value of 14 means that the directory was last written 14 hours ago.
93010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Returns -1 if there was an error retrieving the directory.
94010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)int GetUserDataDirectoryWriteAgeInHours() {
95010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  base::FilePath user_data_dir;
96010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
97010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return -1;
98010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int dir_time = GetDirectoryWriteTimeInHours(user_data_dir.value().c_str());
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (dir_time < 0)
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return dir_time;
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FILETIME time;
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GetSystemTimeAsFileTime(&time);
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int now_time = FileTimeToHours(time);
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (dir_time >= now_time)
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 0;
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (now_time - dir_time);
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Launches setup.exe (located at |setup_path|) with |cmd_line|.
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// If system_level_toast is true, appends --system-level-toast.
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// If handle to experiment result key was given at startup, re-add it.
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Does not wait for the process to terminate.
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |cmd_line| may be modified as a result of this call.
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool LaunchSetup(CommandLine* cmd_line, bool system_level_toast) {
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Propagate --verbose-logging to the invoked setup.exe.
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (current_cmd_line.HasSwitch(switches::kVerboseLogging))
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cmd_line->AppendSwitch(switches::kVerboseLogging);
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Re-add the system level toast flag.
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (system_level_toast) {
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cmd_line->AppendSwitch(switches::kSystemLevel);
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cmd_line->AppendSwitch(switches::kSystemLevelToast);
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Re-add the toast result key. We need to do this because Setup running as
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // system passes the key to Setup running as user, but that child process
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // does not perform the actual toasting, it launches another Setup (as user)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // to do so. That is the process that needs the key.
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string key(switches::kToastResultsKey);
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string toast_key = current_cmd_line.GetSwitchValueASCII(key);
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!toast_key.empty()) {
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      cmd_line->AppendSwitchASCII(key, toast_key);
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Use handle inheritance to make sure the duplicated toast results key
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // gets inherited by the child process.
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::LaunchOptions options;
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options.inherit_handles = true;
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return base::LaunchProcess(*cmd_line, options, NULL);
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return base::LaunchProcess(*cmd_line, base::LaunchOptions(), NULL);
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// For System level installs, setup.exe lives in the system temp, which
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// is normally c:\windows\temp. In many cases files inside this folder
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// are not accessible for execution by regular user accounts.
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This function changes the permissions so that any authenticated user
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// can launch |exe| later on. This function should only be called if the
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// code is running at the system level.
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool FixDACLsForExecute(const base::FilePath& exe) {
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The general strategy to is to add an ACE to the exe DACL the quick
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // and dirty way: a) read the DACL b) convert it to sddl string c) add the
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // new ACE to the string d) convert sddl string back to DACL and finally
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // e) write new dacl.
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  char buff[1024];
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DWORD len = sizeof(buff);
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PSECURITY_DESCRIPTOR sd = reinterpret_cast<PSECURITY_DESCRIPTOR>(buff);
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!::GetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION,
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          sd, len, &len)) {
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  wchar_t* sddl = 0;
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(sd,
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddl, NULL))
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 new_sddl(sddl);
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ::LocalFree(sddl);
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sd = NULL;
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See MSDN for the  security descriptor definition language (SDDL) syntax,
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // in our case we add "A;" generic read 'GR' and generic execute 'GX' for
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the nt\authenticated_users 'AU' group, that becomes:
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const wchar_t kAllowACE[] = L"(A;;GRGX;;;AU)";
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We should check that there are no special ACES for the group we
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // are interested, which is nt\authenticated_users.
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (base::string16::npos != new_sddl.find(L";AU)"))
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Specific ACEs (not inherited) need to go to the front. It is ok if we
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // are the very first one.
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t pos_insert = new_sddl.find(L"(");
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (base::string16::npos == pos_insert)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // All good, time to change the dacl.
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  new_sddl.insert(pos_insert, kAllowACE);
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(new_sddl.c_str(),
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SDDL_REVISION_1, &sd, NULL))
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool rv = ::SetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION,
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               sd) == TRUE;
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ::LocalFree(sd);
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return rv;
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This function launches setup as the currently logged-in interactive
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// user that is the user whose logon session is attached to winsta0\default.
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// It assumes that currently we are running as SYSTEM in a non-interactive
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// windowstation.
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The function fails if there is no interactive session active, basically
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the computer is on but nobody has logged in locally.
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Remote Desktop sessions do not count as interactive sessions; running this
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// method as a user logged in via remote desktop will do nothing.
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool LaunchSetupAsConsoleUser(CommandLine* cmd_line) {
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Convey to the invoked setup.exe that it's operating on a system-level
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // installation.
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  cmd_line->AppendSwitch(switches::kSystemLevel);
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Propagate --verbose-logging to the invoked setup.exe.
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kVerboseLogging))
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cmd_line->AppendSwitch(switches::kVerboseLogging);
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Get the Google Update results key, and pass it on the command line to
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the child process.
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int key = GoogleUpdateSettings::DuplicateGoogleUpdateSystemClientKey();
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  cmd_line->AppendSwitchASCII(switches::kToastResultsKey,
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              base::IntToString(key));
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (base::win::GetVersion() > base::win::VERSION_XP) {
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Make sure that in Vista and Above we have the proper DACLs so
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // the interactive user can launch it.
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!FixDACLsForExecute(cmd_line->GetProgram()))
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED();
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DWORD console_id = ::WTSGetActiveConsoleSessionId();
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (console_id == 0xFFFFFFFF) {
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    PLOG(ERROR) << __FUNCTION__ << " failed to get active session id";
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HANDLE user_token;
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!::WTSQueryUserToken(console_id, &user_token)) {
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    PLOG(ERROR) << __FUNCTION__ << " failed to get user token for console_id "
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                << console_id;
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Note: Handle inheritance must be true in order for the child process to be
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // able to use the duplicated handle above (Google Update results).
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::LaunchOptions options;
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.as_user = user_token;
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.inherit_handles = true;
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.empty_desktop_name = true;
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  VLOG(1) << __FUNCTION__ << " launching " << cmd_line->GetCommandLineString();
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool launched = base::LaunchProcess(*cmd_line, options, NULL);
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ::CloseHandle(user_token);
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  VLOG(1) << __FUNCTION__ << "   result: " << launched;
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return launched;
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// A helper function that writes to HKLM if the handle was passed through the
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// command line, but HKCU otherwise. |experiment_group| is the value to write
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// and |last_write| is used when writing to HKLM to determine whether to close
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the handle when done.
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SetClient(const base::string16& experiment_group, bool last_write) {
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static int reg_key_handle = -1;
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (reg_key_handle == -1) {
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // If a specific Toast Results key handle (presumably to our HKLM key) was
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // passed in to the command line (such as for system level installs), we use
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // it. Otherwise, we write to the key under HKCU.
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (cmd_line.HasSwitch(switches::kToastResultsKey)) {
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Get the handle to the key under HKLM.
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::StringToInt(
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          cmd_line.GetSwitchValueNative(switches::kToastResultsKey),
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          &reg_key_handle);
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      reg_key_handle = 0;
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (reg_key_handle) {
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Use it to write the experiment results.
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GoogleUpdateSettings::WriteGoogleUpdateSystemClientKey(
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        reg_key_handle, google_update::kRegClientField, experiment_group);
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (last_write)
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CloseHandle((HANDLE) reg_key_handle);
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Write to HKCU.
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GoogleUpdateSettings::SetClient(experiment_group);
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool CreateExperimentDetails(int flavor, ExperimentDetails* experiment) {
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  struct FlavorDetails {
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int heading_id;
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int flags;
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  };
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Maximum number of experiment flavors we support.
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static const int kMax = 4;
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This struct determines which experiment flavors we show for each locale and
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // brand.
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  //
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Plugin infobar experiment:
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in 2011 used PIxx codes.
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  //
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Inactive user toast experiment:
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Dec 2009 used TGxx and THxx.
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Feb 2010 used TKxx and TLxx.
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Apr 2010 used TMxx and TNxx.
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Oct 2010 used TVxx TWxx TXxx TYxx.
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Feb 2011 used SJxx SKxx SLxx SMxx.
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Mar 2012 used ZAxx ZBxx ZCxx.
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The experiment in Jan 2013 uses DAxx.
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  using namespace attrition_experiments;
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static const struct UserExperimentSpecs {
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const wchar_t* locale;  // Locale to show this experiment for (* for all).
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const wchar_t* brands;  // Brand codes show this experiment for (* for all).
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int control_group;      // Size of the control group, in percentages.
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const wchar_t* prefix;  // The two letter experiment code. The second letter
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            // will be incremented with the flavor.
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FlavorDetails flavors[kMax];
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } kExperiments[] = {
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The first match from top to bottom is used so this list should be ordered
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // most-specific rule first.
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    { L"*", L"GGRV",  // All locales, GGRV is enterprise.
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      0,              // 0 percent control group.
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      L"EA",          // Experiment is EAxx, EBxx, etc.
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // No flavors means no experiment.
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      { { 0, 0 },
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 },
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 },
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 }
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    },
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    { L"*", L"*",     // All locales, all brands.
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      5,              // 5 percent control group.
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      L"DA",          // Experiment is DAxx.
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // One single flavor.
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      { { IDS_TRY_TOAST_HEADING3, kToastUiMakeDefault },
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 },
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 },
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        { 0, 0 }
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  };
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 locale;
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GoogleUpdateSettings::GetLanguage(&locale);
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (locale.empty() || (locale == base::ASCIIToWide("en")))
3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    locale = base::ASCIIToWide("en-US");
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 brand;
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!GoogleUpdateSettings::GetBrand(&brand))
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    brand = base::ASCIIToWide("");  // Could still be viable for catch-all rules
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = 0; i < arraysize(kExperiments); ++i) {
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (kExperiments[i].locale != locale &&
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        kExperiments[i].locale != base::ASCIIToWide("*"))
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::vector<base::string16> brand_codes;
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::SplitString(kExperiments[i].brands, L',', &brand_codes);
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (brand_codes.empty())
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return false;
3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (std::vector<base::string16>::iterator it = brand_codes.begin();
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         it != brand_codes.end(); ++it) {
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (*it != brand && *it != L"*")
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        continue;
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // We have found our match.
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const UserExperimentSpecs& match = kExperiments[i];
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Find out how many flavors we have. Zero means no experiment.
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int num_flavors = 0;
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      while (match.flavors[num_flavors].heading_id) { ++num_flavors; }
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!num_flavors)
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return false;
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (flavor < 0)
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        flavor = base::RandInt(0, num_flavors - 1);
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->flavor = flavor;
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->heading = match.flavors[flavor].heading_id;
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->control_group = match.control_group;
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const wchar_t prefix[] = { match.prefix[0], match.prefix[1] + flavor, 0 };
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->prefix = prefix;
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      experiment->flags = match.flavors[flavor].flags;
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Currently we only have one experiment: the inactive user toast. Which only
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// applies for users doing upgrades.
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// There are three scenarios when this function is called:
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 1- Is a per-user-install and it updated: perform the experiment
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 2- Is a system-install and it updated : relaunch as the interactive user
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 3- It has been re-launched from the #2 case. In this case we enter
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//    this function with |system_install| true and a REENTRY_SYS_UPDATE status.
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void LaunchBrowserUserExperiment(const CommandLine& base_cmd_line,
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 InstallStatus status,
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 bool system_level) {
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (system_level) {
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (NEW_VERSION_UPDATED == status) {
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CommandLine cmd_line(base_cmd_line);
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      cmd_line.AppendSwitch(switches::kSystemLevelToast);
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // We need to relaunch as the interactive user.
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      LaunchSetupAsConsoleUser(&cmd_line);
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
404c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (status != NEW_VERSION_UPDATED && status != REENTRY_SYS_UPDATE) {
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // We are not updating or in re-launch. Exit.
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The |flavor| value ends up being processed by TryChromeDialogView to show
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // different experiments.
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExperimentDetails experiment;
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!CreateExperimentDetails(-1, &experiment)) {
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    VLOG(1) << "Failed to get experiment details.";
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int flavor = experiment.flavor;
4185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 base_group = experiment.prefix;
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 brand;
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (GoogleUpdateSettings::GetBrand(&brand) && (brand == L"CHXX")) {
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Testing only: the user automatically qualifies for the experiment.
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    VLOG(1) << "Experiment qualification bypass";
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Check that the user was not already drafted in this experiment.
4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::string16 client;
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GoogleUpdateSettings::GetClient(&client);
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (client.size() > 2) {
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (base_group == client.substr(0, 2)) {
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        VLOG(1) << "User already participated in this experiment";
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const bool experiment_enabled = false;
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!experiment_enabled) {
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      VLOG(1) << "Toast experiment is disabled.";
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
438010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
439010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
440010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    // Check browser usage inactivity by the age of the last-write time of the
441010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    // relevant chrome user data directory.
442010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    const int kThirtyDays = 30 * 24;
443010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    const int dir_age_hours = GetUserDataDirectoryWriteAgeInHours();
444010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (dir_age_hours < 0) {
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // This means that we failed to find the user data dir. The most likely
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // cause is that this user has not ever used chrome at all which can
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // happen in a system-level install.
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SetClient(base_group + kToastUDDirFailure, true);
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (dir_age_hours < kThirtyDays) {
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // An active user, so it does not qualify.
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      VLOG(1) << "Chrome used in last " << dir_age_hours << " hours";
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SetClient(base_group + kToastActiveGroup, true);
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Check to see if this user belongs to the control group.
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    double control_group = 1.0 * (100 - experiment.control_group) / 100;
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (base::RandDouble() > control_group) {
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SetClient(base_group + kToastExpControlGroup, true);
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      VLOG(1) << "User is control group";
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  VLOG(1) << "User drafted for toast experiment " << flavor;
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetClient(base_group + kToastExpBaseGroup, false);
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // User level: The experiment needs to be performed in a different process
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // because google_update expects the upgrade process to be quick and nimble.
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // System level: We have already been relaunched, so we don't need to be
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // quick, but we relaunch to follow the exact same codepath.
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CommandLine cmd_line(base_cmd_line);
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  cmd_line.AppendSwitchASCII(switches::kInactiveUserToast,
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             base::IntToString(flavor));
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  cmd_line.AppendSwitchASCII(switches::kExperimentGroup,
475a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                             base::UTF16ToASCII(base_group));
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  LaunchSetup(&cmd_line, system_level);
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// User qualifies for the experiment. To test, use --try-chrome-again=|flavor|
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// as a parameter to chrome.exe.
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void InactiveUserToastExperiment(int flavor,
4825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 const base::string16& experiment_group,
483c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 const Product& product,
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 const base::FilePath& application_path) {
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Add the 'welcome back' url for chrome to show.
4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CommandLine options(CommandLine::NO_PROGRAM);
4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.AppendSwitchNative(::switches::kTryChromeAgain,
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::IntToString16(flavor));
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Prepend the url with a space.
4905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 url(GetWelcomeBackUrl());
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.AppendArg("--");
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.AppendArgNative(url);
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The command line should now have the url added as:
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // "chrome.exe -- <url>"
4955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_NE(base::string16::npos,
4965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            options.GetCommandLineString().find(L" -- " + url));
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Launch chrome now. It will show the toast UI.
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int32 exit_code = 0;
500c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!product.LaunchChromeAndWait(application_path, options, &exit_code))
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The chrome process has exited, figure out what happened.
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const wchar_t* outcome = NULL;
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (exit_code) {
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case content::RESULT_CODE_NORMAL_EXIT:
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      outcome = kToastExpTriesOkGroup;
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case chrome::RESULT_CODE_NORMAL_EXIT_CANCEL:
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      outcome = kToastExpCancelGroup;
5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case chrome::RESULT_CODE_NORMAL_EXIT_EXP2:
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      outcome = kToastExpUninstallGroup;
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      outcome = kToastExpTriesErrorGroup;
5171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Write to the |client| key for the last time.
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetClient(experiment_group + outcome, true);
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (outcome != kToastExpUninstallGroup)
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The user wants to uninstall. This is a best effort operation. Note that
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // we waited for chrome to exit so the uninstall would not detect chrome
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // running.
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool system_level_toast = CommandLine::ForCurrentProcess()->HasSwitch(
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      switches::kSystemLevelToast);
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
529c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CommandLine cmd(InstallUtil::GetChromeUninstallCmd(
530c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      system_level_toast, product.distribution()->GetType()));
5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::LaunchProcess(cmd, base::LaunchOptions(), NULL);
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace installer
535