Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ada78eb7 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add support for specifying lens facing for test camera" into main

parents b9d94d67 773c014a
Loading
Loading
Loading
Loading
+132 −26
Original line number Diff line number Diff line
@@ -18,18 +18,25 @@
#define LOG_TAG "VirtualCameraService"
#include "VirtualCameraService.h"

#include <algorithm>
#include <cinttypes>
#include <cstdint>
#include <cstdio>
#include <iterator>
#include <memory>
#include <mutex>
#include <optional>
#include <regex>
#include <variant>

#include "VirtualCameraDevice.h"
#include "VirtualCameraProvider.h"
#include "aidl/android/companion/virtualcamera/Format.h"
#include "aidl/android/companion/virtualcamera/LensFacing.h"
#include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
#include "android/binder_auto_utils.h"
#include "android/binder_libbinder.h"
#include "android/binder_status.h"
#include "binder/Status.h"
#include "util/Permissions.h"
#include "util/Util.h"
@@ -57,9 +64,15 @@ constexpr int kVgaHeight = 480;
constexpr int kMaxFps = 60;
constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
constexpr char kHelp[] = "help";
constexpr char kShellCmdHelp[] = R"(
Usage:
   cmd virtual_camera command [--option=value]
Available commands:
 * enable_test_camera
     Options:
       --camera_id=(ID) - override numerical ID for test camera instance
       --lens_facing=(front|back|external) - specifies lens facing for test camera instance
 * disable_test_camera
)";
constexpr char kCreateVirtualDevicePermission[] =
@@ -102,6 +115,66 @@ ndk::ScopedAStatus validateConfiguration(
  return ndk::ScopedAStatus::ok();
}

enum class Command {
  ENABLE_TEST_CAMERA,
  DISABLE_TEST_CAMERA,
  HELP,
};

struct CommandWithOptions {
  Command command;
  std::map<std::string, std::string> optionToValueMap;
};

std::optional<int> parseInt(const std::string& s) {
  if (!std::all_of(s.begin(), s.end(), [](char c) { return std::isdigit(c); })) {
    return std::nullopt;
  }
  int ret = atoi(s.c_str());
  return ret > 0 ? std::optional(ret) : std::nullopt;
}

std::optional<LensFacing> parseLensFacing(const std::string& s) {
  static const std::map<std::string, LensFacing> strToLensFacing{
      {"front", LensFacing::FRONT},
      {"back", LensFacing::BACK},
      {"external", LensFacing::EXTERNAL}};
  auto it = strToLensFacing.find(s);
  return it == strToLensFacing.end() ? std::nullopt : std::optional(it->second);
}

std::variant<CommandWithOptions, std::string> parseCommand(
    const char** args, const uint32_t numArgs) {
  static const std::regex optionRegex("^--(\\w+)(?:=(.+))?$");
  static const std::map<std::string, Command> strToCommand{
      {kHelp, Command::HELP},
      {kEnableTestCameraCmd, Command::ENABLE_TEST_CAMERA},
      {kDisableTestCameraCmd, Command::DISABLE_TEST_CAMERA}};

  if (numArgs < 1) {
    return CommandWithOptions{.command = Command::HELP};
  }

  // We interpret the first argument as command;
  auto it = strToCommand.find(args[0]);
  if (it == strToCommand.end()) {
    return "Unknown command: " + std::string(args[0]);
  }

  CommandWithOptions cmd{.command = it->second};

  for (int i = 1; i < numArgs; i++) {
    std::cmatch cm;
    if (!std::regex_match(args[i], cm, optionRegex)) {
      return "Not an option: " + std::string(args[i]);
    }

    cmd.optionToValueMap[cm[1]] = cm[2];
  }

  return cmd;
};

}  // namespace

