Commit c72dbb1c authored by Avi Drissman's avatar Avi Drissman Committed by Commit Bot

Update Keystone glue for SxS

Each SxS-capable Chrome channel can have a different brand code, so
update the Keystone glue to know that.

This can be tricky because the old, non-SxS-capable Chromes are still
built from this code, so be sure to handle that case.

Overview plan at https://docs.google.com/document/d/1MVANxDLPPeN1x-NsfgsLSjbX8twzQi69Wb1iPK-owaU/edit

Bug: 1061486
Change-Id: I2cbc2d6ba698b085ee00536a1fbfffa3336ddecd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2102642
Commit-Queue: Avi Drissman <avi@chromium.org>
Reviewed-by: default avatarMark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#752637}
parent b2237f72
...@@ -39,41 +39,49 @@ namespace { ...@@ -39,41 +39,49 @@ namespace {
namespace ksr = keystone_registration; namespace ksr = keystone_registration;
// Constants for the brand file (uses an external file so it can survive
// updates to Chrome.)
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
#define kStableBrandFileName @"Google Chrome Brand.plist"
#define kCanaryBrandFileName @"Google Chrome Canary Brand.plist"
#elif BUILDFLAG(CHROMIUM_BRANDING)
#define kStableBrandFileName @"Chromium Brand.plist"
#define kCanaryBrandFileName @"Chromium Canary Brand.plist"
#else
#error Unknown branding
#endif
// These directories are hardcoded in Keystone promotion preflight and the // Functions to handle the brand file.
// Keystone install script, so NSSearchPathForDirectoriesInDomains isn't used //
// since the scripts couldn't use anything like that. // Note that an external file is used so it can survive updates to Chrome.
NSString* kStableBrandUserFile = @"~/Library/Google/" kStableBrandFileName; //
NSString* kStableBrandSystemFile = @"/Library/Google/" kStableBrandFileName; // Note that these directories are hard-coded in Keystone scripts, so
NSString* kCanaryBrandUserFile = @"~/Library/Google/" kCanaryBrandFileName; // NSSearchPathForDirectoriesInDomains isn't used since the scripts couldn't use
NSString* kCanaryBrandSystemFile = @"/Library/Google/" kCanaryBrandFileName; // anything like that.
NSString* BrandFileName(version_info::Channel channel) {
NSString* fragment;
switch (channel) {
case version_info::Channel::CANARY:
fragment = @" Canary";
break;
case version_info::Channel::DEV:
fragment = @" Dev";
break;
case version_info::Channel::BETA:
fragment = @" Beta";
break;
default:
fragment = @"";
break;
}
return [NSString stringWithFormat:@"Google Chrome%@ Brand.plist", fragment];
}
NSString* UserBrandFilePath(version_info::Channel channel) { NSString* UserBrandFilePath(version_info::Channel channel) {
NSString* file = (channel == version_info::Channel::CANARY) return [[@"~/Library/Google/" stringByAppendingString:BrandFileName(channel)]
? kCanaryBrandUserFile stringByStandardizingPath];
: kStableBrandUserFile;
return [file stringByStandardizingPath];
} }
NSString* SystemBrandFilePath(version_info::Channel channel) { NSString* SystemBrandFilePath(version_info::Channel channel) {
NSString* file = (channel == version_info::Channel::CANARY) return [[@"/Library/Google/" stringByAppendingString:BrandFileName(channel)]
? kCanaryBrandSystemFile stringByStandardizingPath];
: kStableBrandSystemFile;
return [file stringByStandardizingPath];
} }
#endif
// Adaptor for scheduling an Objective-C method call in ThreadPool. // Adaptor for scheduling an Objective-C method call in ThreadPool.
class PerformBridge : public base::RefCountedThreadSafe<PerformBridge> { class PerformBridge : public base::RefCountedThreadSafe<PerformBridge> {
public: public:
...@@ -198,8 +206,10 @@ class PerformBridge : public base::RefCountedThreadSafe<PerformBridge> { ...@@ -198,8 +206,10 @@ class PerformBridge : public base::RefCountedThreadSafe<PerformBridge> {
- (void)changePermissionsForPromotionWithTool:(NSString*)toolPath; - (void)changePermissionsForPromotionWithTool:(NSString*)toolPath;
- (void)changePermissionsForPromotionComplete; - (void)changePermissionsForPromotionComplete;
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
// Returns the brand file path to use for Keystone. // Returns the brand file path to use for Keystone.
- (NSString*)brandFilePath; - (NSString*)brandFilePath;
#endif
// YES if no update installation has succeeded since a binary diff patch // YES if no update installation has succeeded since a binary diff patch
// installation failed. This signals the need to attempt a full installer // installation failed. This signals the need to attempt a full installer
...@@ -314,10 +324,8 @@ NSString* const kVersionKey = @"KSVersion"; ...@@ -314,10 +324,8 @@ NSString* const kVersionKey = @"KSVersion";
version_info::Channel channelType = chrome::GetChannelByName(channel); version_info::Channel channelType = chrome::GetChannelByName(channel);
if (channelType == version_info::Channel::STABLE) { if (channelType == version_info::Channel::STABLE) {
channel = base::SysNSStringToUTF8(ksr::KSRegistrationRemoveExistingTag); channel = base::SysNSStringToUTF8(ksr::KSRegistrationRemoveExistingTag);
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
DCHECK(chrome::GetChannelByName(channel) == version_info::Channel::STABLE) DCHECK(chrome::GetChannelByName(channel) == version_info::Channel::STABLE)
<< "-channel name modification has side effect"; << "-channel name modification has side effect";
#endif
} }
_productID.reset([productID copy]); _productID.reset([productID copy]);
...@@ -327,6 +335,8 @@ NSString* const kVersionKey = @"KSVersion"; ...@@ -327,6 +335,8 @@ NSString* const kVersionKey = @"KSVersion";
_channel = channel; _channel = channel;
} }
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
- (NSString*)brandFilePath { - (NSString*)brandFilePath {
DCHECK(_version != nil) << "-loadParameters must be called first"; DCHECK(_version != nil) << "-loadParameters must be called first";
...@@ -341,29 +351,22 @@ NSString* const kVersionKey = @"KSVersion"; ...@@ -341,29 +351,22 @@ NSString* const kVersionKey = @"KSVersion";
// Default to none. // Default to none.
_brandFile.reset(@"", base::scoped_policy::RETAIN); _brandFile.reset(@"", base::scoped_policy::RETAIN);
// Only the stable and canary channel can have independent brand codes. // Only a side-by-side capable Chromium can have an independent brand code.
if (!chrome::IsSideBySideCapable()) {
// If on the older dev or beta channels that were not side-by-side capable,
// this installation may have replaced an older system-level installation.
// Check for a user brand file and nuke it if present. Don't try to remove
// the system brand file, there wouldn't be any permission to do so.
if (channel == version_info::Channel::DEV || // Don't do this on a side-by-side capable channel. Those can run
channel == version_info::Channel::BETA) { // side-by-side with another Google Chrome installation whose brand code, if
// If on the dev or beta channel, this installation may have replaced // any, should remain intact.
// an older system-level installation. Check for a user brand file and
// nuke it if present. Don't try to remove the system brand file, there
// wouldn't be any permission to do so.
//
// Don't do this on the canary channel. The canary can run side-by-side
// with another Google Chrome installation whose brand code, if any,
// should remain intact.
if ([fm fileExistsAtPath:userBrandFile]) { if ([fm fileExistsAtPath:userBrandFile]) {
[fm removeItemAtPath:userBrandFile error:NULL]; [fm removeItemAtPath:userBrandFile error:NULL];
} }
} else {
} else if (channel == version_info::Channel::STABLE ||
channel == version_info::Channel::CANARY) {
// Stable and Canary use different app ids, so they can both have brand
// codes. Even if Canary does not actively use brand codes, we want to
// exercise the same logic, so that we can detect perf regressions early.
// If there is a system brand file, use it. // If there is a system brand file, use it.
if ([fm fileExistsAtPath:systemBrandFile]) { if ([fm fileExistsAtPath:systemBrandFile]) {
// System // System
...@@ -420,6 +423,8 @@ NSString* const kVersionKey = @"KSVersion"; ...@@ -420,6 +423,8 @@ NSString* const kVersionKey = @"KSVersion";
return _brandFile; return _brandFile;
} }
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
- (BOOL)loadKeystoneRegistration { - (BOOL)loadKeystoneRegistration {
if (!_productID || !_appPath || !_url || !_version) if (!_productID || !_appPath || !_url || !_version)
return NO; return NO;
...@@ -456,7 +461,10 @@ NSString* const kVersionKey = @"KSVersion"; ...@@ -456,7 +461,10 @@ NSString* const kVersionKey = @"KSVersion";
NSNumber* preserveTTToken = @YES; NSNumber* preserveTTToken = @YES;
NSString* appInfoPlistPath = [self appInfoPlistPath]; NSString* appInfoPlistPath = [self appInfoPlistPath];
NSString* brandKey = kBrandKey; NSString* brandKey = kBrandKey;
NSString* brandPath = [self brandFilePath]; NSString* brandPath = @"";
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
brandPath = [self brandFilePath];
#endif
if ([brandPath length] == 0) { if ([brandPath length] == 0) {
// Brand path and brand key must be cleared together or ksadmin seems // Brand path and brand key must be cleared together or ksadmin seems
...@@ -464,7 +472,7 @@ NSString* const kVersionKey = @"KSVersion"; ...@@ -464,7 +472,7 @@ NSString* const kVersionKey = @"KSVersion";
brandKey = @""; brandKey = @"";
} }
// Note that channel_ is permitted to be an empty string, but it must not be // Note that _channel is permitted to be an empty string, but it must not be
// nil. // nil.
NSString* tagSuffix = [self tagSuffix]; NSString* tagSuffix = [self tagSuffix];
NSString* tagValue = NSString* tagValue =
...@@ -924,6 +932,7 @@ NSString* const kVersionKey = @"KSVersion"; ...@@ -924,6 +932,7 @@ NSString* const kVersionKey = @"KSVersion";
[self updateStatus:kAutoupdatePromoting version:nil error:nil]; [self updateStatus:kAutoupdatePromoting version:nil error:nil];
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
// TODO(mark): Remove when able! // TODO(mark): Remove when able!
// //
// keystone_promote_preflight will copy the current brand information out to // keystone_promote_preflight will copy the current brand information out to
...@@ -1021,6 +1030,7 @@ NSString* const kVersionKey = @"KSVersion"; ...@@ -1021,6 +1030,7 @@ NSString* const kVersionKey = @"KSVersion";
if (synchronous) { if (synchronous) {
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);
} }
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
} }
- (void)promotionComplete:(NSNotification*)notification { - (void)promotionComplete:(NSNotification*)notification {
...@@ -1144,6 +1154,8 @@ NSString* const kVersionKey = @"KSVersion"; ...@@ -1144,6 +1154,8 @@ NSString* const kVersionKey = @"KSVersion";
@end // @implementation KeystoneGlue @end // @implementation KeystoneGlue
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
namespace { namespace {
std::string BrandCodeInternal() { std::string BrandCodeInternal() {
...@@ -1164,12 +1176,17 @@ std::string BrandCodeInternal() { ...@@ -1164,12 +1176,17 @@ std::string BrandCodeInternal() {
} // namespace } // namespace
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
namespace keystone_glue { namespace keystone_glue {
std::string BrandCode() { std::string BrandCode() {
// |s_brand_code| is leaked. #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
static std::string* s_brand_code = new std::string(BrandCodeInternal()); static std::string s_brand_code = std::move(BrandCodeInternal());
return *s_brand_code; return s_brand_code;
#else
return std::string();
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
} }
bool KeystoneEnabled() { bool KeystoneEnabled() {
......
...@@ -46,6 +46,12 @@ version_info::Channel GetChannel(); ...@@ -46,6 +46,12 @@ version_info::Channel GetChannel();
// Channel::STABLE, if channel is empty, else matches the name and returns // Channel::STABLE, if channel is empty, else matches the name and returns
// {STABLE,BETA,DEV,CANARY, UNKNOWN}. // {STABLE,BETA,DEV,CANARY, UNKNOWN}.
version_info::Channel GetChannelByName(const std::string& channel); version_info::Channel GetChannelByName(const std::string& channel);
// Returns whether this is a side-by-side capable copy of Chromium. For
// unbranded builds, this is always true. For branded builds, this may not be
// true for old copies of beta and dev channels that share the same user data
// dir as the stable channel.
bool IsSideBySideCapable();
#endif #endif
#if defined(OS_POSIX) #if defined(OS_POSIX)
......
...@@ -56,6 +56,33 @@ version_info::Channel GetChannelByName(const std::string& channel) { ...@@ -56,6 +56,33 @@ version_info::Channel GetChannelByName(const std::string& channel) {
return version_info::Channel::UNKNOWN; return version_info::Channel::UNKNOWN;
} }
bool IsSideBySideCapable() {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
// Use the main Chrome application bundle and not the framework bundle.
// Keystone keys don't live in the framework.
NSBundle* bundle = base::mac::OuterBundle();
if (![bundle objectForInfoDictionaryKey:@"KSProductID"]) {
// This build is not Keystone-enabled, and without a channel assume it is
// side-by-side capable.
return true;
}
if (![bundle objectForInfoDictionaryKey:@"KSChannelID"]) {
// For the stable channel, KSChannelID is not set. Stable Chromes are what
// side-by-side capable Chromes are running side-by-side *to* and by
// definition are side-by-side capable.
return true;
}
// If there is a CrProductDirName key, then the user data dir of this
// beta/dev/canary Chrome is separate, and it can run side-by-side to the
// stable Chrome.
return [bundle objectForInfoDictionaryKey:@"CrProductDirName"];
#else
return true;
#endif
}
version_info::Channel GetChannel() { version_info::Channel GetChannel() {
return GetChannelByName(GetChannelName()); return GetChannelByName(GetChannelName());
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment