blob: d2e50fbdf869b7647cff46eee24e441bcdccad6e [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 "base/task/post_task.h"
#import "chrome/updater/app/server/mac/update_service_wrappers.h"
#include "chrome/updater/update_service.h"
@interface CRUUpdateClientOnDemandImpl () {
base::scoped_nsobject<NSXPCConnection> _xpcConnection;
}
@end
namespace {
NSString* const kLaunchdServiceName = @"org.chromium.ChromiumUpdater.service";
NSString* GetMachServiceName() {
return [kLaunchdServiceName
stringByAppendingFormat:@".%lu", [kLaunchdServiceName hash]];
}
NSString* GetAppIdForUpdaterAsNSString() {
return base::SysUTF8ToNSString(base::mac::BaseBundleID());
}
} // namespace
@implementation CRUUpdateClientOnDemandImpl
- (instancetype)init {
if (self = [super init]) {
_xpcConnection.reset([[NSXPCConnection alloc]
initWithMachServiceName:GetMachServiceName()
options:0]);
_xpcConnection.get().remoteObjectInterface =
updater::GetXPCUpdateCheckingInterface();
_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
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
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
priority:(CRUPriorityWrapper* _Nonnull)priority
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
priority:priority
updateState:updateState
reply:reply];
}
@end
BrowserUpdaterClientMac::BrowserUpdaterClientMac()
: BrowserUpdaterClientMac(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl>(
[[CRUUpdateClientOnDemandImpl alloc] init])) {}
BrowserUpdaterClientMac::BrowserUpdaterClientMac(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl> client)
: client_(client) {}
BrowserUpdaterClientMac::~BrowserUpdaterClientMac() = default;
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)
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()]);
[client_ checkForUpdateWithAppID:GetAppIdForUpdaterAsNSString()
priority:priority_wrapper.get()
updateState:state_observer.get()
reply:reply];
}
// static
scoped_refptr<BrowserUpdaterClient> BrowserUpdaterClient::Create() {
return base::MakeRefCounted<BrowserUpdaterClientMac>();
}