chromium/chromeos/ash/services/ime/ime_shared_library_wrapper.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/services/ime/ime_shared_library_wrapper.h"

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/system/sys_info.h"
#include "chromeos/ash/services/ime/constants.h"

namespace ash {
namespace ime {

namespace {

const char kCrosImeDecoderLib[] = "libimedecoder.so";

base::FilePath GetImeDecoderLibPath() {
#if defined(__x86_64__) || defined(__aarch64__)
  base::FilePath lib_path("/usr/lib64");
#else
  base::FilePath lib_path("/usr/lib");
#endif
  return lib_path.Append(kCrosImeDecoderLib);
}

// Simple bridge between logging in the loaded shared library and logging in
// Chrome.
// Severity comes from the LogSeverity enum in absl logging.
void ImeLoggerBridge(int severity, const char* message) {
  switch (severity) {
    case logging::LOGGING_INFO:
      // Silently ignore.
      break;
    case logging::LOGGING_WARNING:
      LOG(WARNING) << message;
      break;
    case logging::LOGGING_ERROR:
      LOG(ERROR) << message;
      break;
    case logging::LOGGING_FATAL:
      LOG(FATAL) << message;
    default:
      // There's no LOGGING_VERBOSE level in absl logging. Nothing should reach
      // here.
      NOTREACHED();
  }
}

}  // namespace

ImeSharedLibraryWrapperImpl::ImeSharedLibraryWrapperImpl() = default;

std::optional<ImeSharedLibraryWrapper::EntryPoints>
ImeSharedLibraryWrapperImpl::MaybeLoadThenReturnEntryPoints() {
  if (entry_points_) {
    return entry_points_;
  }

  base::FilePath path = GetImeDecoderLibPath();

  // Add dlopen flags (RTLD_LAZY | RTLD_NODELETE) later.
  base::ScopedNativeLibrary library = base::ScopedNativeLibrary(path);
  if (!library.is_valid()) {
    LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS())
        << "Failed to load decoder shared library from: " << path
        << ", error: " << library.GetError()->ToString();
    return std::nullopt;
  }

  EntryPoints entry_points = {
      .init_proto_mode = reinterpret_cast<InitProtoModeFn>(
          library.GetFunctionPointer(kInitProtoModeFnName)),
      .close_proto_mode = reinterpret_cast<CloseProtoModeFn>(
          library.GetFunctionPointer(kCloseProtoModeFnName)),
      .proto_mode_supports = reinterpret_cast<ImeDecoderSupportsFn>(
          library.GetFunctionPointer(kImeDecoderSupportsFnName)),
      .proto_mode_activate_ime = reinterpret_cast<ImeDecoderActivateImeFn>(
          library.GetFunctionPointer(kImeDecoderActivateImeFnName)),
      .proto_mode_process = reinterpret_cast<ImeDecoderProcessFn>(
          library.GetFunctionPointer(kImeDecoderProcessFnName)),
      .init_mojo_mode = reinterpret_cast<InitMojoModeFn>(
          library.GetFunctionPointer(kInitMojoModeFnName)),
      .close_mojo_mode = reinterpret_cast<CloseMojoModeFn>(
          library.GetFunctionPointer(kCloseMojoModeFnName)),
      .mojo_mode_initialize_connection_factory =
          reinterpret_cast<InitializeConnectionFactoryFn>(
              library.GetFunctionPointer(kInitializeConnectionFactoryFnName)),
      .mojo_mode_is_input_method_connected =
          reinterpret_cast<IsInputMethodConnectedFn>(
              library.GetFunctionPointer(kIsInputMethodConnectedFnName)),
      .init_user_data_service = reinterpret_cast<InitUserDataServiceFn>(
          library.GetFunctionPointer(kInitUserDataServiceFnName)),
      .process_user_data_request = reinterpret_cast<ProcessUserDataRequestFn>(
          library.GetFunctionPointer(kProcessUserDataRequestFnName)),
      .delete_serialized_proto = reinterpret_cast<DeleteSerializedProtoFn>(
          library.GetFunctionPointer(kDeleteSerializedProtoFnName)),
  };

  // Checking if entry_points are loaded.
  // TODO(b/328997024): Add .init_user_data_service check once implemented in
  // sharedlib.
  if (!entry_points.init_proto_mode || !entry_points.close_proto_mode ||
      !entry_points.proto_mode_supports ||
      !entry_points.proto_mode_activate_ime ||
      !entry_points.proto_mode_process || !entry_points.init_mojo_mode ||
      !entry_points.close_mojo_mode ||
      !entry_points.mojo_mode_is_input_method_connected ||
      !entry_points.mojo_mode_initialize_connection_factory) {
    return std::nullopt;
  }

  // Optional function pointer.
  SetImeEngineLoggerFn logger_setter = reinterpret_cast<SetImeEngineLoggerFn>(
      library.GetFunctionPointer(kSetImeEngineLoggerFnName));
  if (logger_setter) {
    logger_setter(ImeLoggerBridge);
  } else {
    LOG(WARNING) << "Failed to set a Chrome Logger for decoder DSO.";
  }

  library_ = std::move(library);
  entry_points_ = entry_points;
  return entry_points_;
}

ImeSharedLibraryWrapperImpl::~ImeSharedLibraryWrapperImpl() = default;

ImeSharedLibraryWrapperImpl* ImeSharedLibraryWrapperImpl::GetInstance() {
  static base::NoDestructor<ImeSharedLibraryWrapperImpl> instance;
  return instance.get();
}

}  // namespace ime
}  // namespace ash