Commit 4c2d22e2 authored by Maksim Ivanov's avatar Maksim Ivanov Committed by Commit Bot

Implement sending diagnostics messages from extension

Add pre-defined native messaging destination that allows
whitelisted extensions to send messages to the
diagnostics_processor daemon (via the diagnosticsd daemon)
and receive the responses back.

The extension should use either chrome.runtime.sendNativeMessage
or chrome.runtime.connectNative with the
'com.google.wilco_dtc' application in order to use this
message pipe.

The hardcoded whitelist for accessing this message pipe is currently
empty - it will have to be updated once we figure out extension IDs
that we'll use.

BUG=chromium:907932,b:123926112
TEST=manual

Change-Id: I9083dd87346b490cb43ca8aabd2f7534245215e2
Reviewed-on: https://chromium-review.googlesource.com/c/1463442Reviewed-by: default avatarMattias Nissler <mnissler@chromium.org>
Reviewed-by: default avatarSergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarPolina Bondarenko <pbond@chromium.org>
Commit-Queue: Maksim Ivanov <emaxx@chromium.org>
Cr-Commit-Position: refs/heads/master@{#635494}
parent 722e5748
...@@ -729,6 +729,8 @@ source_set("chromeos") { ...@@ -729,6 +729,8 @@ source_set("chromeos") {
"device_sync/device_sync_client_factory.h", "device_sync/device_sync_client_factory.h",
"diagnosticsd/diagnosticsd_bridge.cc", "diagnosticsd/diagnosticsd_bridge.cc",
"diagnosticsd/diagnosticsd_bridge.h", "diagnosticsd/diagnosticsd_bridge.h",
"diagnosticsd/diagnosticsd_messaging.cc",
"diagnosticsd/diagnosticsd_messaging.h",
"diagnosticsd/diagnosticsd_web_request_service.cc", "diagnosticsd/diagnosticsd_web_request_service.cc",
"diagnosticsd/diagnosticsd_web_request_service.h", "diagnosticsd/diagnosticsd_web_request_service.h",
"diagnosticsd/mojo_utils.cc", "diagnosticsd/mojo_utils.cc",
......
...@@ -18,8 +18,10 @@ ...@@ -18,8 +18,10 @@
#include "chromeos/dbus/fake_diagnosticsd_client.h" #include "chromeos/dbus/fake_diagnosticsd_client.h"
#include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/system/handle.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h" #include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace chromeos { namespace chromeos {
...@@ -27,7 +29,12 @@ namespace chromeos { ...@@ -27,7 +29,12 @@ namespace chromeos {
namespace { namespace {
class MockMojoDiagnosticsdService class MockMojoDiagnosticsdService
: public diagnosticsd::mojom::DiagnosticsdService {}; : public diagnosticsd::mojom::DiagnosticsdService {
public:
MOCK_METHOD2(SendUiMessageToDiagnosticsProcessor,
void(mojo::ScopedHandle,
SendUiMessageToDiagnosticsProcessorCallback));
};
// Fake implementation of the DiagnosticsdServiceFactory Mojo interface that // Fake implementation of the DiagnosticsdServiceFactory Mojo interface that
// holds up method calls and allows to complete them afterwards. // holds up method calls and allows to complete them afterwards.
......
// Copyright 2019 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/chromeos/diagnosticsd/diagnosticsd_messaging.h"
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/shared_memory.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_piece.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/diagnosticsd/diagnosticsd_bridge.h"
#include "chrome/browser/chromeos/diagnosticsd/mojo_utils.h"
#include "chrome/services/diagnosticsd/public/mojom/diagnosticsd.mojom.h"
#include "extensions/browser/api/messaging/native_message_host.h"
#include "mojo/public/cpp/system/handle.h"
namespace chromeos {
// Native application name that is used for passing UI messages between the
// diagnostics_processor daemon and extensions.
const char kDiagnosticsdUiMessageHost[] = "com.google.wilco_dtc";
// Error nessages sent to the extension:
const char kDiagnosticsdUiMessageTooBigExtensionsError[] =
"Message is too big.";
const char kDiagnosticsdUiExtraMessagesExtensionsError[] =
"At most one message must be sent through the message channel.";
// Maximum allowed size of UI messages passed between the diagnostics_processor
// daemon and extensions.
const int kDiagnosticsdUiMessageMaxSize = 1000000;
namespace {
// Extensions native message host implementation that is used when an
// extension requests a message channel to the diagnostics_processor daemon.
//
// The message is transmitted via the diagnosticsd daemon. One instance of this
// class allows only one message to be sent; at most one message will be sent in
// the reverse direction: it will contain the daemon's response.
class DiagnosticsdExtensionOwnedMessageHost final
: public extensions::NativeMessageHost {
public:
DiagnosticsdExtensionOwnedMessageHost() = default;
~DiagnosticsdExtensionOwnedMessageHost() override = default;
// extensions::NativeMessageHost:
void Start(Client* client) override {
DCHECK(!client_);
client_ = client;
}
void OnMessage(const std::string& request_string) override {
DCHECK(client_);
if (is_disposed_) {
// We already called CloseChannel() before so ignore messages arriving at
// this point. This corner case can happen because CloseChannel() does its
// job asynchronously.
return;
}
if (message_from_extension_received_) {
// Our implementation doesn't allow sending multiple messages from the
// extension over the same instance.
DisposeSelf(kDiagnosticsdUiExtraMessagesExtensionsError);
return;
}
message_from_extension_received_ = true;
if (request_string.size() > kDiagnosticsdUiMessageMaxSize) {
DisposeSelf(kDiagnosticsdUiMessageTooBigExtensionsError);
return;
}
DiagnosticsdBridge* const diagnosticsd_bridge = DiagnosticsdBridge::Get();
if (!diagnosticsd_bridge) {
VLOG(0) << "Cannot send message - no bridge to the daemon";
DisposeSelf(kNotFoundError);
return;
}
diagnosticsd::mojom::DiagnosticsdServiceProxy* const
diagnosticsd_mojo_proxy =
diagnosticsd_bridge->diagnosticsd_service_mojo_proxy();
if (!diagnosticsd_mojo_proxy) {
VLOG(0) << "Cannot send message - Mojo connection to the daemon isn't "
"bootstrapped yet";
DisposeSelf(kNotFoundError);
return;
}
mojo::ScopedHandle json_message_mojo_handle =
CreateReadOnlySharedMemoryMojoHandle(request_string);
if (!json_message_mojo_handle) {
LOG(ERROR) << "Cannot create Mojo shared memory handle from string";
DisposeSelf(kHostInputOutputError);
return;
}
diagnosticsd_mojo_proxy->SendUiMessageToDiagnosticsProcessor(
std::move(json_message_mojo_handle),
base::BindOnce(&DiagnosticsdExtensionOwnedMessageHost::
OnResponseReceivedFromDaemon,
weak_ptr_factory_.GetWeakPtr()));
}
scoped_refptr<base::SingleThreadTaskRunner> task_runner() const override {
return task_runner_;
}
private:
void DisposeSelf(const std::string& error_message) {
DCHECK(!is_disposed_);
is_disposed_ = true;
client_->CloseChannel(error_message);
// Prevent the Mojo call result, if it's still in flight, from being
// forwarded to the extension.
weak_ptr_factory_.InvalidateWeakPtrs();
}
void OnResponseReceivedFromDaemon(mojo::ScopedHandle response_json_message) {
DCHECK(client_);
DCHECK(!is_disposed_);
if (!response_json_message) {
// The call to the diagnostics_processor daemon failed or the daemon
// provided no response, so just close the extension message channel as
// it's intended to be used for one-time messages only.
VLOG(1) << "Empty response, closing the extension message channel";
DisposeSelf(std::string() /* error_message */);
return;
}
std::unique_ptr<base::SharedMemory> response_json_shared_memory;
base::StringPiece response_json_string = GetStringPieceFromMojoHandle(
std::move(response_json_message), &response_json_shared_memory);
if (response_json_string.empty()) {
LOG(ERROR) << "Cannot read response from Mojo shared memory";
DisposeSelf(kHostInputOutputError);
return;
}
if (response_json_string.size() > kDiagnosticsdUiMessageMaxSize) {
LOG(ERROR) << "The message received from the daemon is too big";
DisposeSelf(kDiagnosticsdUiMessageTooBigExtensionsError);
return;
}
client_->PostMessageFromNativeHost(response_json_string.as_string());
DisposeSelf(std::string() /* error_message */);
}
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_ =
base::ThreadTaskRunnerHandle::Get();
// Unowned.
Client* client_ = nullptr;
// Whether a message has already been received from the extension.
bool message_from_extension_received_ = false;
// Whether DisposeSelf() has already been called.
bool is_disposed_ = false;
// Must be the last member.
base::WeakPtrFactory<DiagnosticsdExtensionOwnedMessageHost> weak_ptr_factory_{
this};
DISALLOW_COPY_AND_ASSIGN(DiagnosticsdExtensionOwnedMessageHost);
};
} // namespace
std::unique_ptr<extensions::NativeMessageHost>
CreateExtensionOwnedDiagnosticsdMessageHost() {
return std::make_unique<DiagnosticsdExtensionOwnedMessageHost>();
}
} // namespace chromeos
// Copyright 2019 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_BROWSER_CHROMEOS_DIAGNOSTICSD_DIAGNOSTICSD_MESSAGING_H_
#define CHROME_BROWSER_CHROMEOS_DIAGNOSTICSD_DIAGNOSTICSD_MESSAGING_H_
#include <memory>
namespace extensions {
class NativeMessageHost;
} // namespace extensions
namespace chromeos {
extern const char kDiagnosticsdUiMessageHost[];
extern const char kDiagnosticsdUiMessageTooBigExtensionsError[];
extern const char kDiagnosticsdUiExtraMessagesExtensionsError[];
extern const int kDiagnosticsdUiMessageMaxSize;
// Creates an extensions native message host that talks to the
// diagnostics_processor daemon. This should be used when the communication is
// initiated by the extension (i.e., not the daemon).
std::unique_ptr<extensions::NativeMessageHost>
CreateExtensionOwnedDiagnosticsdMessageHost();
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_DIAGNOSTICSD_DIAGNOSTICSD_MESSAGING_H_
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/arc/extensions/arc_support_message_host.h" #include "chrome/browser/chromeos/arc/extensions/arc_support_message_host.h"
#include "chrome/browser/chromeos/diagnosticsd/diagnosticsd_messaging.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
...@@ -128,6 +129,8 @@ static const BuiltInHost kBuiltInHost[] = { ...@@ -128,6 +129,8 @@ static const BuiltInHost kBuiltInHost[] = {
{arc::ArcSupportMessageHost::kHostName, {arc::ArcSupportMessageHost::kHostName,
arc::ArcSupportMessageHost::kHostOrigin, 1, arc::ArcSupportMessageHost::kHostOrigin, 1,
&arc::ArcSupportMessageHost::Create}, &arc::ArcSupportMessageHost::Create},
{chromeos::kDiagnosticsdUiMessageHost, nullptr, 0,
&chromeos::CreateExtensionOwnedDiagnosticsdMessageHost},
}; };
bool MatchesSecurityOrigin(const BuiltInHost& host, bool MatchesSecurityOrigin(const BuiltInHost& host,
......
...@@ -41,6 +41,25 @@ interface DiagnosticsdServiceFactory { ...@@ -41,6 +41,25 @@ interface DiagnosticsdServiceFactory {
// Interface exposed by the diagnosticsd daemon. // Interface exposed by the diagnosticsd daemon.
interface DiagnosticsdService { interface DiagnosticsdService {
// Sends a message, originating from the diagnostics UI extension (hosted by
// the browser), to the diagnostics_processor daemon. The message contents are
// serialized JSON. Delivery of the message is not guaranteed (for example, if
// the diagnostics_processor daemon isn't running at the moment).
//
// NOTE: the |json_message| message must not exceed 1 MB (1'000'000 bytes).
//
// The response will contain the JSON message returned by the
// diagnostics_processor daemon. The response handle will be unset if the
// request wasn't delivered to the daemon or the daemon made no reply.
//
// NOTE: Both request and response messages are opaque to Chrome and not
// interpreted by it in any way (except for the JSON validity verification
// of the response message that happens in the target extension's renderer
// process). This method simply transmits data between two endpoints that are
// implemented by third parties (the diagnostics UI extension and the
// diagnostics_processor daemon).
SendUiMessageToDiagnosticsProcessor@0(handle json_message)
=> (handle? response_json_message);
}; };
// Interface exposed by the consumer of DiagnosticsdService. In production this // Interface exposed by the consumer of DiagnosticsdService. In production this
......
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