Added initial vehicle API
authorKevron Rees <kevron.m.rees@intel.com>
Wed, 16 Apr 2014 22:43:19 +0000 (15:43 -0700)
committerKevron Rees <kevron.m.rees@intel.com>
Thu, 26 Jun 2014 15:13:37 +0000 (08:13 -0700)
BUG=XWALK-865
SPEC=https://rawgit.com/w3c/automotive-bg/master/vehicle_spec.html

examples/vehicle.html [new file with mode: 0644]
packaging/tizen-extensions-crosswalk.spec
tizen-wrt.gyp
vehicle/vehicle.cc [new file with mode: 0644]
vehicle/vehicle.gyp [new file with mode: 0644]
vehicle/vehicle.h [new file with mode: 0644]
vehicle/vehicle_api.js [new file with mode: 0644]
vehicle/vehicle_extension.cc [new file with mode: 0644]
vehicle/vehicle_extension.h [new file with mode: 0644]
vehicle/vehicle_instance.cc [new file with mode: 0644]
vehicle/vehicle_instance.h [new file with mode: 0644]

diff --git a/examples/vehicle.html b/examples/vehicle.html
new file mode 100644 (file)
index 0000000..1590904
--- /dev/null
@@ -0,0 +1,22 @@
+<html>
+<h1>Hello, Tizen Vehicle API!</h1>
+
+<body>
+<pre id="console"></pre>
+<script src="js/js-test-pre.js"></script>
+<script>
+
+debug('tizen.vehicle.vehicleSpeed ' + tizen.vehicle.vehicleSpeed);
+debug('tizen.vehicle.vehicleSpeed.zones ' + tizen.vehicle.vehicleSpeed.zones);
+
+tizen.vehicle.vehicleSpeed.get().then(function(vehicleSpeed) {
+  debug("Vehicle speed: " + vehicleSpeed.speed);
+},
+function(error) {
+  debug("There was an error");
+});
+
+</script>
+<script src="js/js-test-post.js"></script>
+</body>
+</html>
index fa23592..15bab09 100644 (file)
@@ -38,6 +38,9 @@ BuildRequires: pkgconfig(capi-system-runtime-info)
 BuildRequires: pkgconfig(capi-system-sensor)
 BuildRequires: pkgconfig(capi-system-system-settings)
 BuildRequires: pkgconfig(libtzplatform-config)
+%if "%{profile}" == "ivi"
+BuildRequires: pkgconfig(automotive-message-broker)
+%endif
 # For IVI, it doesn't need sim package.
 %if "%{profile}" == "mobile"
 BuildRequires: pkgconfig(capi-telephony-sim)
index 0c339e3..0d8a703 100644 (file)
             'callhistory/callhistory.gyp:*',
           ],
         }],