VirtualCameraService::VirtualCameraService(
@@ -232,8 +305,7 @@ std::shared_ptr<VirtualCameraDevice> VirtualCameraService::getCamera(
  return mVirtualCameraProvider->getCamera(it->second);
}

binder_status_t VirtualCameraService::handleShellCommand(int in, int out,
                                                         int err,
binder_status_t VirtualCameraService::handleShellCommand(int, int out, int err,
                                                         const char** args,
                                                         uint32_t numArgs) {
  if (numArgs <= 0) {
@@ -242,36 +314,68 @@ binder_status_t VirtualCameraService::handleShellCommand(int in, int out,
    return STATUS_OK;
  }

  if (args == nullptr || args[0] == nullptr) {
  auto isNullptr = [](const char* ptr) { return ptr == nullptr; };
  if (args == nullptr || std::any_of(args, args + numArgs, isNullptr)) {
    return STATUS_BAD_VALUE;
  }
  const char* const cmd = args[0];
  if (strcmp(kEnableTestCameraCmd, cmd) == 0) {
    int cameraId = 0;
    if (numArgs > 1 && args[1] != nullptr) {
      cameraId = atoi(args[1]);
    }
    if (cameraId == 0) {
      cameraId = sNextId++;

  std::variant<CommandWithOptions, std::string> cmdOrErrorMessage =
      parseCommand(args, numArgs);
  if (std::holds_alternative<std::string>(cmdOrErrorMessage)) {
    dprintf(err, "Error: %s\n",
            std::get<std::string>(cmdOrErrorMessage).c_str());
    return STATUS_BAD_VALUE;
  }

    enableTestCameraCmd(in, err, cameraId);
  } else if (strcmp(kDisableTestCameraCmd, cmd) == 0) {
    disableTestCameraCmd(in);
  } else {
  const CommandWithOptions& cmd =
      std::get<CommandWithOptions>(cmdOrErrorMessage);
  binder_status_t status = STATUS_OK;
  switch (cmd.command) {
    case Command::HELP:
      dprintf(out, kShellCmdHelp);
      break;
    case Command::ENABLE_TEST_CAMERA:
      status = enableTestCameraCmd(out, err, cmd.optionToValueMap);
      break;
    case Command::DISABLE_TEST_CAMERA:
      disableTestCameraCmd(out);
      break;
  }

  fsync(err);
  fsync(out);
  return STATUS_OK;
  return status;
}

void VirtualCameraService::enableTestCameraCmd(const int out, const int err,
                                               const int cameraId) {
binder_status_t VirtualCameraService::enableTestCameraCmd(
    const int out, const int err,
    const std::map<std::string, std::string>& options) {
  if (mTestCameraToken != nullptr) {
    dprintf(out, "Test camera is already enabled (%s).",
    dprintf(out, "Test camera is already enabled (%s).\n",
            getCamera(mTestCameraToken)->getCameraName().c_str());
    return;
    return STATUS_OK;
  }

  std::optional<int> cameraId;
  auto it = options.find("camera_id");
  if (it != options.end()) {
    cameraId = parseInt(it->second);
    if (!cameraId.has_value()) {
      dprintf(err, "Invalid camera_id: %s\n, must be number > 0",
              it->second.c_str());
      return STATUS_BAD_VALUE;
    }
  }

  std::optional<LensFacing> lensFacing;
  it = options.find("lens_facing");
  if (it != options.end()) {
    lensFacing = parseLensFacing(it->second);
    if (!lensFacing.has_value()) {
      dprintf(err, "Invalid lens_facing: %s\n, must be front|back|external",
              it->second.c_str());
      return STATUS_BAD_VALUE;
    }
  }

  sp<BBinder> token = sp<BBinder>::make();
@@ -283,14 +387,16 @@ void VirtualCameraService::enableTestCameraCmd(const int out, const int err,
                                                  .height = kVgaHeight,
                                                  Format::YUV_420_888,
                                                  .maxFps = kMaxFps});
  configuration.lensFacing = LensFacing::EXTERNAL;
  registerCamera(mTestCameraToken, configuration, cameraId, &ret);
  configuration.lensFacing = lensFacing.value_or(LensFacing::EXTERNAL);
  registerCamera(mTestCameraToken, configuration, cameraId.value_or(sNextId++),
                 &ret);
  if (ret) {
    dprintf(out, "Successfully registered test camera %s",
    dprintf(out, "Successfully registered test camera %s\n",
            getCamera(mTestCameraToken)->getCameraName().c_str());
  } else {
    dprintf(err, "Failed to create test camera");
    dprintf(err, "Failed to create test camera\n");
  }
  return STATUS_OK;
}

void VirtualCameraService::disableTestCameraCmd(const int out) {
+2 −1
Original line number Diff line number Diff line
@@ -71,7 +71,8 @@ class VirtualCameraService

 private:
  // Create and enable test camera instance if there's none.
  void enableTestCameraCmd(int out, int err, int cameraId);
  binder_status_t enableTestCameraCmd(
      int out, int err, const std::map<std::string, std::string>& options);
  // Disable and destroy test camera instance if there's one.
  void disableTestCameraCmd(int out);

+53 −9
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include "binder/Binder.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "util/MetadataUtil.h"
#include "util/Permissions.h"
#include "utils/Errors.h"

@@ -47,6 +48,7 @@ using ::aidl::android::companion::virtualcamera::SensorOrientation;
using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
using ::aidl::android::hardware::camera::common::CameraDeviceStatus;
using ::aidl::android::hardware::camera::common::TorchModeStatus;
using ::aidl::android::hardware::camera::device::CameraMetadata;
using ::aidl::android::hardware::camera::provider::BnCameraProviderCallback;
using ::aidl::android::hardware::graphics::common::PixelFormat;
using ::aidl::android::view::Surface;
@@ -57,6 +59,7 @@ using ::testing::Ge;
using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::Not;
using ::testing::Optional;
using ::testing::Return;
using ::testing::SizeIs;

@@ -138,7 +141,7 @@ class VirtualCameraServiceTest : public ::testing::Test {
    close(mDevNullFd);
  }

  void execute_shell_command(const std::string& cmd) {
  binder_status_t execute_shell_command(const std::string& cmd) {
    const static std::regex whitespaceRegex("\\s+");
    std::vector<std::string> tokens;
    std::copy_if(
@@ -151,10 +154,8 @@ class VirtualCameraServiceTest : public ::testing::Test {
    std::transform(tokens.begin(), tokens.end(), std::back_inserter(argv),
                   [](const std::string& str) { return str.c_str(); });

    ASSERT_THAT(
        mCameraService->handleShellCommand(mDevNullFd, mDevNullFd, mDevNullFd,
                                           argv.data(), argv.size()),
        Eq(NO_ERROR));
    return mCameraService->handleShellCommand(
        mDevNullFd, mDevNullFd, mDevNullFd, argv.data(), argv.size());
  }

  std::vector<std::string> getCameraIds() {
@@ -163,6 +164,17 @@ class VirtualCameraServiceTest : public ::testing::Test {
    return cameraIds;
  }

  std::optional<camera_metadata_enum_android_lens_facing> getCameraLensFacing(
      const std::string& id) {
    std::shared_ptr<VirtualCameraDevice> camera = mCameraProvider->getCamera(id);
    if (camera == nullptr) {
      return std::nullopt;
    }
    CameraMetadata metadata;
    camera->getCameraCharacteristics(&metadata);
    return getLensFacing(metadata);
  }

 protected:
  std::shared_ptr<VirtualCameraService> mCameraService;
  std::shared_ptr<VirtualCameraProvider> mCameraProvider;
@@ -374,29 +386,61 @@ TEST_F(VirtualCameraServiceTest, ShellCmdWithNoArgs) {
}

TEST_F(VirtualCameraServiceTest, TestCameraShellCmd) {
  execute_shell_command("enable_test_camera");
  EXPECT_THAT(execute_shell_command("enable_test_camera"), Eq(NO_ERROR));

  std::vector<std::string> cameraIdsAfterEnable = getCameraIds();
  EXPECT_THAT(cameraIdsAfterEnable, SizeIs(1));

  execute_shell_command("disable_test_camera");
  EXPECT_THAT(execute_shell_command("disable_test_camera"), Eq(NO_ERROR));

  std::vector<std::string> cameraIdsAfterDisable = getCameraIds();
  EXPECT_THAT(cameraIdsAfterDisable, IsEmpty());
}

TEST_F(VirtualCameraServiceTest, TestCameraShellCmdWithId) {
  execute_shell_command("enable_test_camera 12345");
  EXPECT_THAT(execute_shell_command("enable_test_camera --camera_id=12345"),
              Eq(NO_ERROR));

  std::vector<std::string> cameraIdsAfterEnable = getCameraIds();
  EXPECT_THAT(cameraIdsAfterEnable, ElementsAre("device@1.1/virtual/12345"));

  execute_shell_command("disable_test_camera");
  EXPECT_THAT(execute_shell_command("disable_test_camera"), Eq(NO_ERROR));

  std::vector<std::string> cameraIdsAfterDisable = getCameraIds();
  EXPECT_THAT(cameraIdsAfterDisable, IsEmpty());
}

TEST_F(VirtualCameraServiceTest, TestCameraShellCmdWithInvalidId) {
  EXPECT_THAT(
      execute_shell_command("enable_test_camera --camera_id=NotNumericalId"),
      Eq(STATUS_BAD_VALUE));
}

TEST_F(VirtualCameraServiceTest, TestCameraShellCmdWithUnknownCommand) {
  EXPECT_THAT(execute_shell_command("brew_coffee --flavor=vanilla"),
              Eq(STATUS_BAD_VALUE));
}

TEST_F(VirtualCameraServiceTest, TestCameraShellCmdWithMalformedOption) {
  EXPECT_THAT(execute_shell_command("enable_test_camera **camera_id=12345"),
              Eq(STATUS_BAD_VALUE));
}

TEST_F(VirtualCameraServiceTest, TestCameraShellCmdWithLensFacing) {
  EXPECT_THAT(execute_shell_command("enable_test_camera --lens_facing=front"),
              Eq(NO_ERROR));

  std::vector<std::string> cameraIds = getCameraIds();
  ASSERT_THAT(cameraIds, SizeIs(1));
  EXPECT_THAT(getCameraLensFacing(cameraIds[0]),
              Optional(Eq(ANDROID_LENS_FACING_FRONT)));
}

TEST_F(VirtualCameraServiceTest, TestCameraShellCmdWithInvalidLensFacing) {
  EXPECT_THAT(execute_shell_command("enable_test_camera --lens_facing=west"),
              Eq(STATUS_BAD_VALUE));
}

}  // namespace
}  // namespace virtualcamera
}  // namespace companion
+14 −0
Original line number Diff line number Diff line
@@ -903,6 +903,20 @@ std::optional<GpsCoordinates> getGpsCoordinates(
  return coordinates;
}

std::optional<camera_metadata_enum_android_lens_facing> getLensFacing(
    const aidl::android::hardware::camera::device::CameraMetadata& cameraMetadata) {
  auto metadata =
      reinterpret_cast<const camera_metadata_t*>(cameraMetadata.metadata.data());

  camera_metadata_ro_entry_t entry;
  if (find_camera_metadata_ro_entry(metadata, ANDROID_LENS_FACING, &entry) !=
      OK) {
    return std::nullopt;
  }

  return static_cast<camera_metadata_enum_android_lens_facing>(entry.data.u8[0]);
}

}  // namespace virtualcamera
}  // namespace companion
}  // namespace android
+3 −0
Original line number Diff line number Diff line
@@ -469,6 +469,9 @@ std::optional<camera_metadata_enum_android_control_capture_intent> getCaptureInt
std::optional<GpsCoordinates> getGpsCoordinates(
    const aidl::android::hardware::camera::device::CameraMetadata& metadata);

std::optional<camera_metadata_enum_android_lens_facing> getLensFacing(
    const aidl::android::hardware::camera::device::CameraMetadata& metadata);

}  // namespace virtualcamera
}  // namespace companion
}  // namespace android