frameworks/native: Add VR command-line tools

Bug: None
Test: `m -j32` succeeds
Change-Id: Ia83c71875eb0f207f63a168c88a138daeea42d5d
diff --git a/cmds/vr/.clang-format b/cmds/vr/.clang-format
new file mode 100644
index 0000000..04d7970
--- /dev/null
+++ b/cmds/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/cmds/vr/pose/Android.mk b/cmds/vr/pose/Android.mk
new file mode 100644
index 0000000..1657551
--- /dev/null
+++ b/cmds/vr/pose/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+  pose.cpp
+
+staticLibraries := \
+  libdvrcommon \
+  libsensor \
+  libpdx_default_transport \
+
+sharedLibraries := \
+  libcutils \
+  liblog
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := pose
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(TARGET_BUILD_VARIANT),eng)
+ALL_DEFAULT_INSTALLED_MODULES += pose
+all_modules: pose
+endif
+
diff --git a/cmds/vr/pose/pose.cpp b/cmds/vr/pose/pose.cpp
new file mode 100644
index 0000000..2288a86
--- /dev/null
+++ b/cmds/vr/pose/pose.cpp
@@ -0,0 +1,274 @@
+// pose is a utility to query and manipulate the current pose via the pose
+// service.
+
+#include <cmath>
+#include <cstdio>
+#include <iomanip>
+#include <iostream>
+#include <regex>
+#include <vector>
+
+#include <private/dvr/types.h>
+#include <dvr/pose_client.h>
+
+using android::dvr::vec3;
+using android::dvr::quat;
+
+namespace {
+
+// Prints usage information to stderr.
+void PrintUsage(const char* executable_name) {
+  std::cerr << "Usage: " << executable_name
+            << " [--identity|--set=...|--unfreeze]\n"
+            << "\n"
+            << "  no arguments: display the current pose.\n"
+            << "  --identity: freeze the pose to the identity pose.\n"
+            << "  --set=rx,ry,rz,rw[,px,py,pz]: freeze the pose to the given "
+               "state. rx,ry,rz,rw are interpreted as rotation quaternion. "
+               " px, py, pz as position (0,0,0 if omitted).\n"
+            << "  --mode=mode: sets mode to one of normal, head_turn:slow, "
+               "head_turn:fast, rotate:slow, rotate:medium, rotate:fast, "
+               "circle_strafe.\n"
+            << "  --unfreeze: sets the mode to normal.\n"
+            << "  --log_controller=[true|false]: starts and stops controller"
+               " logs\n"
+            << std::endl;
+}
+
+// If return_code is negative, print out its corresponding string description
+// and exit the program with a non-zero exit code.
+void ExitIfNegative(int return_code) {
+  if (return_code < 0) {
+    std::cerr << "Error: " << strerror(-return_code) << std::endl;
+    std::exit(1);
+  }
+}
+
+// Parses the following command line flags:
+// --identity
+// --set=rx,ry,rz,rw[,px,py,pz]
+// Returns false if parsing fails.
+bool ParseState(const std::string& arg, DvrPoseState* out_state) {
+  if (arg == "--identity") {
+    *out_state = {.head_from_start_rotation = {0.f, 0.f, 0.f, 1.f},
+                  .head_from_start_translation = {0.f, 0.f, 0.f},
+                  .timestamp_ns = 0,
+                  .sensor_from_start_rotation_velocity = {0.f, 0.f, 0.f}};
+    return true;
+  }
+
+  const std::string prefix("--set=");
+  if (arg.size() < 6 || arg.compare(0, prefix.size(), prefix) != 0) {
+    return false;
+  }
+
+  // Tokenize by ','.
+  std::regex split_by_comma("[,]+");
+  std::sregex_token_iterator token_it(arg.begin() + prefix.size(), arg.end(),
+                                      split_by_comma,
+                                      -1 /* return inbetween parts */);
+  std::sregex_token_iterator token_end;
+
+  // Convert to float and store values.
+  std::vector<float> values;
+  for (; token_it != token_end; ++token_it) {
+    std::string token = *(token_it);
+    float value = 0.f;
+    if (sscanf(token.c_str(), "%f", &value) != 1) {
+      std::cerr << "Unable to parse --set value as float: " << token
+                << std::endl;
+      return false;
+    } else {
+      values.push_back(value);
+    }
+  }
+
+  if (values.size() != 4 && values.size() != 7) {
+    std::cerr << "Unable to parse --set, expected either 4 or 7 of values."
+              << std::endl;
+    return false;
+  }
+
+  float norm2 = values[0] * values[0] + values[1] * values[1] +
+                values[2] * values[2] + values[3] * values[3];
+  if (std::abs(norm2 - 1.f) > 1e-4) {
+    if (norm2 < 1e-8) {
+      std::cerr << "--set quaternion norm close to zero." << std::endl;
+      return false;
+    }
+    float norm = std::sqrt(norm2);
+    values[0] /= norm;
+    values[1] /= norm;
+    values[2] /= norm;
+    values[3] /= norm;
+  }
+
+  out_state->head_from_start_rotation = {values[0], values[1], values[2],
+                                         values[3]};
+
+  if (values.size() == 7) {
+    out_state->head_from_start_translation = {values[4], values[5], values[6]};
+  } else {
+    out_state->head_from_start_translation = {0.f, 0.f, 0.f};
+  }
+
+  out_state->timestamp_ns = 0;
+  out_state->sensor_from_start_rotation_velocity = {0.f, 0.f, 0.f};
+
+  return true;
+}
+
+// Parses the command line flag --mode.
+// Returns false if parsing fails.
+bool ParseSetMode(const std::string& arg, DvrPoseMode* mode) {
+  const std::string prefix("--mode=");
+  if (arg.size() < prefix.size() ||
+      arg.compare(0, prefix.size(), prefix) != 0) {
+    return false;
+  }
+
+  std::string value = arg.substr(prefix.size());
+
+  if (value == "normal") {
+    *mode = DVR_POSE_MODE_6DOF;
+    return true;
+  } else if (value == "head_turn:slow") {
+    *mode = DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW;
+    return true;
+  } else if (value == "head_turn:fast") {
+    *mode = DVR_POSE_MODE_MOCK_HEAD_TURN_FAST;
+    return true;
+  } else if (value == "rotate:slow") {
+    *mode = DVR_POSE_MODE_MOCK_ROTATE_SLOW;
+    return true;
+  } else if (value == "rotate:medium") {
+    *mode = DVR_POSE_MODE_MOCK_ROTATE_MEDIUM;
+    return true;
+  } else if (value == "rotate:fast") {
+    *mode = DVR_POSE_MODE_MOCK_ROTATE_FAST;
+    return true;
+  } else if (value == "circle_strafe") {
+    *mode = DVR_POSE_MODE_MOCK_CIRCLE_STRAFE;
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// Parses the command line flag --controller_log.
+// Returns false if parsing fails.
+bool ParseLogController(const std::string& arg, bool* log_enabled) {
+  const std::string prefix("--log_controller=");
+  if (arg.size() < prefix.size() ||
+      arg.compare(0, prefix.size(), prefix) != 0) {
+    return false;
+  }
+
+  std::string value = arg.substr(prefix.size());
+
+  if (value == "false") {
+    *log_enabled = false;
+    return true;
+  } else if (value == "true") {
+    *log_enabled = true;
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// The different actions that the tool can perform.
+enum class Action {
+  Query,                 // Query the current pose.
+  Set,                   // Set the pose and freeze.
+  Unfreeze,              // Set the pose mode to normal.
+  SetMode,               // Sets the pose mode.
+  LogController,         // Start/stop controller logging in sensord.
+};
+
+// The action to perform when no arguments are passed to the tool.
+constexpr Action kDefaultAction = Action::Query;
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  Action action = kDefaultAction;
+  DvrPoseState state;
+  DvrPoseMode pose_mode = DVR_POSE_MODE_6DOF;
+  bool log_controller = false;
+
+  // Parse command-line arguments.
+  for (int i = 1; i < argc; ++i) {
+    const std::string arg = argv[i];
+    if (ParseState(arg, &state) && action == kDefaultAction) {
+      action = Action::Set;
+    } else if (arg == "--unfreeze" && action == kDefaultAction) {
+      action = Action::Unfreeze;
+    } else if (ParseSetMode(arg, &pose_mode) && action == kDefaultAction) {
+      action = Action::SetMode;
+    } else if (ParseLogController(arg, &log_controller)) {
+      action = Action::LogController;
+    } else {
+      PrintUsage(argv[0]);
+      return 1;
+    }
+  }
+
+  auto pose_client = dvrPoseCreate();
+  if (!pose_client) {
+    std::cerr << "Unable to create pose client." << std::endl;
+    return 1;
+  }
+
+  switch (action) {
+    case Action::Query: {
+      ExitIfNegative(dvrPosePoll(pose_client, &state));
+      uint64_t timestamp = state.timestamp_ns;
+      const auto& rotation = state.head_from_start_rotation;
+      const auto& translation = state.head_from_start_translation;
+      const auto& rotation_velocity = state.sensor_from_start_rotation_velocity;
+      quat q(rotation.w, rotation.x, rotation.y, rotation.z);
+      vec3 angles = q.matrix().eulerAngles(0, 1, 2);
+      angles = angles * 180.f / M_PI;
+      vec3 x = q * vec3(1.0f, 0.0f, 0.0f);
+      vec3 y = q * vec3(0.0f, 1.0f, 0.0f);
+      vec3 z = q * vec3(0.0f, 0.0f, 1.0f);
+
+      std::cout << "timestamp_ns: " << timestamp << std::endl
+                << "rotation_quaternion: " << rotation.x << ", " << rotation.y
+                << ", " << rotation.z << ", " << rotation.w << std::endl
+                << "rotation_angles: " << angles.x() << ", " << angles.y()
+                << ", " << angles.z() << std::endl
+                << "translation: " << translation.x << ", " << translation.y
+                << ", " << translation.z << std::endl
+                << "rotation_velocity: " << rotation_velocity.x << ", "
+                << rotation_velocity.y << ", " << rotation_velocity.z
+                << std::endl
+                << "axes: " << std::setprecision(3)
+                << "x(" << x.x() << ", " << x.y() << ", " << x.z() << "), "
+                << "y(" << y.x() << ", " << y.y() << ", " << y.z() << "), "
+                << "z(" << z.x() << ", " << z.y() << ", " << z.z() << "), "
+                << std::endl;
+      break;
+    }
+    case Action::Set: {
+      ExitIfNegative(dvrPoseFreeze(pose_client, &state));
+      break;
+    }
+    case Action::Unfreeze: {
+      ExitIfNegative(dvrPoseSetMode(pose_client, DVR_POSE_MODE_6DOF));
+      break;
+    }
+    case Action::SetMode: {
+      ExitIfNegative(dvrPoseSetMode(pose_client, pose_mode));
+      break;
+    }
+    case Action::LogController: {
+      ExitIfNegative(
+          dvrPoseLogController(pose_client, log_controller));
+      break;
+    }
+  }
+
+  dvrPoseDestroy(pose_client);
+}
diff --git a/cmds/vr/vrscreencap/Android.mk b/cmds/vr/vrscreencap/Android.mk
new file mode 100644
index 0000000..2fa9155
--- /dev/null
+++ b/cmds/vr/vrscreencap/Android.mk
@@ -0,0 +1,28 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	vrscreencap.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+	libdisplay \
+	libimageio \
+	libpdx_default_transport \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	liblog \
+	libpng \
+	libsync
+
+LOCAL_MODULE := vrscreencap
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(TARGET_BUILD_VARIANT),eng)
+ALL_DEFAULT_INSTALLED_MODULES += vrscreencap
+all_modules: vrscreencap
+endif
diff --git a/cmds/vr/vrscreencap/vrscreencap.cpp b/cmds/vr/vrscreencap/vrscreencap.cpp
new file mode 100644
index 0000000..3d0d112
--- /dev/null
+++ b/cmds/vr/vrscreencap/vrscreencap.cpp
@@ -0,0 +1,74 @@
+// screencap is a tool for taking screenshots using the screenshot service.
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <private/dvr/image_io.h>
+#include <private/dvr/screenshot_client.h>
+
+namespace {
+
+// Attempt to take a screenshot and save it to |filename|.
+// Returns zero on success, or a non-zero exit code otherwise.
+int TakeScreenshot(const std::string& app_name, const std::string& filename,
+                   int index) {
+  auto error_out = [app_name]() -> std::ostream& {
+    return std::cerr << app_name << ": ";
+  };
+
+  auto info_out = [app_name]() -> std::ostream& {
+    return std::cout << app_name << ": ";
+  };
+
+  auto client = android::dvr::ScreenshotClient::Create();
+
+  if (client->format() != HAL_PIXEL_FORMAT_RGB_888) {
+    error_out() << "The screenshot format for this device is not supported."
+                << std::endl;
+    return 1;
+  }
+
+  std::vector<uint8_t> image;
+  int width = 0;
+  int height = 0;
+  if (client->Take(&image, index, &width, &height) != 0) {
+    error_out() << "Failed to take screenshot." << std::endl;
+    return 1;
+  }
+
+  info_out() << "Got " << width << "x" << height << " screenshot." << std::endl;
+
+  if (!image_io_write_rgb888(filename.c_str(), width, height, image.data())) {
+    error_out() << "Failed to write image to output file " << filename
+                << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  // Parse arguments
+  if (argc != 2 && argc != 3) {
+    std::cerr
+        << "Usage: " << argv[0]
+        << " filename.[" DVR_IMAGE_IO_SUPPORTED_WRITE
+           "] [INDEX]\n"
+           "INDEX: specify 1..n to grab hw_composer layers by index.\n"
+           "       specify -n to grab pre-warp layers (-1 is base layer).\n"
+           "       the default is 1 (the base hw_composer layer).\n"
+           "       an invalid index will result in an error.\n";
+    return 1;
+  }
+  const std::string filename(argv[1]);
+  int index = 1;
+  if (argc > 2)
+    index = atoi(argv[2]);
+
+  // Perform the actual screenshot.
+  return TakeScreenshot(argv[0], filename, index);
+}