+        [ 'extension_host_os == "ivi"', {
+          'dependencies': [
+            'vehicle/vehicle.gyp:*',
+          ],
+        }],
       ],
     },
     {
diff --git a/vehicle/vehicle.cc b/vehicle/vehicle.cc
new file mode 100644 (file)
index 0000000..561a5ef
--- /dev/null
@@ -0,0 +1,324 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vehicle/vehicle.h"
+
+#include <abstractpropertytype.h>
+#include <debugout.h>
+#include <gio/gio.h>
+#include <glib.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+
+#include "common/extension.h"
+#include "common/picojson.h"
+
+namespace {
+
+template<typename T> struct traits;
+
+template<>
+struct traits<GVariant> {
+  struct delete_functor {
+    void operator()(GVariant * p) const {
+      if (p != nullptr)
+        g_variant_unref(p);
+    }
+  };
+};
+
+template<>
+struct traits<GError> {
+  struct delete_functor {
+    void operator()(GError * p) const {
+      if (p != nullptr)
+        g_error_free(p);
+    }
+  };
+};
+
+template<>
+struct traits<GDBusProxy> {
+  struct delete_functor {
+    void operator()(GDBusProxy * p) const {
+      if (p != nullptr)
+        g_object_unref(p);
+    }
+  };
+};
+
+template<>
+struct traits<GVariantIter> {
+  struct delete_functor {
+    void operator()(GVariantIter * p) const {
+      if (p != nullptr)
+        g_variant_iter_free(p);
+    }
+  };
+};
+
+template<>
+struct traits<gchar> {
+  struct delete_functor {
+    void operator()(gchar * p) const {
+      if (p != nullptr)
+        g_free(p);
+    }
+  };
+};
+
+template<typename T> using super_ptr =
+    ::std::unique_ptr<T, typename traits<T>::delete_functor>;
+
+template<typename T> super_ptr<T> make_super(T* t) {
+  return super_ptr<T>(t);
+}
+
+void PostReply(Vehicle::CallbackInfo* cb_obj, picojson::object object) {
+  picojson::object msg;
+
+  msg["method"] = picojson::value(cb_obj->method);
+  msg["asyncCallId"] = picojson::value(cb_obj->callback_id);
+  msg["value"] = picojson::value(object);
+
+  std::string message = picojson::value(msg).serialize();
+
+  DebugOut() << "Reply message: " << message << endl;
+
+  cb_obj->instance->PostMessage(message.c_str());
+}
+
+void PostError(Vehicle::CallbackInfo* cb_obj, std::string error) {
+  picojson::object msg;
+  msg["method"] = picojson::value(cb_obj->method);
+  msg["error"] = picojson::value(true);
+  msg["value"] = picojson::value(error);
+  msg["asyncCallId"] =
+      picojson::value(static_cast<double>(cb_obj->callback_id));
+
+  std::string message = picojson::value(msg).serialize();
+
+  DebugOut() << "Error Reply message: " << message << endl;
+
+  cb_obj->instance->PostMessage(message.c_str());
+}
+
+picojson::value GetBasic(GVariant* value) {
+  std::string type = g_variant_get_type_string(value);
+  picojson::value v;
+
+  if (type == "i") {
+    v = picojson::value(static_cast<double>(GVS<int>::value(value)));
+  } else if (type == "d") {
+    v = picojson::value(GVS<double>::value(value));
+  } else if (type == "q") {
+    v = picojson::value(static_cast<double>(GVS<uint16_t>::value(value)));
+  } else if (type == "n") {
+    v = picojson::value(static_cast<double>(GVS<int16_t>::value(value)));
+  } else if (type == "y") {
+    v = picojson::value(static_cast<double>(GVS<char>::value(value)));
+  } else if (type == "u") {
+    v = picojson::value(static_cast<double>(GVS<uint32_t>::value(value)));
+  } else if (type == "x") {
+    v = picojson::value(static_cast<double>(GVS<int64_t>::value(value)));
+  } else if (type == "t") {
+    v = picojson::value(static_cast<double>(GVS<uint64_t>::value(value)));
+  } else if (type == "b") {
+    v = picojson::value(GVS<bool>::value(value));
+  }
+
+  return v;
+}
+
+void AsyncCallback(GObject* source, GAsyncResult* res, gpointer user_data) {
+  debugOut("GetAll() method call completed");
+
+  Vehicle::CallbackInfo *cb_obj =
+    static_cast<Vehicle::CallbackInfo*>(user_data);
+
+  if (!cb_obj) {
+    debugOut("invalid cb object");
+    return;
+  }
+
+  GError* error = nullptr;
+
+  auto property_map = make_super(
+        g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error));
+
+  auto error_ptr = make_super(error);
+
+  if (error_ptr) {
+    DebugOut() << "failed to call GetAll on interface: "
+               << error_ptr->message << endl;
+    PostError(cb_obj, "unknown");
+    delete cb_obj;
+    return;
+  }
+
+  GVariantIter* iter;
+  gchar* key;
+  GVariant* value;
+
+  g_variant_get(property_map.get(), "(a{sv})", &iter);
+
+  auto iter_ptr = make_super(iter);
+
+  std::map<std::string, GVariant*> return_value;
+
+  while (g_variant_iter_next(iter_ptr.get(), "{sv}", &key, &value)) {
+    return_value[key] = value;
+    g_free(key);
+  }
+
+  picojson::value::object object;
+
+  for (auto itr = return_value.begin(); itr != return_value.end(); itr++) {
+    std::string key = (*itr).first;
+
+    /// make key lowerCamelCase:
+    std::transform(key.begin(), key.begin() + 1, key.begin(), ::tolower);
+
+    auto variant = make_super((*itr).second);
+    picojson::value v = GetBasic(variant.get());
+    object[key] = v;
+  }
+
+  PostReply(cb_obj, object);
+  delete cb_obj;
+}
+
+}  // namespace
+
+Vehicle::Vehicle(common::Instance* instance)
+  : main_loop_(g_main_loop_new(0, FALSE)),
+    thread_(Vehicle::SetupMainloop, this),
+    instance_(instance) {
+  thread_.detach();
+}
+
+Vehicle::~Vehicle() {
+  g_main_loop_quit(main_loop_);
+  g_main_loop_unref(main_loop_);
+}
+
+void Vehicle::Get(const std::string& property, Zone::Type zone, double ret_id) {
+  CallbackInfo * data = new CallbackInfo;
+
+  data->callback_id = ret_id;
+  data->method = "get";
+  data->instance = instance_;
+
+  std::string obj_pstr = FindProperty(property, zone);
+
+  if (obj_pstr.empty()) {
+    debugOut("could not find property " + property);
+    PostError(data, "invalid_operation");
+    return;
+  }
+
+  debugOut("Getting properties interface");
+
+  GError* error = nullptr;
+
+  auto properties_proxy = make_super(
+      g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
+                                    G_DBUS_PROXY_FLAGS_NONE, NULL,
+                                    "org.automotive.message.broker",
+                                    obj_pstr.c_str(),
+                                    "org.freedesktop.DBus.Properties",
+                                    NULL,
+                                    &error));
+
+  auto error_ptr = make_super(error);
+
+  if (error_ptr) {
+    debugOut("failed to get properties proxy");
+    return;
+  }
+
+  std::string interfaceName = "org.automotive." + property;
+
+  debugOut("Calling GetAll");
+
+  g_dbus_proxy_call(properties_proxy.get(),
+                    "GetAll",
+                    g_variant_new("(s)", interfaceName.c_str()),
+                    G_DBUS_CALL_FLAGS_NONE, -1, NULL,
+                    AsyncCallback, data);
+}
+
+std::string Vehicle::FindProperty(const std::string& object_name, int zone) {
+  auto manager_proxy = make_super(GetAutomotiveManager());
+
+  if (!manager_proxy) {
+    return "";
+  }
+
+  GError* error(nullptr);
+
+  auto object_path_variant = make_super(
+      g_dbus_proxy_call_sync(manager_proxy.get(),
+                             "FindObjectForZone",
+                             g_variant_new("(si)",
+                                           object_name.c_str(),
+                                           zone),
+                             G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error));
+
+  auto error_ptr = make_super(error);
+
+  if (error_ptr) {
+    DebugOut() << "error calling FindObjectForZone: "
+               << error_ptr->message << endl;
+
+    DebugOut() << "Could not find object in zone: " << zone << endl;
+    return "";
+  }
+
+  if (!object_path_variant) {
+    DebugOut() << "Could not find object in zone: "  << zone << endl;
+    return "";
+  }
+
+  gchar* obj_path = nullptr;
+  g_variant_get(object_path_variant.get(), "(o)", &obj_path);
+
+  auto obj_path_ptr = make_super(obj_path);
+
+  DebugOut() << "FindObjectForZone() returned object path: " <<
+                obj_path_ptr.get() << endl;
+
+  return obj_path;
+}
+
+GDBusProxy* Vehicle::GetAutomotiveManager() {
+  GError* error = nullptr;
+  GDBusProxy* am =
+      g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
+                                    G_DBUS_PROXY_FLAGS_NONE, NULL,
+                                    "org.automotive.message.broker",
+                                    "/",
+                                    "org.automotive.Manager",
+                                    NULL,
+                                    &error);
+
+  auto error_ptr = make_super(error);
+
+  if (error_ptr) {
+     DebugOut() << "error calling GetAutomotiveManager: "
+                << error_ptr->message << endl;
+  }
+
+  return am;
+}
+
+void Vehicle::SetupMainloop(void *data) {
+  Vehicle* self = reinterpret_cast<Vehicle*>(data);
+  GMainContext* ctx = g_main_context_default();
+
+  g_main_context_push_thread_default(ctx);
+  g_main_loop_run(self->main_loop_);
+}
diff --git a/vehicle/vehicle.gyp b/vehicle/vehicle.gyp
new file mode 100644 (file)
index 0000000..7522bc1
--- /dev/null
@@ -0,0 +1,36 @@
+{
+  'includes':[
+    '../common/common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'tizen_vehicle',
+      'type': 'loadable_module',
+      'variables': {
+        'packages': [
+          'automotive-message-broker',
+          'icu-i18n',
+          'gio-2.0',
+          'gio-unix-2.0',
+        ],
+      },
+      'includes': [
+        '../common/pkg-config.gypi',
+      ],
+      'sources': [
+        'vehicle.cc',
+        'vehicle.h',
+        'vehicle_api.js',
+        'vehicle_extension.cc',
+        'vehicle_extension.h',
+        'vehicle_instance.cc',
+        'vehicle_instance.h',
+      ],
+      'conditions': [
+        [ 'tizen == 1', {
+            'variables': { 'packages': ['vconf'] },
+        }],
+      ],
+    },
+  ],
+}
diff --git a/vehicle/vehicle.h b/vehicle/vehicle.h
new file mode 100644 (file)
index 0000000..3535a5f
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VEHICLE_VEHICLE_H_
+#define VEHICLE_VEHICLE_H_
+
+#include <abstractpropertytype.h>
+#include <gio/gio.h>
+#include <glib.h>
+
+#include <string>
+#include <thread> // NOLINT
+
+#include "common/picojson.h"
+
+namespace common {
+
+class Instance;
+
+}  // namespace common
+
+typedef std::function<void (picojson::object)> GetReply;
+typedef std::function<void (std::string)> ErrorReply;
+
+class Vehicle {
+ public:
+  struct CallbackInfo {
+    std::string method;
+    common::Instance* instance;
+    double callback_id;
+  };
+
+  explicit Vehicle(common::Instance* i);
+  ~Vehicle();
+
+  void Get(const std::string& property, Zone::Type zone, double ret_id);
+
+ private:
+  std::string FindProperty(const std::string& object_name, int zone);
+  GDBusProxy* GetAutomotiveManager();
+
+  static void SetupMainloop(void *data);
+  GMainLoop* main_loop_;
+  std::thread thread_;
+  common::Instance* instance_;
+};
+
+#endif  // VEHICLE_VEHICLE_H_
diff --git a/vehicle/vehicle_api.js b/vehicle/vehicle_api.js
new file mode 100644 (file)
index 0000000..29b7baa
--- /dev/null
@@ -0,0 +1,93 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var next_async_call_id = 0;
+var async_calls = {};
+
+function VehicleInterface(attname) {
+  this.attributeName = attname;
+
+  var msg = {};
+  msg['method'] = 'zones';
+  msg['name'] = this.attributeName;
+
+  this._zones = [];
+
+  var call = new AsyncCall(function(data) {
+    this._zones = data;
+  });
+
+  async_calls[next_async_call_id] = call;
+  ++next_async_call_id;
+
+  extension.postMessage(JSON.stringify(msg));
+
+  Object.defineProperty(this, 'zones', { get: function() { return this._zones } });
+}
+
+VehicleInterface.prototype.get = function(zone) {
+  var msg = {};
+  msg['method'] = 'get';
+  msg['name'] = this.attributeName;
+  msg['zone'] = zone;
+
+  return createPromise(msg);
+};
+
+function AsyncCall(resolve, reject) {
+  this.resolve = resolve;
+  this.reject = reject;
+}
+
+function createPromise(msg) {
+  var promise = new Promise(function(resolve, reject) {
+    async_calls[next_async_call_id] = new AsyncCall(resolve, reject);
+  });
+
+  msg.asyncCallId = next_async_call_id;
+  extension.postMessage(JSON.stringify(msg));
+  ++next_async_call_id;
+  return promise;
+}
+
+function _defineVehicleProperty(obj, prop) {
+  Object.defineProperty(obj, prop, { enumerable: true, value: new VehicleInterface(prop) });
+}
+
+extension.setMessageListener(function(json) {
+  var msg = JSON.parse(json);
+
+  console.log('message received: ' + msg);
+
+  switch (msg.method) {
+    case 'get':
+      handleGetReply(msg);
+      break;
+    case 'zones':
+      handleZonesReply(msg);
+      break;
+  }
+});
+
+function handleGetReply(msg) {
+  console.log('handle get reply');
+
+  var cbobj = async_calls[msg.asyncCallId];
+
+  if (msg.error)
+    cbobj.reject(msg.value);
+  else
+    cbobj.resolve(msg.value);
+
+  delete async_calls[msg.asyncCallId];
+}
+
+function handleZonesReply(msg) {
+  var cbobj = async_calls[msg.asyncCallId];
+
+  if (cbobj)
+    cbobj.resolve(msg.value);
+}
+
+_defineVehicleProperty(exports, 'vehicleSpeed');
diff --git a/vehicle/vehicle_extension.cc b/vehicle/vehicle_extension.cc
new file mode 100644 (file)
index 0000000..57dee3a
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vehicle/vehicle_extension.h"
+
+#include "vehicle/vehicle_instance.h"
+
+common::Extension* CreateExtension() {
+  return new VehicleExtension();
+}
+
+extern const char kSource_vehicle_api[];
+
+VehicleExtension::VehicleExtension() {
+  SetExtensionName("tizen.vehicle");
+  SetJavaScriptAPI(kSource_vehicle_api);
+}
+
+VehicleExtension::~VehicleExtension() {
+}
+
+common::Instance* VehicleExtension::CreateInstance() {
+  return new VehicleInstance;
+}
diff --git a/vehicle/vehicle_extension.h b/vehicle/vehicle_extension.h
new file mode 100644 (file)
index 0000000..ebd5081
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VEHICLE_VEHICLE_EXTENSION_H_
+#define VEHICLE_VEHICLE_EXTENSION_H_
+
+#include "common/extension.h"
+
+class VehicleExtension : public common::Extension {
+ public:
+  VehicleExtension();
+  virtual ~VehicleExtension();
+
+ private:
+  // common::Extension implementation.
+  virtual common::Instance* CreateInstance();
+};
+
+#endif  // VEHICLE_VEHICLE_EXTENSION_H_
diff --git a/vehicle/vehicle_instance.cc b/vehicle/vehicle_instance.cc
new file mode 100644 (file)
index 0000000..6526d69
--- /dev/null
@@ -0,0 +1,73 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vehicle/vehicle_instance.h"
+
+#include <abstractpropertytype.h>
+
+#include <algorithm>
+#include <string>
+
+#include "vehicle/vehicle.h"
+
+VehicleInstance::VehicleInstance(): vehicle_(new Vehicle(this)) {
+  DebugOut::setDebugThreshhold(5);
+}
+
+void VehicleInstance::HandleMessage(const char* message) {
+  DebugOut() << "VehicleInstance message received " << message << endl;
+
+  picojson::value v;
+
+  std::string err;
+  picojson::parse(v, message, message + strlen(message), &err);
+  if (!err.empty()) {
+    return;
+  }
+
+  std::string method = v.get("method").to_str();
+
+  if (method == "get") {
+    std::string attribute = v.get("name").to_str();
+    int callback_id = v.get("asyncCallId").get<double>();
+    Zone::Type amb_zone = 0;
+    if (v.contains("zone")) {
+      picojson::value zone = v.get("zone");
+      picojson::array zones = zone.get("value").get<picojson::array>();
+      amb_zone = ZoneToAMBZone(zones);
+    }
+
+    std::transform(attribute.begin(), attribute.begin() + 1, attribute.begin(),
+                   ::toupper);
+
+    vehicle_->Get(attribute, amb_zone, callback_id);
+  }
+}
+
+void VehicleInstance::HandleSyncMessage(const char* message) {
+}
+
+int VehicleInstance::ZoneToAMBZone(picojson::array zones) {
+  Zone::Type amb_zone = 0;
+
+  for (auto zone : zones) {
+    std::string tempzone = zone.to_str();
+
+    if (tempzone == "Front") {
+      amb_zone |= Zone::Front;
+    } else if (tempzone == "Middle") {
+      amb_zone |= Zone::Middle;
+    } else if (tempzone == "Right") {
+      amb_zone |= Zone::Right;
+    } else if (tempzone == "Left") {
+      amb_zone |= Zone::Left;
+    } else if (tempzone == "Rear") {
+      amb_zone |= Zone::Rear;
+    } else if (tempzone == "Center") {
+      amb_zone |= Zone::Center;
+    }
+  }
+
+  return amb_zone;
+}
diff --git a/vehicle/vehicle_instance.h b/vehicle/vehicle_instance.h
new file mode 100644 (file)
index 0000000..58012b0
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VEHICLE_VEHICLE_INSTANCE_H_
+#define VEHICLE_VEHICLE_INSTANCE_H_
+
+#include "common/extension.h"
+#include "common/picojson.h"
+#include "vehicle/vehicle.h"
+
+class VehicleInstance : public common::Instance {
+ public:
+  VehicleInstance();
+  virtual ~VehicleInstance() {}
+
+ private:
+  // common::Instance implementation.
+  virtual void HandleMessage(const char* msg);
+  virtual void HandleSyncMessage(const char* msg);
+
+  int ZoneToAMBZone(picojson::array);
+
+  Vehicle* vehicle_;
+};
+
+#endif  // VEHICLE_VEHICLE_INSTANCE_H_