blob: 012a59d6ef08d92378c65c530881ab09cc33696c [file] [log] [blame]
// Copyright 2020 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/browser/updater/browser_updater_client_mac.h"
#import <Foundation/Foundation.h>
#include <string>
#include <utility>
#include "base/callback.h"
#include "base/logging.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/updater/browser_updater_client_util.h"
#import "chrome/updater/app/server/mac/update_service_wrappers.h"
#import "chrome/updater/mac/xpc_service_names.h"
#include "chrome/updater/update_service.h"
#include "chrome/updater/updater_scope.h"
@interface CRUUpdateClientOnDemandImpl () {
base::scoped_nsobject<NSXPCConnection> _xpcConnection;
}
@end
namespace {
NSString* GetAppIdForUpdaterAsNSString() {
return base::SysUTF8ToNSString(base::mac::BaseBundleID());
}
} // namespace
@implementation CRUUpdateClientOnDemandImpl
- (instancetype)initWithScope:(updater::UpdaterScope)scope {
// If the system-level updater exists, and the browser is registered to the
// system-level updater, then connect using NSXPCConnectionPrivileged.
NSXPCConnectionOptions options = 0;
if (scope == updater::UpdaterScope::kSystem) {
options = NSXPCConnectionPrivileged;
}
return [self initWithConnectionOptions:options withScope:scope];
}
- (instancetype)initWithConnectionOptions:(NSXPCConnectionOptions)options
withScope:(updater::UpdaterScope)scope {
if (self = [super init]) {
_xpcConnection.reset([[NSXPCConnection alloc]
initWithMachServiceName:updater::GetUpdateServiceMachName(scope)
options:options]);
_xpcConnection.get().remoteObjectInterface =
updater::GetXPCUpdateServicingInterface();
_xpcConnection.get().interruptionHandler = ^{
LOG(WARNING)
<< "CRUUpdateClientOnDemandImpl: XPC connection interrupted.";
};
_xpcConnection.get().invalidationHandler = ^{
LOG(WARNING)
<< "CRUUpdateClientOnDemandImpl: XPC connection invalidated.";
};
[_xpcConnection resume];
}
return self;
}
- (void)getVersionWithReply:
(void (^_Nonnull)(NSString* _Nullable version))reply {
auto errorHandler = ^(NSError* xpcError) {
LOG(ERROR) << "XPC Connection failed: "
<< base::SysNSStringToUTF8([xpcError description]);
reply(nil);
};
[[_xpcConnection remoteObjectProxyWithErrorHandler:errorHandler]
getVersionWithReply:reply];
}
- (void)registerForUpdatesWithAppId:(NSString* _Nullable)appId
brandCode:(NSString* _Nullable)brandCode
brandPath:(NSString* _Nullable)brandPath
tag:(NSString* _Nullable)tag
version:(NSString* _Nullable)version
existenceCheckerPath:(NSString* _Nullable)existenceCheckerPath
reply:(void (^_Nonnull)(int rc))reply {
auto errorHandler = ^(NSError* xpcError) {
LOG(ERROR) << "XPC Connection failed: "
<< base::SysNSStringToUTF8([xpcError description]);
reply(
static_cast<int>(updater::UpdateService::Result::kIPCConnectionFailed));
};
[[_xpcConnection remoteObjectProxyWithErrorHandler:errorHandler]
registerForUpdatesWithAppId:appId
brandCode:brandCode
brandPath:brandPath
tag:tag
version:version
existenceCheckerPath:existenceCheckerPath
reply:reply];
}
// Checks for updates and returns the result in the reply block.
- (void)checkForUpdatesWithUpdateState:
(CRUUpdateStateObserver* _Nonnull)updateState
reply:(void (^_Nonnull)(int rc))reply {
// The method stub needs to be implemented as it is part of the
// CRUUpdateChecking protocol. However, this method does not need to be
// implemented because checkForUpdatesWithAppId:priority:updateState:reply:
// gives us all the functionality we need for on-demand updates.
NOTIMPLEMENTED();
}
// Checks for update of a given app, with specified priority. Sends repeated
// updates of progress and returns the result in the reply block.
- (void)checkForUpdateWithAppID:(NSString* _Nonnull)appID
installDataIndex:(NSString* _Nullable)installDataIndex
priority:(CRUPriorityWrapper* _Nonnull)priority
policySameVersionUpdate:
(CRUPolicySameVersionUpdateWrapper* _Nonnull)policySameVersionUpdate
updateState:(CRUUpdateStateObserver* _Nonnull)updateState
reply:(void (^_Nonnull)(int rc))reply {
auto errorHandler = ^(NSError* xpcError) {
LOG(ERROR) << "XPC Connection failed: "
<< base::SysNSStringToUTF8([xpcError description]);
reply(
static_cast<int>(updater::UpdateService::Result::kIPCConnectionFailed));
};
[[_xpcConnection remoteObjectProxyWithErrorHandler:errorHandler]
checkForUpdateWithAppID:appID
installDataIndex:installDataIndex
priority:priority
policySameVersionUpdate:policySameVersionUpdate
updateState:updateState
reply:reply];
}
// Runs periodic updater tasks like checking for uninstalls and background
// update checks.
- (void)runPeriodicTasksWithReply:(void (^_Nullable)(void))reply {
// This method does not need to be implemented in the RPC on-demand update
// call from the browser to the updater.
NOTIMPLEMENTED();
}
// Gets states of all registered apps.
- (void)getAppStatesWithReply:
(void (^_Nonnull)(CRUAppStatesWrapper* _Nullable apps))reply {
NOTIMPLEMENTED();
}
- (void)runInstallerWithAppId:(NSString* _Nonnull)appId
installerPath:(NSString* _Nonnull)installerPath
installArgs:(NSString* _Nullable)installArgs
installData:(NSString* _Nullable)installData
installSettings:(NSString* _Nullable)installSettings
updateState:(id<CRUUpdateStateObserving> _Nonnull)updateState
reply:(void (^_Nonnull)(
updater::UpdateService::Result rc))reply {
NOTIMPLEMENTED();
}
@end
BrowserUpdaterClientMac::BrowserUpdaterClientMac(updater::UpdaterScope scope)
: BrowserUpdaterClientMac(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl>(
[[CRUUpdateClientOnDemandImpl alloc] initWithScope:scope])) {}
BrowserUpdaterClientMac::BrowserUpdaterClientMac(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl> client)
: client_(client) {}
BrowserUpdaterClientMac::~BrowserUpdaterClientMac() = default;
void BrowserUpdaterClientMac::GetUpdaterVersion(
base::OnceCallback<void(const std::string&)> callback) {
__block base::OnceCallback<void(const std::string&)> block_callback =
base::BindOnce(
[](base::OnceCallback<void(const std::string&)> callback,
scoped_refptr<BrowserUpdaterClientMac> keep_alive,
const std::string& version) { std::move(callback).Run(version); },
std::move(callback), base::WrapRefCounted(this));
auto reply = ^(NSString* version) {
std::string result = base::SysNSStringToUTF8(version);
task_runner()->PostTask(FROM_HERE,
base::BindOnce(std::move(block_callback), result));
};
[client_ getVersionWithReply:reply];
}
void BrowserUpdaterClientMac::BeginRegister(
const std::string& brand_code,
const std::string& tag,
const std::string& version,
updater::UpdateService::Callback callback) {
__block updater::UpdateService::Callback block_callback = std::move(callback);
auto reply = ^(int error) {
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(std::move(block_callback),
static_cast<updater::UpdateService::Result>(error)));
};
[client_ registerForUpdatesWithAppId:GetAppIdForUpdaterAsNSString()
brandCode:base::SysUTF8ToNSString(brand_code)
brandPath:@""
tag:base::SysUTF8ToNSString(tag)
version:base::SysUTF8ToNSString(version)
existenceCheckerPath:base::mac::FilePathToNSString(
base::mac::OuterBundlePath())
reply:reply];
}
void BrowserUpdaterClientMac::BeginUpdateCheck(
updater::UpdateService::StateChangeCallback state_update,
updater::UpdateService::Callback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
__block updater::UpdateService::Callback block_callback = std::move(callback);
auto reply = ^(int error) {
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(std::move(block_callback),
static_cast<updater::UpdateService::Result>(error)));
};
base::scoped_nsobject<CRUPriorityWrapper> priority_wrapper(
[[CRUPriorityWrapper alloc]
initWithPriority:updater::UpdateService::Priority::kForeground]);
base::scoped_nsprotocol<id<CRUUpdateStateObserving>> state_observer(
[[CRUUpdateStateObserver alloc] initWithRepeatingCallback:state_update
callbackRunner:task_runner()]);
base::scoped_nsobject<CRUPolicySameVersionUpdateWrapper>
policySameVersionUpdateWrapper([[CRUPolicySameVersionUpdateWrapper alloc]
initWithPolicySameVersionUpdate:
updater::UpdateService::PolicySameVersionUpdate::kNotAllowed]);
[client_ checkForUpdateWithAppID:GetAppIdForUpdaterAsNSString()
installDataIndex:nil
priority:priority_wrapper.get()
policySameVersionUpdate:policySameVersionUpdateWrapper.get()
updateState:state_observer.get()
reply:reply];
}
// static
scoped_refptr<BrowserUpdaterClient> BrowserUpdaterClient::Create(
updater::UpdaterScope scope) {
return base::MakeRefCounted<BrowserUpdaterClientMac>(scope);
}