/* * * oFono - Open Source Telephony * * Copyright (C) 2015 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" #define UPOWER_SERVICE "org.freedesktop.UPower" #define UPOWER_PATH "/org/freedesktop/UPower" #define UPOWER_INTERFACE UPOWER_SERVICE #define UPOWER_DEVICE_INTERFACE UPOWER_SERVICE ".Device" static guint modem_watch; static guint upower_daemon_watch; static DBusConnection *connection; static int last_battery_level; static char *battery_device_path; static void emulator_battery_cb(struct ofono_atom *atom, void *data) { struct ofono_emulator *em = __ofono_atom_get_data(atom); int val = GPOINTER_TO_INT(data); DBG("calling set_indicator: %d", val); ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_BATTERY, val); } static void update_modem_battery_indicator(struct ofono_modem *modem, void *data) { __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_battery_cb, data); } static void update_battery_level(double percentage_val) { int battery_level; if (percentage_val <= 1.00) { battery_level = 0; } else if (percentage_val > 1.00 && percentage_val <= 100.00) { battery_level = ((int) percentage_val - 1) / 20 + 1; } else { ofono_error("%s: Invalid value for battery level: %f", __func__, percentage_val); return; } DBG("last_battery_level: %d battery_level: %d (%f)", last_battery_level, battery_level, percentage_val); if (last_battery_level == battery_level) return; last_battery_level = battery_level; __ofono_modem_foreach(update_modem_battery_indicator, GINT_TO_POINTER(battery_level)); } static gboolean battery_props_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *iface; DBusMessageIter iter, dict; double percentage_val; gboolean percentage_found = FALSE; gboolean retval = FALSE; DBG(""); dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { ofono_error("%s: iface != TYPE_STRING!", __func__); goto done; } dbus_message_iter_get_basic(&iter, &iface); if (g_str_equal(iface, UPOWER_DEVICE_INTERFACE) != TRUE) { ofono_error("%s: wrong iface: %s!", __func__, iface); goto done; } if (!dbus_message_iter_next(&iter)) { ofono_error("%s: advance iter failed!", __func__); goto done; } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { ofono_error("%s: type != ARRAY!", __func__); goto done; } dbus_message_iter_recurse(&iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, val; const char *key; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) { ofono_error("%s: key type != STRING!", __func__); goto done; } dbus_message_iter_get_basic(&entry, &key); if (g_str_equal(key, "Percentage") != TRUE) { dbus_message_iter_next(&dict); continue; } dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) { ofono_error("%s: 'Percentage' val != VARIANT", __func__); goto done; } dbus_message_iter_recurse(&entry, &val); if (dbus_message_iter_get_arg_type(&val) != DBUS_TYPE_DOUBLE) { ofono_error("%s: 'Percentage' val != DOUBLE", __func__); goto done; } dbus_message_iter_get_basic(&val, &percentage_val); percentage_found = TRUE; break; } /* No errors found during parsing, so don't trigger cb removal */ retval = TRUE; if (percentage_found == FALSE) goto done; update_battery_level(percentage_val); done: return retval; } static void emulator_hfp_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) { struct ofono_emulator *em = __ofono_atom_get_data(atom); DBG("REGISTERED; calling set_indicator: %d", last_battery_level); ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_BATTERY, last_battery_level); return; } DBG("UNREGISTERED"); } static void modemwatch(struct ofono_modem *modem, gboolean added, void *user) { const char *path = ofono_modem_get_path(modem); DBG("modem: %s, added: %d", path, added); if (added) __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_hfp_watch, NULL, NULL); } static void call_modemwatch(struct ofono_modem *modem, void *user) { modemwatch(modem, TRUE, user); } static gboolean parse_devices_reply(DBusMessage *reply) { DBusMessageIter array, iter; const char *path; DBG(""); if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { ofono_error("%s: ERROR reply to EnumerateDevices", __func__); return FALSE; } if (dbus_message_iter_init(reply, &array) == FALSE) { ofono_error("%s: error initializing array iter", __func__); return FALSE; } if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) { ofono_error("%s: type != ARRAY!", __func__); return FALSE; } dbus_message_iter_recurse(&array, &iter); while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_OBJECT_PATH) { dbus_message_iter_get_basic(&iter, &path); if (g_strrstr(path, "/battery_")) { ofono_info("%s: found 1st battery device: %s", __func__, path); battery_device_path = g_strdup(path); break; } if (!dbus_message_iter_next(&iter)) break; } return TRUE; } static void enum_devices_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply; DBG(""); reply = dbus_pending_call_steal_reply(call); if (reply == NULL) { ofono_error("%s: dbus_message_new_method failed", __func__); goto done; } if (parse_devices_reply(reply) == FALSE) goto done; DBG("parse_devices_reply OK"); /* TODO: handle removable batteries */ if (battery_device_path == NULL) { ofono_error("%s: no battery detected", __func__); goto done; } /* Always listen to PropertiesChanged for battery */ g_dbus_add_signal_watch(connection, UPOWER_SERVICE, battery_device_path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", battery_props_changed, NULL, NULL); modem_watch = __ofono_modemwatch_add(modemwatch, NULL, NULL); __ofono_modem_foreach(call_modemwatch, NULL); done: if (reply) dbus_message_unref(reply); dbus_pending_call_unref(call); } static void upower_connect(DBusConnection *conn, void *user_data) { DBusPendingCall *call; DBusMessage *msg; DBG("upower connect"); msg = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_PATH, UPOWER_INTERFACE, "EnumerateDevices"); if (msg == NULL) { ofono_error("%s: dbus_message_new_method failed", __func__); return; } if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) { ofono_error("%s: Sending EnumerateDevices failed", __func__); goto done; } dbus_pending_call_set_notify(call, enum_devices_reply, NULL, NULL); done: dbus_message_unref(msg); } static void upower_disconnect(DBusConnection *conn, void *user_data) { DBG("upower disconnect"); if (modem_watch) { __ofono_modemwatch_remove(modem_watch); modem_watch = 0; } if (battery_device_path) { g_free(battery_device_path); battery_device_path = NULL; } } static int upower_init(void) { DBG("upower init"); connection = ofono_dbus_get_connection(); upower_daemon_watch = g_dbus_add_service_watch(connection, UPOWER_SERVICE, upower_connect, upower_disconnect, NULL, NULL); return 0; } static void upower_exit(void) { if (upower_daemon_watch) g_dbus_remove_watch(connection, upower_daemon_watch); if (modem_watch) __ofono_modemwatch_remove(modem_watch); if (battery_device_path) g_free(battery_device_path); } OFONO_PLUGIN_DEFINE(upower, "upower battery monitor", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, upower_init, upower_exit)