// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/apps/app_shim/app_shim_listener.h"
#import <Foundation/Foundation.h>
#include <bsm/libbsm.h>
#include <unistd.h>
#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/hash/md5.h"
#include "base/path_service.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "chrome/browser/apps/app_shim/app_shim_host_bootstrap_mac.h"
#include "chrome/browser/apps/app_shim/app_shim_manager_mac.h"
#include "chrome/browser/apps/app_shim/app_shim_termination_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/mac/app_mode_common.h"
#include "components/variations/net/variations_command_line.h"
#include "content/public/browser/browser_task_traits.h"
AppShimListener::AppShimListener() = default;
void AppShimListener::Init() {
has_initialized_ = true;
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Initialize the instance of AppShimTerminationManager, to ensure that it
// registers for its notifications.
apps::AppShimTerminationManager::Get();
// If running the shim triggers Chrome startup, the user must wait for the
// socket to be set up before the shim will be usable. This also requires
// IO, so use MayBlock() with USER_VISIBLE.
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&AppShimListener::InitOnBackgroundThread, this));
}
AppShimListener::~AppShimListener() {
content::GetIOThreadTaskRunner({})->DeleteSoon(FROM_HERE,
std::move(mach_acceptor_));
// The AppShimListener is only initialized if the Chrome process
// successfully took the singleton lock. If it was not initialized, do not
// delete existing app shim socket files as they belong to another process.
if (!has_initialized_)
return;
AppShimHostBootstrap::SetClient(nullptr);
base::FilePath user_data_dir;
if (base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
base::FilePath version_path =
user_data_dir.Append(app_mode::kRunningChromeVersionSymlinkName);
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::GetDeleteFileCallback(version_path));
}
}
void AppShimListener::InitOnBackgroundThread() {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
base::FilePath user_data_dir;
if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
return;
std::string name_fragment =
std::string(app_mode::kAppShimBootstrapNameFragment) + "." +
base::MD5String(user_data_dir.value());
mach_acceptor_ =
std::make_unique<apps::MachBootstrapAcceptor>(name_fragment, this);
mach_acceptor_->Start();
// Create a symlink containing the current version string and a bit indicating
// whether or not the MojoIpcz feature is enabled. This allows the shim to
// load the same framework version as the currently running Chrome process,
// and it ensures that both processes are using the same IPC implementation.
base::FilePath version_path =
user_data_dir.Append(app_mode::kRunningChromeVersionSymlinkName);
const auto config =
app_mode::ChromeConnectionConfig::GenerateForCurrentProcess();
base::DeleteFile(version_path);
base::CreateSymbolicLink(config.EncodeAsPath(), version_path);
if (!variations::VariationsCommandLine::GetForCurrentProcess().WriteToFile(
user_data_dir.Append(app_mode::kFeatureStateFileName))) {
LOG(ERROR) << "Failed to write feature state to " << user_data_dir;
}
}
void AppShimListener::OnClientConnected(mojo::PlatformChannelEndpoint endpoint,
audit_token_t audit_token) {
// TODO(crbug.com/40674145): Remove NSLog logging, and move to an
// internal debugging URL.
NSLog(@"AppShim: Connection received from pid %d",
audit_token_to_pid(audit_token));
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&AppShimHostBootstrap::CreateForChannelAndPeerAuditToken,
std::move(endpoint), audit_token));
}
void AppShimListener::OnServerChannelCreateError() {
// TODO(crbug.com/41035623): Set a timeout and attempt to reconstruct
// the channel. Until cases where the error could occur are better known,
// just reset the acceptor to allow failure to be communicated via the test
// API.
mach_acceptor_.reset();
}