diff options
author | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-31 03:59:20 +0000 |
---|---|---|
committer | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-31 03:59:20 +0000 |
commit | ebcb5207a92da2acf2034d4e2d9054b76b41c6d5 (patch) | |
tree | 6d6f2244e08bf66427f9734cf35e9a29dfa6f3d8 | |
parent | 716a6125dd1633606261ba49b30408a466a6fc44 (diff) | |
download | chromium_src-ebcb5207a92da2acf2034d4e2d9054b76b41c6d5.zip chromium_src-ebcb5207a92da2acf2034d4e2d9054b76b41c6d5.tar.gz chromium_src-ebcb5207a92da2acf2034d4e2d9054b76b41c6d5.tar.bz2 |
Implementation of GCAPI reactivation.
Also, some cleanup in gcapi_tests such that it now only runs gtest tests by default.
BUG=111453
TEST=gcapi_tests.exe
Review URL: https://chromiumcodereview.appspot.com/9288056
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119841 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/chrome_installer.gypi | 6 | ||||
-rw-r--r-- | chrome/installer/gcapi/gcapi.cc | 72 | ||||
-rw-r--r-- | chrome/installer/gcapi/gcapi.def | 2 | ||||
-rw-r--r-- | chrome/installer/gcapi/gcapi.h | 45 | ||||
-rw-r--r-- | chrome/installer/gcapi/gcapi_reactivation.cc | 77 | ||||
-rw-r--r-- | chrome/installer/gcapi/gcapi_reactivation.h | 19 | ||||
-rw-r--r-- | chrome/installer/gcapi/gcapi_reactivation_test.cc | 186 | ||||
-rw-r--r-- | chrome/installer/gcapi/gcapi_test.cc | 13 |
8 files changed, 413 insertions, 7 deletions
diff --git a/chrome/chrome_installer.gypi b/chrome/chrome_installer.gypi index 6a9cf37..961c9a8 100644 --- a/chrome/chrome_installer.gypi +++ b/chrome/chrome_installer.gypi @@ -14,6 +14,7 @@ 'target_name': 'gcapi_dll', 'type': 'loadable_module', 'dependencies': [ + 'gcapi_lib', 'installer_util', '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/google_update/google_update.gyp:google_update', @@ -22,9 +23,7 @@ '<(DEPTH)', ], 'sources': [ - 'installer/gcapi/gcapi.cc', 'installer/gcapi/gcapi.def', - 'installer/gcapi/gcapi.h', ], }, { @@ -41,6 +40,8 @@ 'sources': [ 'installer/gcapi/gcapi.cc', 'installer/gcapi/gcapi.h', + 'installer/gcapi/gcapi_reactivation.cc', + 'installer/gcapi/gcapi_reactivation.h', ], }, { @@ -60,6 +61,7 @@ ], 'sources': [ 'installer/gcapi/gcapi_last_run_test.cc', + 'installer/gcapi/gcapi_reactivation_test.cc', 'installer/gcapi/gcapi_test.cc', 'installer/gcapi/gcapi_test.rc', 'installer/gcapi/resource.h', diff --git a/chrome/installer/gcapi/gcapi.cc b/chrome/installer/gcapi/gcapi.cc index bac5179..79d3a3c 100644 --- a/chrome/installer/gcapi/gcapi.cc +++ b/chrome/installer/gcapi/gcapi.cc @@ -29,6 +29,7 @@ #include "base/win/scoped_com_initializer.h" #include "base/win/scoped_comptr.h" #include "base/win/scoped_handle.h" +#include "chrome/installer/gcapi/gcapi_reactivation.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/util_constants.h" @@ -539,3 +540,74 @@ int __stdcall GoogleChromeDaysSinceLastRun() { return days_since_last_run; } + +BOOL __stdcall CanOfferReactivation(const wchar_t* brand_code, + int previous_brand_codes_length, + const wchar_t** previous_brand_codes, + DWORD* error_code) { + DCHECK(error_code); + + if (!brand_code || + (previous_brand_codes_length > 0 && previous_brand_codes == NULL)) { + if (error_code) + *error_code = REACTIVATE_ERROR_INVALID_INPUT; + return FALSE; + } + + bool has_system_install = IsChromeInstalled(HKEY_LOCAL_MACHINE); + bool has_user_install = IsChromeInstalled(HKEY_CURRENT_USER); + + if (!has_system_install && !has_user_install) { + if (error_code) + *error_code = REACTIVATE_ERROR_NOTINSTALLED; + return FALSE; + } + + int days_since_last_run = GoogleChromeDaysSinceLastRun(); + if (days_since_last_run > 0 && + days_since_last_run < kReactivationMinDaysDormant) { + if (error_code) + *error_code = REACTIVATE_ERROR_NOTDORMANT; + return FALSE; + } + + // Make sure we haven't previously been reactivated by this brand code + // or any of the previous brand codes from this partner. + std::vector<std::wstring> reactivation_brands; + reactivation_brands.push_back(brand_code); + if (previous_brand_codes_length > 0 && previous_brand_codes != NULL) { + std::copy(previous_brand_codes, + previous_brand_codes + previous_brand_codes_length, + std::back_inserter(reactivation_brands)); + } + if (HasBeenReactivatedByBrandCodes(reactivation_brands)) { + if (error_code) + *error_code = REACTIVATE_ERROR_ALREADY_REACTIVATED; + return FALSE; + } + + return TRUE; +} + +BOOL __stdcall ReactivateChrome(wchar_t* brand_code, + int previous_brand_codes_length, + const wchar_t** previous_brand_codes, + DWORD* error_code) { + BOOL result = FALSE; + if (CanOfferReactivation(brand_code, + previous_brand_codes_length, + previous_brand_codes, + error_code)) { + if (SetReactivationBrandCode(brand_code)) { + // TODO(robertshield): Set Omaha reg key to add experiment label for + // tracking 7DA. + result = TRUE; + } else { + if (error_code) + *error_code = REACTIVATE_ERROR_REACTIVATION_FAILED; + } + } + + return result; +} + diff --git a/chrome/installer/gcapi/gcapi.def b/chrome/installer/gcapi/gcapi.def index ad7286f..8785c25 100644 --- a/chrome/installer/gcapi/gcapi.def +++ b/chrome/installer/gcapi/gcapi.def @@ -9,3 +9,5 @@ EXPORTS LaunchGoogleChrome PRIVATE LaunchGoogleChromeWithDimensions @16 PRIVATE GoogleChromeDaysSinceLastRun PRIVATE + CanOfferReactivation PRIVATE + ReactivateChrome PRIVATE diff --git a/chrome/installer/gcapi/gcapi.h b/chrome/installer/gcapi/gcapi.h index a9fd040..30a5226 100644 --- a/chrome/installer/gcapi/gcapi.h +++ b/chrome/installer/gcapi/gcapi.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,6 +17,17 @@ extern "C" { #define GCCC_ERROR_ALREADYOFFERED 0x10 #define GCCC_ERROR_INTEGRITYLEVEL 0x20 +// Error conditions for CanReactivateChrome(). +#define REACTIVATE_ERROR_NOTINSTALLED 0x01 +#define REACTIVATE_ERROR_NOTDORMANT 0x02 +#define REACTIVATE_ERROR_ALREADY_REACTIVATED 0x04 +#define REACTIVATE_ERROR_INVALID_INPUT 0x08 +#define REACTIVATE_ERROR_REACTIVATION_FAILED 0x10 + +// The minimum number of days an installation can be dormant before reactivation +// may be offered. +const int kReactivationMinDaysDormant = 50; + // This function returns TRUE if Google Chrome should be offered. // If the return is FALSE, the reasons DWORD explains why. If you don't care // for the reason, you can pass NULL for reasons. @@ -54,11 +65,41 @@ BOOL __stdcall LaunchGoogleChromeWithDimensions(int x, // launched. int __stdcall GoogleChromeDaysSinceLastRun(); -// Funtion pointer type declarations to use with GetProcAddress. +// Returns true if a vendor with the specified |brand_code| may offer +// reactivation at this time. If the vendor has previously used other brand +// codes, they must pass them in an array of size |previous_brand_codes_length| +// as |previous_brand_codes|. Returns false if the vendor may not offer +// reactivation at this time, and places one of the REACTIVATE_ERROR_XXX values +// in |error_code| if |error_code| is non-null. +BOOL __stdcall CanOfferReactivation(const wchar_t* brand_code, + int previous_brand_codes_length, + const wchar_t** previous_brand_codes, + DWORD* error_code); + +// Attempts to reactivate Chrome for the specified |brand_code|. If the vendor +// has previously used other brand codes, they must pass them in an array of +// size |previous_brand_codes_length| as |previous_brand_codes|. Returns false +// if reactivation fails, and places one of the REACTIVATE_ERROR_XXX values +// in |error_code| if |error_code| is non-null. +BOOL __stdcall ReactivateChrome(wchar_t* brand_code, + int previous_brand_codes_length, + const wchar_t** previous_brand_codes, + DWORD* error_code); + +// Function pointer type declarations to use with GetProcAddress. typedef BOOL (__stdcall *GCCC_CompatibilityCheck)(BOOL, DWORD *); typedef BOOL (__stdcall *GCCC_LaunchGC)(HANDLE *); typedef BOOL (__stdcall *GCCC_LaunchGCWithDimensions)(int, int, int, int); typedef int (__stdcall *GCCC_GoogleChromeDaysSinceLastRun)(); +typedef BOOL (__stdcall *GCCC_CanOfferReactivation)(const wchar_t*, + int, + const wchar_t**, + DWORD*); +typedef BOOL (__stdcall *GCCC_ReactivateChrome)(const wchar_t*, + int, + const wchar_t**, + DWORD*); + } // extern "C" #endif // CHROME_INSTALLER_GCAPI_GCAPI_H_ diff --git a/chrome/installer/gcapi/gcapi_reactivation.cc b/chrome/installer/gcapi/gcapi_reactivation.cc new file mode 100644 index 0000000..6755e2f --- /dev/null +++ b/chrome/installer/gcapi/gcapi_reactivation.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/installer/gcapi/gcapi_reactivation.h" + +#include "base/time.h" +#include "base/win/registry.h" +#include "chrome/installer/util/google_update_constants.h" + +using base::Time; +using base::win::RegKey; + +namespace { +const wchar_t kReactivationHistoryKey[] = L"reactivation"; + +std::wstring GetReactivationHistoryKeyPath() { + std::wstring reactivation_path(google_update::kRegPathClientState); + reactivation_path += L"\\"; + reactivation_path += google_update::kChromeUpgradeCode; + reactivation_path += L"\\"; + reactivation_path += kReactivationHistoryKey; + return reactivation_path; +} +} // namespace + +bool HasBeenReactivatedByBrandCodes( + const std::vector<std::wstring>& brand_codes) { + bool success = false; + + RegKey reactivation_key(HKEY_CURRENT_USER, + GetReactivationHistoryKeyPath().c_str(), + KEY_QUERY_VALUE); + if (reactivation_key.Valid()) { + std::vector<std::wstring>::const_iterator brand_iter(brand_codes.begin()); + for (; brand_iter != brand_codes.end(); ++brand_iter) { + if (reactivation_key.HasValue(brand_iter->c_str())) { + success = true; + break; + } + } + } + + return success; +} + +bool SetReactivationBrandCode(const std::wstring& brand_code) { + bool success = false; + + std::wstring path(google_update::kRegPathClientState); + path += L"\\"; + path += google_update::kChromeUpgradeCode; + + RegKey client_state_key(HKEY_CURRENT_USER, path.c_str(), KEY_SET_VALUE); + if (client_state_key.Valid()) { + success = client_state_key.WriteValue( + google_update::kRegRLZReactivationBrandField, + brand_code.c_str()) == ERROR_SUCCESS; + } + + if (success) { + // Store this brand code in the reactivation history. Store it with a + // a currently un-used timestamp for future proofing. + RegKey reactivation_key(HKEY_CURRENT_USER, + GetReactivationHistoryKeyPath().c_str(), + KEY_WRITE); + if (reactivation_key.Valid()) { + int64 timestamp = Time::Now().ToInternalValue(); + reactivation_key.WriteValue(brand_code.c_str(), + ×tamp, + sizeof(timestamp), + REG_QWORD); + } + } + + return success; +}
\ No newline at end of file diff --git a/chrome/installer/gcapi/gcapi_reactivation.h b/chrome/installer/gcapi/gcapi_reactivation.h new file mode 100644 index 0000000..5ace0b8 --- /dev/null +++ b/chrome/installer/gcapi/gcapi_reactivation.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_INSTALLER_GCAPI_GCAPI_REACTIVATION_H_ +#define CHROME_INSTALLER_GCAPI_GCAPI_REACTIVATION_H_ +#pragma once + +#include <windows.h> + +#include <string> +#include <vector> + +bool HasBeenReactivatedByBrandCodes( + const std::vector<std::wstring>& brand_codes); + +bool SetReactivationBrandCode(const std::wstring& brand_code); + +#endif // CHROME_INSTALLER_GCAPI_GCAPI_REACTIVATION_H_ diff --git a/chrome/installer/gcapi/gcapi_reactivation_test.cc b/chrome/installer/gcapi/gcapi_reactivation_test.cc new file mode 100644 index 0000000..9cadafc --- /dev/null +++ b/chrome/installer/gcapi/gcapi_reactivation_test.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/basictypes.h" +#include "base/string_number_conversions.h" +#include "base/stringprintf.h" +#include "base/test/test_reg_util_win.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "base/win/registry.h" +#include "chrome/common/guid.h" +#include "chrome/installer/gcapi/gcapi.h" +#include "chrome/installer/gcapi/gcapi_reactivation.h" +#include "chrome/installer/util/google_update_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using base::TimeDelta; +using base::win::RegKey; + + +class GCAPIReactivationTest : public ::testing::Test { + protected: + void SetUp() { + // Override keys - this is undone during destruction. + std::wstring hkcu_override = base::StringPrintf( + L"hkcu_override\\%ls", ASCIIToWide(guid::GenerateGUID())); + override_manager_.OverrideRegistry(HKEY_CURRENT_USER, hkcu_override); + } + + bool SetChromeInstallMarker(HKEY hive) { + // Create the client state keys in the right places. + std::wstring reg_path(google_update::kRegPathClients); + reg_path += L"\\"; + reg_path += google_update::kChromeUpgradeCode; + RegKey client_state(hive, + reg_path.c_str(), + KEY_CREATE_SUB_KEY | KEY_SET_VALUE); + return (client_state.Valid() && + client_state.WriteValue( + google_update::kRegVersionField, L"1.2.3.4") == ERROR_SUCCESS); + } + + bool SetLastRunTime(HKEY hive, int64 last_run_time) { + return SetLastRunTimeString(hive, base::Int64ToString16(last_run_time)); + } + + bool SetLastRunTimeString(HKEY hive, const string16& last_run_time_string) { + const wchar_t* base_path = + (hive == HKEY_LOCAL_MACHINE) ? + google_update::kRegPathClientStateMedium : + google_update::kRegPathClientState; + std::wstring path(base_path); + path += L"\\"; + path += google_update::kChromeUpgradeCode; + + RegKey client_state(hive, path.c_str(), KEY_SET_VALUE); + return (client_state.Valid() && + client_state.WriteValue( + google_update::kRegLastRunTimeField, + last_run_time_string.c_str()) == ERROR_SUCCESS); + } + + std::wstring GetReactivationString(HKEY hive) { + const wchar_t* base_path = + (hive == HKEY_LOCAL_MACHINE) ? + google_update::kRegPathClientStateMedium : + google_update::kRegPathClientState; + std::wstring path(base_path); + path += L"\\"; + path += google_update::kChromeUpgradeCode; + + RegKey client_state(hive, path.c_str(), KEY_QUERY_VALUE); + if (client_state.Valid()) { + std::wstring actual_brand; + if (client_state.ReadValue(google_update::kRegRLZReactivationBrandField, + &actual_brand) == ERROR_SUCCESS) { + return actual_brand; + } + } + + return L"ERROR"; + } + + private: + registry_util::RegistryOverrideManager override_manager_; +}; + +TEST_F(GCAPIReactivationTest, CheckSetReactivationBrandCode) { + EXPECT_TRUE(SetReactivationBrandCode(L"GAGA")); + EXPECT_EQ(L"GAGA", GetReactivationString(HKEY_CURRENT_USER)); + + std::vector<std::wstring> check_codes; + check_codes.push_back(L"GAGA"); + EXPECT_TRUE(HasBeenReactivatedByBrandCodes(check_codes)); + + check_codes.push_back(L"GOOGOO"); + EXPECT_TRUE(HasBeenReactivatedByBrandCodes(check_codes)); + + check_codes.erase(check_codes.begin()); + EXPECT_FALSE(HasBeenReactivatedByBrandCodes(check_codes)); +} + +TEST_F(GCAPIReactivationTest, CanOfferReactivation_Basic) { + const wchar_t* previous_brands[] = {L"GOOGOO", L"MAMA", L"DADA"}; + DWORD error; + + // We're not installed yet. Make sure CanOfferReactivation fails. + EXPECT_FALSE(CanOfferReactivation(L"GAGA", arraysize(previous_brands), + previous_brands, &error)); + EXPECT_EQ(REACTIVATE_ERROR_NOTINSTALLED, error); + + // Now pretend to be installed. CanOfferReactivation should pass. + EXPECT_TRUE(SetChromeInstallMarker(HKEY_CURRENT_USER)); + EXPECT_TRUE(CanOfferReactivation(L"GAGA", arraysize(previous_brands), + previous_brands, &error)); + + // Now set a recent last_run value. CanOfferReactivation should fail again. + Time hkcu_last_run = Time::NowFromSystemTime() - TimeDelta::FromDays(20); + EXPECT_TRUE(SetLastRunTime(HKEY_CURRENT_USER, + hkcu_last_run.ToInternalValue())); + EXPECT_FALSE(CanOfferReactivation(L"GAGA", arraysize(previous_brands), + previous_brands, &error)); + EXPECT_EQ(REACTIVATE_ERROR_NOTDORMANT, error); + + // Now set a last_run value that exceeds the threshold. + hkcu_last_run = Time::NowFromSystemTime() - + TimeDelta::FromDays(kReactivationMinDaysDormant); + EXPECT_TRUE(SetLastRunTime(HKEY_CURRENT_USER, + hkcu_last_run.ToInternalValue())); + EXPECT_TRUE(CanOfferReactivation(L"GAGA", arraysize(previous_brands), + previous_brands, &error)); + + // Test some invalid inputs + EXPECT_FALSE(CanOfferReactivation(NULL, arraysize(previous_brands), + previous_brands, &error)); + EXPECT_EQ(REACTIVATE_ERROR_INVALID_INPUT, error); + EXPECT_FALSE(CanOfferReactivation(L"GAGA", arraysize(previous_brands), + NULL, &error)); + EXPECT_EQ(REACTIVATE_ERROR_INVALID_INPUT, error); + + // One more valid one + EXPECT_TRUE(CanOfferReactivation(L"GAGA", 0, NULL, &error)); + + // Check that the previous brands check works: + EXPECT_TRUE(SetReactivationBrandCode(L"GOOGOO")); + EXPECT_FALSE(CanOfferReactivation(L"GAGA", arraysize(previous_brands), + previous_brands, &error)); + EXPECT_EQ(REACTIVATE_ERROR_ALREADY_REACTIVATED, error); +} + +TEST_F(GCAPIReactivationTest, Reactivation_Flow) { + const wchar_t* previous_brands[] = {L"GOOGOO", L"MAMA", L"DADA"}; + DWORD error; + + // Set us up as a candidate for reactivation. + EXPECT_TRUE(SetChromeInstallMarker(HKEY_CURRENT_USER)); + + Time hkcu_last_run = Time::NowFromSystemTime() - + TimeDelta::FromDays(kReactivationMinDaysDormant); + EXPECT_TRUE(SetLastRunTime(HKEY_CURRENT_USER, + hkcu_last_run.ToInternalValue())); + + EXPECT_TRUE(ReactivateChrome(L"GAGA", arraysize(previous_brands), + previous_brands, &error)); + EXPECT_EQ(L"GAGA", GetReactivationString(HKEY_CURRENT_USER)); + + // Make sure we can't reactivate again: + EXPECT_FALSE(ReactivateChrome(L"GAGA", arraysize(previous_brands), + previous_brands, &error)); + EXPECT_EQ(REACTIVATE_ERROR_ALREADY_REACTIVATED, error); + + // Should still be able to reactivate under other brands: + EXPECT_TRUE(ReactivateChrome(L"MAMA", arraysize(previous_brands), + previous_brands, &error)); + EXPECT_EQ(L"MAMA", GetReactivationString(HKEY_CURRENT_USER)); + + // Validate that previous_brands are rejected: + EXPECT_FALSE(ReactivateChrome(L"PFFT", arraysize(previous_brands), + previous_brands, &error)); + EXPECT_EQ(REACTIVATE_ERROR_ALREADY_REACTIVATED, error); + EXPECT_EQ(L"MAMA", GetReactivationString(HKEY_CURRENT_USER)); +} diff --git a/chrome/installer/gcapi/gcapi_test.cc b/chrome/installer/gcapi/gcapi_test.cc index 2100744..b7f08bb 100644 --- a/chrome/installer/gcapi/gcapi_test.cc +++ b/chrome/installer/gcapi/gcapi_test.cc @@ -4,6 +4,7 @@ #include <stdio.h> +#include "base/command_line.h" #include "chrome/installer/gcapi/gcapi.h" #include "testing/gtest/include/gtest/gtest.h" @@ -54,11 +55,17 @@ void call_dynamically() { FreeLibrary(module); } +const char kManualLaunchTests[] = "launch-chrome"; + int main(int argc, char* argv[]) { + CommandLine::Init(argc, argv); + testing::InitGoogleTest(&argc, argv); RUN_ALL_TESTS(); - call_dynamically(); - call_statically(); - printf("LaunchChrome returned %d.\n", LaunchGoogleChrome()); + if (CommandLine::ForCurrentProcess()->HasSwitch(kManualLaunchTests)) { + call_dynamically(); + call_statically(); + printf("LaunchChrome returned %d.\n", LaunchGoogleChrome()); + } } |