1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include <abstractpropertytype.h>
11 #include <listplusplus.h>
13 #include <superptr.hpp>
19 #include "common/extension.h"
21 common::Instance* Vehicle::CallbackInfo::instance = nullptr;
24 const char* amb_service = "org.automotive.message.broker";
25 const char* prop_iface = "org.freedesktop.DBus.Properties";
27 const char* vehicle_error_permission_denied = "permission_denied";
28 const char* vehicle_error_invalid_operation = "invalid_operation";
29 const char* vehicle_error_timeout = "timeout";
30 const char* vehicle_error_invalid_zone = "invalid_zone";
31 const char* vehicle_error_unknown = "unknown";
33 picojson::value::array AmbZoneToW3C(const std::vector<int>& amb_zones);
34 picojson::value::array AmbZoneToW3C(int amb_zone);
36 template<typename T> unique_ptr<T> make_unique(T* t) {
37 return unique_ptr<T>(t);
40 void PostReply(Vehicle::CallbackInfo* cb_obj, picojson::value object) {
41 DebugOut() << "Posting reply" << endl;
44 msg["method"] = picojson::value(cb_obj->method);
45 msg["asyncCallId"] = picojson::value(cb_obj->callback_id);
46 msg["value"] = object;
48 std::string message = picojson::value(msg).serialize();
50 DebugOut() << "Reply message: " << message << endl;
52 cb_obj->instance->PostMessage(message.c_str());
55 void PostError(Vehicle::CallbackInfo* cb_obj, const std::string& error) {
57 msg["method"] = picojson::value(cb_obj->method);
58 msg["error"] = picojson::value(true);
59 msg["value"] = picojson::value(error);
61 picojson::value(static_cast<double>(cb_obj->callback_id));
63 std::string message = picojson::value(msg).serialize();
65 DebugOut() << "Error Reply message: " << message << endl;
67 cb_obj->instance->PostMessage(message.c_str());
70 picojson::value GetBasic(GVariant* value) {
71 std::string type = g_variant_get_type_string(value);
75 int tempVal = GVS<int>::value(value);
76 v = picojson::value(static_cast<double>(tempVal));
77 } else if (type == "d") {
78 v = picojson::value(GVS<double>::value(value));
79 } else if (type == "q") {
80 v = picojson::value(static_cast<double>(GVS<uint16_t>::value(value)));
81 } else if (type == "n") {
82 v = picojson::value(static_cast<double>(GVS<int16_t>::value(value)));
83 } else if (type == "y") {
84 v = picojson::value(static_cast<double>(GVS<char>::value(value)));
85 } else if (type == "u") {
86 v = picojson::value(static_cast<double>(GVS<uint32_t>::value(value)));
87 } else if (type == "x") {
88 v = picojson::value(static_cast<double>(GVS<int64_t>::value(value)));
89 } else if (type == "t") {
90 v = picojson::value(static_cast<double>(GVS<uint64_t>::value(value)));
91 } else if (type == "b") {
92 v = picojson::value(GVS<bool>::value(value));
93 } else if (type == "s") {
94 v = picojson::value(g_variant_get_string(value, nullptr));
96 DebugOut(DebugOut::Error) << "Unsupported type: " << type << endl;
102 GVariant* GetBasic(picojson::value value, const std::string& type) {
103 GVariant* v = nullptr;
106 v = g_variant_new(type.c_str(), (int32_t)value.get<double>());
107 } else if (type == "d") {
108 v = g_variant_new(type.c_str(), value.get<double>());
109 } else if (type == "q") {
110 v = g_variant_new(type.c_str(), (uint16_t)value.get<double>());
111 } else if (type == "n") {
112 v = g_variant_new(type.c_str(), (int16_t)value.get<double>());
113 } else if (type == "u") {
114 v = g_variant_new(type.c_str(), (uint32_t)value.get<double>());
115 } else if (type == "x") {
116 v = g_variant_new(type.c_str(), (int64_t)value.get<double>());
117 } else if (type == "t") {
118 v = g_variant_new(type.c_str(), (uint64_t)value.get<double>());
119 } else if (type == "b") {
120 v = g_variant_new(type.c_str(), value.get<bool>());
121 } else if (type == "s") {
122 v = g_variant_new(type.c_str(), value.get<std::string>().c_str());
124 DebugOut(DebugOut::Error) << "Unsupported type: " << type << endl;
130 void AsyncGetCallback(GObject* source, GAsyncResult* res, gpointer user_data) {
131 debugOut("GetAll() method call completed");
133 Vehicle::CallbackInfo *cb_obj =
134 static_cast<Vehicle::CallbackInfo*>(user_data);
136 auto cb_obj_ptr = make_unique(cb_obj);
139 debugOut("invalid cb object");
143 GError* error = nullptr;
145 auto property_map = amb::make_super(
146 g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error));
148 auto error_ptr = amb::make_super(error);
151 DebugOut() << "failed to call GetAll on interface: "
152 << error_ptr->message << endl;
153 PostError(cb_obj_ptr.get(), "unknown");
161 g_variant_get(property_map.get(), "(a{sv})", &iter);
163 auto iter_ptr = amb::make_super(iter);
165 picojson::value::object object;
167 while (g_variant_iter_next(iter_ptr.get(), "{sv}", &key, &value)) {
168 auto key_ptr = amb::make_super(key);
169 auto value_ptr = amb::make_super(value);
171 std::string temp_key = key_ptr.get();
173 std::transform(temp_key.begin(), temp_key.begin() + 1,
174 temp_key.begin(), ::tolower);
176 object[temp_key] = GetBasic(value_ptr.get());
178 if (temp_key == "zone") {
180 picojson::value(AmbZoneToW3C(object[temp_key].get<double>()));
184 PostReply(cb_obj_ptr.get(), picojson::value(object));
187 picojson::value::array AmbZoneToW3C(int amb_zone) {
188 picojson::value::array z;
190 if (amb_zone & Zone::Left) {
191 z.push_back(picojson::value("left"));
193 if (amb_zone & Zone::Right) {
194 z.push_back(picojson::value("right"));
196 if (amb_zone & Zone::Front) {
197 z.push_back(picojson::value("front"));
199 if (amb_zone & Zone::Middle) {
200 z.push_back(picojson::value("middle"));
202 if (amb_zone & Zone::Center) {
203 z.push_back(picojson::value("center"));
205 if (amb_zone & Zone::Rear) {
206 z.push_back(picojson::value("rear"));
212 picojson::value::array AmbZoneToW3C(const std::vector<int>& amb_zones) {
213 picojson::value::array zones;
215 for (auto i : amb_zones) {
216 zones.push_back(picojson::value(AmbZoneToW3C(i)));
222 static void SignalCallback(GDBusConnection* connection,
224 const gchar* object_path,
227 GVariant* parameters,
228 gpointer user_data) {
229 DebugOut() << "Got signal" << endl;
230 std::vector<ObjectZone*> amb_objects_ =
231 *(static_cast<std::vector<ObjectZone*>*>(user_data));
233 GVariant* value_array;
234 GVariant* iface_name;
235 GVariant* invalidated;
237 g_variant_get(parameters,
245 g_variant_iter_init(&iter, value_array);
247 ObjectZone* object = nullptr;
249 for (auto i : amb_objects_) {
250 if (i->object_path == object_path) {
256 DebugOut(DebugOut::Error) << "received signal for which "
257 << "we have no corresponding amb object" << endl;
264 while (g_variant_iter_next(&iter, "{sv}", &key, &value)) {
265 auto key_ptr = amb::make_super(key);
266 auto value_ptr = amb::make_super(value);
268 std::string tempkey = key_ptr.get();
270 std::transform(tempkey.begin(), tempkey.begin() + 1, tempkey.begin(),
273 object->value[tempkey] = GetBasic(value_ptr.get());
275 if (tempkey == "zone") {
276 object->value[tempkey] =
277 picojson::value(AmbZoneToW3C(object->value[tempkey].get<double>()));
281 object->value["interfaceName"] = picojson::value(object->name);
283 Vehicle::CallbackInfo call;
284 call.method = "subscribe";
285 call.callback_id = -1;
287 PostReply(&call, picojson::value(object->value));
292 Vehicle::Vehicle(common::Instance* instance)
293 : main_loop_(g_main_loop_new(0, FALSE)),
294 thread_(Vehicle::SetupMainloop, this),
296 manager_proxy_(nullptr){
297 CallbackInfo::instance = instance_;
300 GError* error = nullptr;
302 dbus_connection_ = amb::make_super(g_bus_get_sync(G_BUS_TYPE_SYSTEM,
306 auto errorPtr = amb::make_super(error);
308 DebugOut(DebugOut::Error) << "getting bus: "
309 << errorPtr->message << std::endl;
312 GError* error_manager = nullptr;
313 manager_proxy_ = amb::make_super(g_dbus_proxy_new_sync(dbus_connection_.get(),
314 G_DBUS_PROXY_FLAGS_NONE, NULL,
317 "org.automotive.Manager",
321 auto error_ptr = amb::make_super(error_manager);
324 DebugOut(DebugOut::Error) << "calling GetAutomotiveManager: "
325 << error_ptr->message << endl;
329 GError* list_error = nullptr;
330 auto supported_list_variant = amb::make_super(
331 g_dbus_proxy_call_sync(manager_proxy_.get(),
334 G_DBUS_CALL_FLAGS_NONE,
335 -1, NULL, &list_error));
337 auto list_error_ptr = amb::make_super(list_error);
339 if (list_error_ptr) {
340 DebugOut(DebugOut::Error) << "error calling List: "
341 << error_ptr->message << endl;
346 g_variant_iter_init(&iter, supported_list_variant.get());
347 picojson::array list;
349 gchar* propertyName = nullptr;
351 while(g_variant_iter_next(&iter,"s", &propertyName))
353 auto propertyNamePtr = amb::make_super(propertyName);
355 std::string p = propertyNamePtr.get();
357 std::transform(p.begin(), p.begin()+1, p.begin(), ::tolower);
358 list.push_back(picojson::value(p));
361 picojson::object obj;
362 obj["method"] = picojson::value("vehicleSupportedAttributes");
363 obj["value"] = picojson::value(list);
365 instance->PostMessage(picojson::value(obj).serialize().c_str());
368 Vehicle::~Vehicle() {
369 g_main_loop_quit(main_loop_);
370 g_main_loop_unref(main_loop_);
372 for (auto i : amb_objects_) {
377 void Vehicle::Get(const std::string& property, Zone::Type zone, double ret_id) {
378 CallbackInfo* data = new CallbackInfo;
380 data->callback_id = ret_id;
381 data->method = "get";
382 data->instance = instance_;
385 std::string find_error;
386 std::string obj_pstr = FindProperty(property, zone, find_error);
388 if (obj_pstr.empty()) {
389 debugOut("could not find property " + property);
390 PostError(data, find_error);
394 GError* error = nullptr;
396 auto properties_proxy = amb::make_super(
397 g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
398 G_DBUS_PROXY_FLAGS_NONE, NULL,
405 auto error_ptr = amb::make_super(error);
408 DebugOut(DebugOut::Error) << "failed to get properties proxy: " << error->message << endl;
412 std::string interfaceName = "org.automotive." + property;
414 g_dbus_proxy_call(properties_proxy.get(),
416 g_variant_new("(s)", interfaceName.c_str()),
417 G_DBUS_CALL_FLAGS_NONE, -1, NULL,
418 AsyncGetCallback, data);
421 void Vehicle::GetZones(const std::string& object_name, double ret_id) {
422 GError* error(nullptr);
424 auto zones_variant = amb::make_super(
425 g_dbus_proxy_call_sync(manager_proxy_.get(),
426 "ZonesForObjectName",
428 object_name.c_str()),
429 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error));
431 auto error_ptr = amb::make_super(error);
434 DebugOut() << "error calling ZonesForObjectName: "
435 << error_ptr->message << endl;
439 if (!zones_variant) {
440 DebugOut() << "Invalid response from ZonesForObjectName " << endl;
444 GVariantIter* iter(nullptr);
446 g_variant_get(zones_variant.get(), "(ai)", &iter);
449 DebugOut() << "No zones for object " << object_name << endl;
453 auto iter_ptr = amb::make_super(iter);
455 std::vector<int> zones_array;
457 GVariant* value(nullptr);
459 while ((value = g_variant_iter_next_value(iter_ptr.get()))) {
460 auto value_ptr = amb::make_super(value);
463 g_variant_get(value_ptr.get(), "i", &v);
464 zones_array.push_back(v);
467 picojson::value::array w3c_zones = AmbZoneToW3C(zones_array);
469 CallbackInfo* data = new CallbackInfo;
471 data->callback_id = ret_id;
472 data->method = "zones";
473 data->instance = instance_;
475 PostReply(data, picojson::value(w3c_zones));
478 std::string Vehicle::FindProperty(const std::string& object_name, int zone, std::string& error_str) {
479 GError* error(nullptr);
481 auto object_path_variant = amb::make_super(
482 g_dbus_proxy_call_sync(manager_proxy_.get(),
484 g_variant_new("(si)",
487 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error));
489 auto error_ptr = amb::make_super(error);
492 DebugOut(DebugOut::Error) << "error calling FindObjectForZone: "
493 << error_ptr->message << endl;
495 DebugOut() << "Could not find object in zone: " << zone << endl;
496 error_str = vehicle_error_invalid_operation;
500 if (!object_path_variant) {
501 DebugOut() << "Could not find object in zone: " << zone << endl;
502 error_str = vehicle_error_invalid_operation;
506 gchar* obj_path = nullptr;
507 g_variant_get(object_path_variant.get(), "(o)", &obj_path);
509 auto obj_path_ptr = amb::make_super(obj_path);
511 DebugOut() << "FindObjectForZone() returned object path: "
512 << obj_path_ptr.get() << endl;
517 void Vehicle::SetupMainloop(void* data) {
518 Vehicle* self = reinterpret_cast<Vehicle*>(data);
519 GMainContext* ctx = g_main_context_default();
521 g_main_context_push_thread_default(ctx);
522 g_main_loop_run(self->main_loop_);
526 void Vehicle::Subscribe(const std::string& object_name, Zone::Type zone) {
527 std::string find_error;
528 std::string object_path = FindProperty(object_name, zone, find_error);
530 if (object_path.empty()) {
531 DebugOut() << "Error FindProperty failed for " << object_name;
535 bool already_subscribed = false;
537 for (auto i : amb_objects_) {
538 if (i->object_path == object_path) {
539 already_subscribed = true;
544 if (!already_subscribed) {
545 GError* proxy_error = nullptr;
547 auto properties_proxy =
548 amb::make_super(g_dbus_proxy_new_sync(dbus_connection_.get(),
549 G_DBUS_PROXY_FLAGS_NONE,
557 auto proxy_error_ptr = amb::make_super(proxy_error);
559 if (proxy_error_ptr) {
560 DebugOut() << "error creating properties proxy: "
561 << proxy_error_ptr->message << endl;
564 std::string interface_name = "org.automotive." + object_name;
566 GError* get_all_error = nullptr;
568 GVariant* property_map =
569 g_dbus_proxy_call_sync(properties_proxy.get(),
571 g_variant_new("(s)", interface_name.c_str()),
572 G_DBUS_CALL_FLAGS_NONE, -1, NULL,
575 auto get_all_error_ptr = amb::make_super(get_all_error);
577 if (get_all_error_ptr) {
578 DebugOut(DebugOut::Error) << "failed to call GetAll on interface "
579 << interface_name << " "
580 << get_all_error_ptr->message << endl;
586 g_variant_get(property_map, "(a{sv})", &iter);
588 auto iter_ptr = amb::make_super(iter);
593 ObjectZone* object = new ObjectZone(object_name, zone, object_path);
595 while (g_variant_iter_next(iter_ptr.get(), "{sv}", &key, &value)) {
596 auto key_ptr = amb::make_super(key);
597 auto value_ptr = amb::make_super(value);
599 std::string tempkey = key_ptr.get();
601 std::transform(tempkey.begin(), tempkey.begin() + 1, tempkey.begin(),
604 object->value[tempkey] = GetBasic(value_ptr.get());
608 g_dbus_connection_signal_subscribe(dbus_connection_.get(),
612 object_path.c_str(), NULL,
613 G_DBUS_SIGNAL_FLAGS_NONE,
614 SignalCallback, &amb_objects_,
617 amb_objects_.push_back(object);
619 DebugOut() << "Already subscribed to " << object_name << endl;
624 void Vehicle::Unsubscribe(const std::string& property, Zone::Type zone) {
625 std::vector<ObjectZone*> to_clean;
627 for (auto obj : amb_objects_) {
628 if (obj->name == property && obj->zone == zone) {
629 g_dbus_connection_signal_unsubscribe(dbus_connection_.get(),
631 to_clean.push_back(obj);
635 for (auto obj : to_clean) {
636 removeOne(&amb_objects_, obj);
642 void Vehicle::Set(const std::string &object_name, picojson::object value,
643 Zone::Type zone, double ret_id)
645 std::string find_error;
646 std::string object_path = FindProperty(object_name, zone, find_error);
648 Vehicle::CallbackInfo callback;
649 callback.callback_id = ret_id;
650 callback.method = "set";
651 callback.instance = instance_;
653 if (object_path.empty() || !find_error.empty()) {
654 DebugOut(DebugOut::Error) << "Object not found. Check object Name and zone."
655 << object_name << std::endl;
656 PostError(&callback, find_error);
660 GError* proxy_error = nullptr;
662 auto properties_proxy =
663 amb::make_super(g_dbus_proxy_new_sync(dbus_connection_.get(),
664 G_DBUS_PROXY_FLAGS_NONE,
672 auto proxy_error_ptr = amb::make_super(proxy_error);
674 if (proxy_error_ptr) {
675 DebugOut(DebugOut::Error) << "Error creating property proxy for " << object_path << std::endl;
676 PostError(&callback, vehicle_error_unknown);
680 std::string interface_name = "org.automotive." + object_name;
682 for (auto itr : value) {
683 GError* err = nullptr;
684 std::string attribute = itr.first;
686 std::transform(attribute.begin(), attribute.begin()+1, attribute.begin(), ::toupper);
689 amb::make_super(g_dbus_proxy_call_sync(properties_proxy.get(),
691 g_variant_new("(ss)",
692 interface_name.c_str(),
694 G_DBUS_CALL_FLAGS_NONE,
698 auto err_ptr = amb::make_super(err);
699 if (err_ptr || !var_value) {
700 DebugOut(DebugOut::Error) << "Error getting initial property signature type: " <<
701 err_ptr->message << endl;
702 PostError(&callback, vehicle_error_unknown);
706 GVariant* get_value = nullptr;
707 g_variant_get(var_value.get(), "(v)", &get_value);
709 auto get_value_ptr = amb::make_super(get_value);
711 if (!get_value_ptr) {
712 DebugOut(DebugOut::Error) << "Error getting variant value." << endl;
713 PostError(&callback, vehicle_error_unknown);
717 GVariant* v = GetBasic(itr.second, g_variant_get_type_string(get_value_ptr.get()));
720 DebugOut(DebugOut::Error) << "Error converting value to GVariant" << endl;
721 PostError(&callback, vehicle_error_unknown);
725 GError* set_error = nullptr;
727 DebugOut() << "Trying to set " << attribute << " to " << itr.second.serialize() << endl;
729 g_dbus_proxy_call_sync(properties_proxy.get(), "Set",
730 g_variant_new("(ssv)",
731 interface_name.c_str(),
734 G_DBUS_CALL_FLAGS_NONE,
735 -1, NULL, &set_error);
737 auto set_error_ptr = amb::make_super(set_error);
740 DebugOut(DebugOut::Error) << "error setting property:" << set_error_ptr->message << endl;
742 if(set_error_ptr->code == G_IO_ERROR_PERMISSION_DENIED
743 || std::string(g_dbus_error_get_remote_error(set_error_ptr.get())) == "org.freedesktop.DBus.Error.AccessDenied")
745 DebugOut(DebugOut::Error) << "permission denied" << endl;
746 PostError(&callback, vehicle_error_permission_denied);
749 PostError(&callback, vehicle_error_unknown);
754 PostReply(&callback, picojson::value());
757 void Vehicle::Supported(const string& object_name, double ret_id)
759 Vehicle::CallbackInfo callback;
760 callback.callback_id = ret_id;
761 callback.method = "supported";
762 callback.instance = instance_;
764 GError* error(nullptr);
766 auto object_path_variant = amb::make_super(
767 g_dbus_proxy_call_sync(manager_proxy_.get(),
769 g_variant_new("(s)", object_name.c_str()),
770 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error));
772 auto error_ptr = amb::make_super(error);
775 DebugOut(DebugOut::Error) << "error calling FindObjectForZone: "
776 << error_ptr->message << endl;
778 DebugOut() << "Could not find object for: " << object_name << endl;
779 PostReply(&callback, picojson::value(false));
783 if (!object_path_variant) {
784 DebugOut() << "Could not find object for: " << object_name << endl;
785 PostReply(&callback, picojson::value(false));
789 PostReply(&callback, picojson::value(true));
792 bool Vehicle::AvailableForRetrieval(const string &objectName, const string &attName)
794 GError* error = nullptr;
796 auto supportedVariant = amb::make_super(
797 g_dbus_proxy_call_sync(manager_proxy_.get(),
799 g_variant_new("(ss)", objectName.c_str(), attName.c_str()),
800 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error));
802 auto error_ptr = amb::make_super(error);
805 DebugOut(DebugOut::Error) << "error calling SupportsProperty: "
806 << error_ptr->message << endl;
808 DebugOut() << "Could not find object for: " << objectName << endl;
812 bool supported = false;
813 g_variant_get(supportedVariant.get(), "(b)", &supported);