Search
SailfishOS Open Build Service
>
Projects
>
nemo
:
devel:hw
:
pine
:
dontbeevil
>
ofono
> _service:tar_git:0007-Add-sim-info-support-and-the-sailfish-watch-infrastr.patch
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File _service:tar_git:0007-Add-sim-info-support-and-the-sailfish-watch-infrastr.patch of Package ofono
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Adam Pigg <adam@piggz.co.uk> Date: Sun, 10 Mar 2024 12:19:10 +0000 Subject: [PATCH 1/1] Add sim info support and the sailfish-watch infrastrcture --- Makefile.am | 3 + configure.ac | 6 + include/netreg.h | 21 + include/sailfish_watch.h | 110 +++++ include/sim.h | 16 + include/types.h | 19 + src/network.c | 28 ++ src/sailfish_watch.c | 895 +++++++++++++++++++++++++++++++++++++++ src/sim-info-dbus.c | 285 +++++++++++++ src/sim-info.c | 623 +++++++++++++++++++++++++++ src/sim-info.h | 72 ++++ src/sim.c | 100 +++++ 13 files changed, 2325 insertions(+) create mode 100644 include/sailfish_watch.h create mode 100644 src/sailfish_watch.c create mode 100644 src/sim-info-dbus.c create mode 100644 src/sim-info.c create mode 100644 src/sim-info.h diff --git a/Makefile.am b/Makefile.am index bad91b88516c5e4205069f3fd4ba51f000688ac9..cf13de97ea24a5dd7fda5918a8798b3ed6681ad3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -178,6 +178,9 @@ pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \ include/storage.h include/dbus-access.h \ include/dbus-clients.h include/cell-info.h +if SAILFISH_SLOT +pkginclude_HEADERS += include/sailfish_watch.h +endif nodist_pkginclude_HEADERS = include/version.h local_headers = $(foreach file,$(pkginclude_HEADERS) \ diff --git a/configure.ac b/configure.ac index b2851debf8940cfae28f7bbfd0dd483abf0ee9b8..a54928f20764af7f23fdbd8a70d415465b74b523 100644 --- a/configure.ac +++ b/configure.ac @@ -270,6 +270,12 @@ AC_ARG_ENABLE(sailfish-bt, AS_HELP_STRING([--enable-sailfish-bt], [enable_sailfish_bt=${enableval}]) AM_CONDITIONAL(SAILFISH_BT, test "${enable_sailfish_bt}" = "yes") + +AC_ARG_ENABLE(sailfish-slot, AS_HELP_STRING([--enable-sailfish-slot], + [enable Sailfish OS Slot Interface]), + [enable_sailfish_slot=${enableval}]) +AM_CONDITIONAL(SAILFISH_SLOT, test "${enable_sailfish_slot}" = "yes") + AC_ARG_ENABLE(upower, AS_HELP_STRING([--disable-upower], [disable UPower plugin]), [enable_upower=${enableval}]) diff --git a/include/netreg.h b/include/netreg.h index 5cd1045f4ea61faefdd22f7dfa76126217228108..7aa3b677095c798697688dad1c7dec27e331086d 100644 --- a/include/netreg.h +++ b/include/netreg.h @@ -30,6 +30,20 @@ extern "C" { struct ofono_netreg; +enum ofono_netreg_status { /* Since mer/1.24+git2 */ + OFONO_NETREG_STATUS_NONE = -1, + /* 27.007 Section 7.2 <stat> */ + OFONO_NETREG_STATUS_NOT_REGISTERED = 0, + OFONO_NETREG_STATUS_REGISTERED = 1, + OFONO_NETREG_STATUS_SEARCHING = 2, + OFONO_NETREG_STATUS_DENIED = 3, + OFONO_NETREG_STATUS_UNKNOWN = 4, + OFONO_NETREG_STATUS_ROAMING = 5, + /* Since mer/1.26+git1 */ + OFONO_NETREG_STATUS_REGISTERED_SMS_EUTRAN = 6, + OFONO_NETREG_STATUS_ROAMING_SMS_EUTRAN = 7 +}; + /* Theoretical limit is 16, but each GSM char can be encoded into * * 3 UTF8 characters resulting in 16*3=48 chars * */ @@ -111,6 +125,13 @@ int ofono_netreg_get_technology(struct ofono_netreg *netreg); const char *ofono_netreg_get_mcc(struct ofono_netreg *netreg); const char *ofono_netreg_get_mnc(struct ofono_netreg *netreg); +const char *ofono_netreg_get_name(struct ofono_netreg *netreg); +struct sim_spdi *ofono_netreg_get_spdi(struct ofono_netreg *netreg); + +/* Since mer/1.24+git2 */ +ofono_bool_t ofono_netreg_spdi_lookup(struct ofono_netreg *netreg, + const char *mcc, const char *mnc); + #ifdef __cplusplus } #endif diff --git a/include/sailfish_watch.h b/include/sailfish_watch.h new file mode 100644 index 0000000000000000000000000000000000000000..02e4cfd1544be26c88efd344ecc3c1182287a4d7 --- /dev/null +++ b/include/sailfish_watch.h @@ -0,0 +1,110 @@ +/* + * oFono - Open Source Telephony + * + * Copyright (C) 2017-2022 Jolla 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. + */ + +#ifndef OFONO_WATCH_H +#define OFONO_WATCH_H + +#include <ofono/gprs-context.h> +#include <ofono/netreg.h> + +struct ofono_modem; +struct ofono_sim; +struct ofono_netreg; + +/* This object watches ofono modem and various other things */ +struct ofono_watch { + const char *path; + /* Modem */ + struct ofono_modem *modem; + ofono_bool_t online; + /* OFONO_ATOM_TYPE_SIM */ + struct ofono_sim *sim; + const char *iccid; + const char *imsi; + const char *spn; + /* OFONO_ATOM_TYPE_NETREG */ + struct ofono_netreg *netreg; + /* Since 1.21+git47 */ + enum ofono_netreg_status reg_status; + const char *reg_mcc; + const char *reg_mnc; + const char *reg_name; + /* OFONO_ATOM_TYPE_GPRS */ + struct ofono_gprs *gprs; + /* Since 1.29+git3 */ + enum ofono_access_technology reg_tech; +}; + +typedef void (*ofono_watch_cb_t)(struct ofono_watch *w, void *user_data); +typedef void (*ofono_watch_gprs_settings_cb_t)(struct ofono_watch *watch, + enum ofono_gprs_context_type type, + const struct ofono_gprs_primary_context *settings, + void *user_data); + +struct ofono_watch *ofono_watch_new(const char *path); +struct ofono_watch *ofono_watch_ref(struct ofono_watch *w); +void ofono_watch_unref(struct ofono_watch *w); + +unsigned long ofono_watch_add_modem_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_online_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_sim_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_sim_state_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_iccid_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_imsi_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_spn_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_netreg_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +void ofono_watch_remove_handler(struct ofono_watch *w, unsigned long id); +void ofono_watch_remove_handlers(struct ofono_watch *w, unsigned long *ids, + unsigned int count); + +#define ofono_watch_remove_all_handlers(w,ids) \ + ofono_watch_remove_handlers(w, ids, sizeof(ids)/sizeof((ids)[0])) + +/* Since 1.21+git47 */ +unsigned long ofono_watch_add_reg_status_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_reg_mcc_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_reg_mnc_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_reg_name_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_gprs_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); +unsigned long ofono_watch_add_gprs_settings_changed_handler + (struct ofono_watch *watch, ofono_watch_gprs_settings_cb_t cb, + void *user_data); + +/* Since 1.29+git3 */ +unsigned long ofono_watch_add_reg_tech_changed_handler(struct ofono_watch *w, + ofono_watch_cb_t cb, void *user_data); + +#endif /* OFONO_WATCH_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/include/sim.h b/include/sim.h index 1403982206fc1dde8b14df45b4fb0c1085859fba..89e7655b7ee70ffc3d61ac8086ae71b5f4fcb680 100644 --- a/include/sim.h +++ b/include/sim.h @@ -252,6 +252,22 @@ ofono_bool_t ofono_sim_add_spn_watch(struct ofono_sim *sim, unsigned int *id, ofono_bool_t ofono_sim_remove_spn_watch(struct ofono_sim *sim, unsigned int *id); +typedef void (*ofono_sim_iccid_event_cb_t)(const char *iccid, void *data); + +unsigned int ofono_sim_add_iccid_watch(struct ofono_sim *sim, + ofono_sim_iccid_event_cb_t cb, void *data, + ofono_destroy_func destroy); + +void ofono_sim_remove_iccid_watch(struct ofono_sim *sim, unsigned int id); + +typedef void (*ofono_sim_imsi_event_cb_t)(const char *imsi, void *data); + +unsigned int ofono_sim_add_imsi_watch(struct ofono_sim *sim, + ofono_sim_imsi_event_cb_t cb, void *data, + ofono_destroy_func destroy); + +void ofono_sim_remove_imsi_watch(struct ofono_sim *sim, unsigned int id); + /* * It is assumed that when ofono_sim_inserted_notify is called, the SIM is * ready to be queried for files that are always available even if SIM diff --git a/include/types.h b/include/types.h index 11950a91e99e1ff014785aad247bd4086096e43c..68a525460661129d6085738771b78c898b1a1fb7 100644 --- a/include/types.h +++ b/include/types.h @@ -44,6 +44,25 @@ struct ofono_modem; typedef void (*ofono_destroy_func)(void *data); +enum ofono_access_technology { + OFONO_ACCESS_TECHNOLOGY_NONE = -1, + /* 27.007 Section 7.3 <AcT> */ + OFONO_ACCESS_TECHNOLOGY_GSM = 0, + OFONO_ACCESS_TECHNOLOGY_GSM_COMPACT = 1, + OFONO_ACCESS_TECHNOLOGY_UTRAN = 2, + OFONO_ACCESS_TECHNOLOGY_GSM_EGPRS = 3, + OFONO_ACCESS_TECHNOLOGY_UTRAN_HSDPA = 4, + OFONO_ACCESS_TECHNOLOGY_UTRAN_HSUPA = 5, + OFONO_ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA = 6, + OFONO_ACCESS_TECHNOLOGY_EUTRAN = 7, + OFONO_ACCESS_TECHNOLOGY_NB_IOT_M1 = 8, + OFONO_ACCESS_TECHNOLOGY_NB_IOT_NB1 = 9, + OFONO_ACCESS_TECHNOLOGY_EUTRA_5GCN = 10, /* Since 1.29+git8 */ + OFONO_ACCESS_TECHNOLOGY_NR_5GCN = 11, /* Since 1.29+git8 */ + OFONO_ACCESS_TECHNOLOGY_NG_RAN = 12, /* Since 1.29+git8 */ + OFONO_ACCESS_TECHNOLOGY_EUTRA_NR = 13, /* Since 1.29+git8 */ +}; + /* 27.007 Section 6.2 */ enum ofono_clir_option { OFONO_CLIR_OPTION_DEFAULT = 0, diff --git a/src/network.c b/src/network.c index 886e5bf0f9a3ddfdf8f795b231887b1706215b26..8f71dacff9a34ef93245b538db40a2500ddcc4a2 100644 --- a/src/network.c +++ b/src/network.c @@ -2077,3 +2077,31 @@ void *ofono_netreg_get_data(struct ofono_netreg *netreg) { return netreg->driver_data; } + +const char *ofono_netreg_get_name(struct ofono_netreg *netreg) +{ + if (netreg == NULL) + return NULL; + + if (netreg->current_operator == NULL) + return NULL; + + return netreg->current_operator->name; +} + +struct sim_spdi *ofono_netreg_get_spdi(struct ofono_netreg *netreg) +{ + if (netreg == NULL) + return NULL; + + if (netreg->spdi == NULL) + return NULL; + + return netreg->spdi; +} + +ofono_bool_t ofono_netreg_spdi_lookup(struct ofono_netreg *netreg, + const char *mcc, const char *mnc) +{ + return mcc && mnc && netreg && sim_spdi_lookup(netreg->spdi, mcc, mnc); +} diff --git a/src/sailfish_watch.c b/src/sailfish_watch.c new file mode 100644 index 0000000000000000000000000000000000000000..ccdd21d21ed29cbb1846df78b986154e43295a8e --- /dev/null +++ b/src/sailfish_watch.c @@ -0,0 +1,895 @@ +/* + * oFono - Open Source Telephony + * + * Copyright (C) 2017-2022 Jolla 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. + */ + +#include "sailfish_watch.h" + +#include "ofono.h" + +#include <glib-object.h> + +typedef GObjectClass OfonoWatchObjectClass; +typedef struct ofono_watch_object OfonoWatchObject; + +struct ofono_watch_object { + GObject object; + struct ofono_watch pub; + char *path; + char *iccid; + char *imsi; + char *spn; + char *reg_mcc; + char *reg_mnc; + char *reg_name; + int queued_signals; + guint modem_watch_id; + guint online_watch_id; + guint sim_watch_id; + guint sim_state_watch_id; + guint iccid_watch_id; + guint imsi_watch_id; + guint spn_watch_id; + guint netreg_watch_id; + guint netreg_status_watch_id; + guint gprs_watch_id; +}; + +struct ofono_watch_closure { + GCClosure cclosure; + union ofono_watch_closure_cb { + GCallback ptr; + ofono_watch_cb_t generic; + ofono_watch_gprs_settings_cb_t gprs_settings; + } cb; + void *user_data; +}; + +enum ofono_watch_signal { + SIGNAL_MODEM_CHANGED, + SIGNAL_ONLINE_CHANGED, + SIGNAL_SIM_CHANGED, + SIGNAL_SIM_STATE_CHANGED, + SIGNAL_ICCID_CHANGED, + SIGNAL_IMSI_CHANGED, + SIGNAL_SPN_CHANGED, + SIGNAL_NETREG_CHANGED, + SIGNAL_REG_STATUS_CHANGED, + SIGNAL_REG_MCC_CHANGED, + SIGNAL_REG_MNC_CHANGED, + SIGNAL_REG_NAME_CHANGED, + SIGNAL_REG_TECH_CHANGED, + SIGNAL_GPRS_CHANGED, + SIGNAL_GPRS_SETTINGS_CHANGED, + SIGNAL_COUNT +}; + +#define SIGNAL_MODEM_CHANGED_NAME "ofono-watch-modem-changed" +#define SIGNAL_ONLINE_CHANGED_NAME "ofono-watch-online-changed" +#define SIGNAL_SIM_CHANGED_NAME "ofono-watch-sim-changed" +#define SIGNAL_SIM_STATE_CHANGED_NAME "ofono-watch-sim-state-changed" +#define SIGNAL_ICCID_CHANGED_NAME "ofono-watch-iccid-changed" +#define SIGNAL_IMSI_CHANGED_NAME "ofono-watch-imsi-changed" +#define SIGNAL_SPN_CHANGED_NAME "ofono-watch-spn-changed" +#define SIGNAL_NETREG_CHANGED_NAME "ofono-watch-netreg-changed" +#define SIGNAL_REG_STATUS_CHANGED_NAME "ofono-watch-reg-status-changed" +#define SIGNAL_REG_MCC_CHANGED_NAME "ofono-watch-reg-mcc-changed" +#define SIGNAL_REG_MNC_CHANGED_NAME "ofono-watch-reg-mnc-changed" +#define SIGNAL_REG_NAME_CHANGED_NAME "ofono-watch-reg-name-changed" +#define SIGNAL_REG_TECH_CHANGED_NAME "ofono-watch-reg-tech-changed" +#define SIGNAL_GPRS_CHANGED_NAME "ofono-watch-gprs-changed" +#define SIGNAL_GPRS_SETTINGS_CHANGED_NAME "ofono-watch-gprs-settings-changed" + +static guint ofono_watch_signals[SIGNAL_COUNT] = { 0 }; +static GHashTable *ofono_watch_table = NULL; + +G_DEFINE_TYPE(OfonoWatchObject, ofono_watch_object, G_TYPE_OBJECT) +#define OFONO_WATCH_OBJECT_TYPE (ofono_watch_object_get_type()) +#define OFONO_WATCH_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ + OFONO_WATCH_OBJECT_TYPE, OfonoWatchObject)) + +#define NEW_SIGNAL(klass,name) \ + ofono_watch_signals[SIGNAL_##name##_CHANGED] = \ + g_signal_new(SIGNAL_##name##_CHANGED_NAME, \ + G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \ + 0, NULL, NULL, NULL, G_TYPE_NONE, 0) + +/* Skip the leading slash from the modem path: */ +#define DBG_(obj,fmt,args...) DBG("%s " fmt, (obj)->path+1, ##args) +#define ASSERT(expr) ((void)0) + +static inline struct ofono_watch_object *ofono_watch_object_cast + (struct ofono_watch *watch) +{ + return watch ? OFONO_WATCH_OBJECT(G_STRUCT_MEMBER_P(watch, + - G_STRUCT_OFFSET(struct ofono_watch_object, pub))) : NULL; +} + +static inline int ofono_watch_signal_bit(enum ofono_watch_signal id) +{ + return (1 << id); +} + +static inline void ofono_watch_signal_emit(struct ofono_watch_object *self, + enum ofono_watch_signal id) +{ + self->queued_signals &= ~ofono_watch_signal_bit(id); + g_signal_emit(self, ofono_watch_signals[id], 0); +} + +static inline void ofono_watch_signal_queue(struct ofono_watch_object *self, + enum ofono_watch_signal id) +{ + self->queued_signals |= ofono_watch_signal_bit(id); +} + +static void ofono_watch_emit_queued_signals(struct ofono_watch_object *self) +{ + int i; + + g_object_ref(self); + for (i = 0; self->queued_signals && i < SIGNAL_COUNT; i++) { + if (self->queued_signals & ofono_watch_signal_bit(i)) { + ofono_watch_signal_emit(self, i); + } + } + g_object_unref(self); +} + +static void ofono_watch_iccid_update(struct ofono_watch_object *self, + const char *iccid) +{ + if (g_strcmp0(self->iccid, iccid)) { + g_free(self->iccid); + self->pub.iccid = self->iccid = g_strdup(iccid); + ofono_watch_signal_queue(self, SIGNAL_ICCID_CHANGED); + } +} + +static void ofono_watch_iccid_notify(const char *iccid, void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ofono_watch_iccid_update(self, iccid); + ofono_watch_emit_queued_signals(self); +} + +static void ofono_watch_iccid_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ASSERT(self->iccid_watch_id); + self->iccid_watch_id = 0; +} + +static void ofono_watch_spn_update(struct ofono_watch_object *self, + const char *spn) +{ + if (g_strcmp0(self->spn, spn)) { + g_free(self->spn); + self->pub.spn = self->spn = g_strdup(spn); + ofono_watch_signal_queue(self, SIGNAL_SPN_CHANGED); + } +} + +static void ofono_watch_spn_notify(const char *spn, const char *dc, + void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ofono_watch_spn_update(self, spn); + ofono_watch_emit_queued_signals(self); +} + +static void ofono_watch_spn_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ASSERT(self->spn_watch_id); + self->spn_watch_id = 0; +} + +static void ofono_watch_imsi_update(struct ofono_watch_object *self, + const char *imsi) +{ + if (g_strcmp0(self->imsi, imsi)) { + struct ofono_watch *watch = &self->pub; + + g_free(self->imsi); + watch->imsi = self->imsi = g_strdup(imsi); + ofono_watch_signal_queue(self, SIGNAL_IMSI_CHANGED); + /* ofono core crashes if we add spn watch too early */ + if (imsi) { + ofono_sim_add_spn_watch(watch->sim, + &self->spn_watch_id, + ofono_watch_spn_notify, self, + ofono_watch_spn_destroy); + } + } +} + +static void ofono_watch_imsi_notify(const char *imsi, void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ofono_watch_imsi_update(self, imsi); + ofono_watch_emit_queued_signals(self); +} + +static void ofono_watch_imsi_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ASSERT(self->imsi_watch_id); + self->imsi_watch_id = 0; +} + +static void ofono_watch_sim_state_notify(enum ofono_sim_state new_state, + void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + /* + * ofono core doesn't notify SIM watches when SIM card gets removed. + * So we have to reset things here based on the SIM state. + */ + if (new_state == OFONO_SIM_STATE_NOT_PRESENT) { + ofono_watch_iccid_update(self, NULL); + } + if (new_state != OFONO_SIM_STATE_READY) { + ofono_watch_imsi_update(self, NULL); + ofono_watch_spn_update(self, NULL); + } + ofono_watch_signal_queue(self, SIGNAL_SIM_STATE_CHANGED); + ofono_watch_emit_queued_signals(self); +} + +static void ofono_watch_sim_state_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ASSERT(self->sim_state_watch_id); + self->sim_state_watch_id = 0; +} + +static void ofono_watch_set_sim(struct ofono_watch_object *self, + struct ofono_sim *sim) +{ + struct ofono_watch *watch = &self->pub; + + if (watch->sim != sim) { + if (self->sim_state_watch_id) { + ofono_sim_remove_state_watch(watch->sim, + self->sim_state_watch_id); + /* The destroy callback clears it */ + ASSERT(!self->sim_state_watch_id); + } + if (self->iccid_watch_id) { + ofono_sim_remove_iccid_watch(watch->sim, + self->iccid_watch_id); + /* The destroy callback clears it */ + ASSERT(!self->iccid_watch_id); + } + if (self->imsi_watch_id) { + ofono_sim_remove_imsi_watch(watch->sim, + self->imsi_watch_id); + /* The destroy callback clears it */ + ASSERT(!self->imsi_watch_id); + } + if (self->spn_watch_id) { + ofono_sim_remove_spn_watch(watch->sim, + &self->spn_watch_id); + /* The destroy callback clears it */ + ASSERT(!self->spn_watch_id); + } + watch->sim = sim; + ofono_watch_signal_queue(self, SIGNAL_SIM_CHANGED); + + /* Reset the current state */ + ofono_watch_iccid_update(self, NULL); + ofono_watch_imsi_update(self, NULL); + ofono_watch_spn_update(self, NULL); + if (sim) { + self->sim_state_watch_id = + ofono_sim_add_state_watch(sim, + ofono_watch_sim_state_notify, self, + ofono_watch_sim_state_destroy); + /* + * Unlike ofono_sim_add_state_watch, the rest + * of ofono_sim_add_xxx_watch functions call the + * notify callback if the value is already known + * to the ofono core. + * + * Also note that ofono core crashes if we add + * spn watch too early. + */ + self->iccid_watch_id = + ofono_sim_add_iccid_watch(sim, + ofono_watch_iccid_notify, self, + ofono_watch_iccid_destroy); + self->imsi_watch_id = + ofono_sim_add_imsi_watch(sim, + ofono_watch_imsi_notify, self, + ofono_watch_imsi_destroy); + } + ofono_watch_emit_queued_signals(self); + } +} + +static void ofono_watch_sim_notify(struct ofono_atom *atom, + enum ofono_atom_watch_condition cond, void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) { + struct ofono_sim *sim = __ofono_atom_get_data(atom); + + DBG_(self, "sim registered"); + ofono_watch_set_sim(self, sim); + } else if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { + DBG_(self, "sim unregistered"); + ofono_watch_set_sim(self, NULL); + } +} + +static void ofono_watch_sim_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + self->sim_watch_id = 0; +} + +static void ofono_watch_netreg_update(struct ofono_watch_object *self) +{ + struct ofono_watch *watch = &self->pub; + struct ofono_netreg *netreg = watch->netreg; + enum ofono_netreg_status status = ofono_netreg_get_status(netreg); + enum ofono_access_technology act = ofono_netreg_get_technology(netreg); + const char *mcc = ofono_netreg_get_mcc(netreg); + const char *mnc = ofono_netreg_get_mnc(netreg); + const char *name = ofono_netreg_get_name(netreg); + + if (watch->reg_status != status) { + watch->reg_status = status; + ofono_watch_signal_queue(self, SIGNAL_REG_STATUS_CHANGED); + } + if (watch->reg_tech != act) { + watch->reg_tech = act; + ofono_watch_signal_queue(self, SIGNAL_REG_TECH_CHANGED); + } + if (g_strcmp0(self->reg_mcc, mcc)) { + g_free(self->reg_mcc); + watch->reg_mcc = self->reg_mcc = g_strdup(mcc); + ofono_watch_signal_queue(self, SIGNAL_REG_MCC_CHANGED); + } + if (g_strcmp0(self->reg_mnc, mnc)) { + g_free(self->reg_mnc); + watch->reg_mnc = self->reg_mnc = g_strdup(mnc); + ofono_watch_signal_queue(self, SIGNAL_REG_MNC_CHANGED); + } + if (g_strcmp0(self->reg_name, name)) { + g_free(self->reg_name); + watch->reg_name = self->reg_name = g_strdup(name); + ofono_watch_signal_queue(self, SIGNAL_REG_NAME_CHANGED); + } +} + +static void ofono_watch_netreg_status_notify(int status, int lac, int ci, + int tech, const char *mcc, const char *mnc, void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ofono_watch_netreg_update(self); + ofono_watch_emit_queued_signals(self); +} + +static void ofono_watch_netreg_status_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ASSERT(self->netreg_status_watch_id); + self->netreg_status_watch_id = 0; +} + +static void ofono_watch_set_netreg(struct ofono_watch_object *self, + struct ofono_netreg *netreg) +{ + struct ofono_watch *watch = &self->pub; + + if (watch->netreg != netreg) { + if (self->netreg_status_watch_id) { + __ofono_netreg_remove_status_watch(watch->netreg, + self->netreg_status_watch_id); + /* The destroy callback clears it */ + ASSERT(!self->netreg_status_watch_id); + } + + watch->netreg = netreg; + ofono_watch_signal_queue(self, SIGNAL_NETREG_CHANGED); + + if (netreg) { + self->netreg_status_watch_id = + __ofono_netreg_add_status_watch(netreg, + ofono_watch_netreg_status_notify, self, + ofono_watch_netreg_status_destroy); + } + + ofono_watch_netreg_update(self); + ofono_watch_emit_queued_signals(self); + } +} + +static void ofono_watch_netreg_notify(struct ofono_atom *atom, + enum ofono_atom_watch_condition cond, void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) { + struct ofono_netreg *netreg = __ofono_atom_get_data(atom); + + DBG_(self, "netreg registered"); + ofono_watch_set_netreg(self, netreg); + } else if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { + DBG_(self, "netreg unregistered"); + ofono_watch_set_netreg(self, NULL); + } +} + +static void ofono_watch_netreg_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + self->netreg_watch_id = 0; +} + +static void ofono_watch_set_gprs(struct ofono_watch_object *self, + struct ofono_gprs *gprs) +{ + struct ofono_watch *watch = &self->pub; + + if (watch->gprs != gprs) { + watch->gprs = gprs; + + ofono_watch_signal_queue(self, SIGNAL_GPRS_CHANGED); + ofono_watch_emit_queued_signals(self); + } +} + +static void ofono_watch_gprs_notify(struct ofono_atom *atom, + enum ofono_atom_watch_condition cond, void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) { + struct ofono_gprs *gprs = __ofono_atom_get_data(atom); + + DBG_(self, "gprs registered"); + ofono_watch_set_gprs(self, gprs); + } else if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { + DBG_(self, "gprs unregistered"); + ofono_watch_set_gprs(self, NULL); + } +} + +static void ofono_watch_gprs_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + self->gprs_watch_id = 0; +} + +static void ofono_watch_online_update(struct ofono_watch_object *self, + gboolean online) +{ + struct ofono_watch *watch = &self->pub; + + if (watch->online != online) { + watch->online = online; + ofono_watch_signal_queue(self, SIGNAL_ONLINE_CHANGED); + } +} + +static void ofono_watch_online_notify(struct ofono_modem *modem, + ofono_bool_t online, void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + ASSERT(self->pub.modem == modem); + ASSERT(online == ofono_modem_get_online(modem)); + ofono_watch_online_update(self, online); + ofono_watch_emit_queued_signals(self); +} + +static void ofono_watch_online_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + self->online_watch_id = 0; +} + +static void ofono_watch_setup_modem(struct ofono_watch_object *self) +{ + struct ofono_watch *watch = &self->pub; + + ASSERT(!self->online_watch_id); + self->online_watch_id = + __ofono_modem_add_online_watch(watch->modem, + ofono_watch_online_notify, self, + ofono_watch_online_destroy); + + /* __ofono_modem_add_atom_watch() calls the notify callback if the + * atom is already registered */ + ASSERT(!self->sim_watch_id); + self->sim_watch_id = __ofono_modem_add_atom_watch(watch->modem, + OFONO_ATOM_TYPE_SIM, ofono_watch_sim_notify, + self, ofono_watch_sim_destroy); + + ASSERT(!self->netreg_watch_id); + self->netreg_watch_id = __ofono_modem_add_atom_watch(watch->modem, + OFONO_ATOM_TYPE_NETREG, ofono_watch_netreg_notify, + self, ofono_watch_netreg_destroy); + + ASSERT(!self->gprs_watch_id); + self->gprs_watch_id = __ofono_modem_add_atom_watch(watch->modem, + OFONO_ATOM_TYPE_GPRS, ofono_watch_gprs_notify, + self, ofono_watch_gprs_destroy); +} + +static void ofono_watch_cleanup_modem(struct ofono_watch_object *self, + struct ofono_modem *modem) +{ + /* + * Caller checks that modem isn't NULL. + * + * Watch ids are getting zeroed when __ofono_watchlist_free() is + * called for the respective watch list. Therefore ids can be zero + * even if we never explicitely removed them. + * + * Calling __ofono_modem_remove_online_watch() and other such + * functions after respective watch lists have been deallocated + * by modem_unregister() will crash the core. + */ + if (self->online_watch_id) { + __ofono_modem_remove_online_watch(modem, self->online_watch_id); + ASSERT(!self->online_watch_id); + } + + if (self->sim_watch_id) { + __ofono_modem_remove_atom_watch(modem, self->sim_watch_id); + ASSERT(!self->sim_watch_id); + } + + if (self->netreg_watch_id) { + __ofono_modem_remove_atom_watch(modem, self->netreg_watch_id); + ASSERT(!self->netreg_watch_id); + } + + if (self->gprs_watch_id) { + __ofono_modem_remove_atom_watch(modem, self->gprs_watch_id); + ASSERT(!self->gprs_watch_id); + } + + ofono_watch_set_sim(self, NULL); + ofono_watch_set_netreg(self, NULL); + ofono_watch_set_gprs(self, NULL); +} + +static void ofono_watch_set_modem(struct ofono_watch_object *self, + struct ofono_modem *modem) +{ + struct ofono_watch *watch = &self->pub; + + if (watch->modem != modem) { + struct ofono_modem *old_modem = watch->modem; + + watch->modem = modem; + ofono_watch_signal_queue(self, SIGNAL_MODEM_CHANGED); + if (old_modem) { + ofono_watch_cleanup_modem(self, old_modem); + } + if (modem) { + ofono_watch_setup_modem(self); + } + ofono_watch_online_update(self, ofono_modem_get_online(modem)); + ofono_watch_emit_queued_signals(self); + } +} + +static void ofono_watch_modem_notify(struct ofono_modem *modem, + ofono_bool_t added, void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + if (added) { + if (!g_strcmp0(self->path, ofono_modem_get_path(modem))) { + ofono_watch_set_modem(self, modem); + } + } else if (self->pub.modem == modem) { + ofono_watch_set_modem(self, NULL); + } +} + +static void ofono_watch_modem_destroy(void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + self->modem_watch_id = 0; +} + +static ofono_bool_t ofono_watch_modem_find(struct ofono_modem *modem, + void *user_data) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(user_data); + + if (!g_strcmp0(self->path, ofono_modem_get_path(modem))) { + self->pub.modem = modem; + ofono_watch_setup_modem(self); + return TRUE; + } else { + return FALSE; + } +} + +static void ofono_watch_initialize(struct ofono_watch_object *self, + const char *path) +{ + struct ofono_watch *watch = &self->pub; + + watch->path = self->path = g_strdup(path); + ofono_modem_find(ofono_watch_modem_find, self); + watch->online = ofono_modem_get_online(watch->modem); + self->modem_watch_id = + __ofono_modemwatch_add(ofono_watch_modem_notify, self, + ofono_watch_modem_destroy); +} + +static void ofono_watch_destroyed(gpointer key, GObject *obj) +{ + ASSERT(ofono_watch_table); + DBG("%s", (char*)key); + if (ofono_watch_table) { + ASSERT(g_hash_table_lookup(ofono_watch_table, key) == obj); + g_hash_table_remove(ofono_watch_table, key); + if (g_hash_table_size(ofono_watch_table) == 0) { + g_hash_table_unref(ofono_watch_table); + ofono_watch_table = NULL; + } + } +} + +struct ofono_watch *ofono_watch_new(const char *path) +{ + if (path) { + struct ofono_watch_object *self = NULL; + + if (ofono_watch_table) { + self = g_hash_table_lookup(ofono_watch_table, path); + } + if (self) { + g_object_ref(self); + } else { + char *key = g_strdup(path); + + self = g_object_new(OFONO_WATCH_OBJECT_TYPE, NULL); + ofono_watch_initialize(self, path); + if (!ofono_watch_table) { + /* Create the table on demand */ + ofono_watch_table = + g_hash_table_new_full(g_str_hash, + g_str_equal, g_free, NULL); + } + g_hash_table_replace(ofono_watch_table, key, self); + g_object_weak_ref(G_OBJECT(self), + ofono_watch_destroyed, key); + DBG_(self, "created"); + } + return &self->pub; + } + return NULL; +} + +struct ofono_watch *ofono_watch_ref(struct ofono_watch *watch) +{ + if (watch) { + g_object_ref(ofono_watch_object_cast(watch)); + return watch; + } else { + return NULL; + } +} + +void ofono_watch_unref(struct ofono_watch *watch) +{ + if (watch) { + g_object_unref(ofono_watch_object_cast(watch)); + } +} + +static void ofono_watch_signal_cb(struct ofono_watch_object *source, + struct ofono_watch_closure *closure) +{ + closure->cb.generic(&source->pub, closure->user_data); +} + +static unsigned long ofono_watch_add_handler(struct ofono_watch_object *self, + enum ofono_watch_signal signal, GCallback handler, + GCallback cb, void *user_data) +{ + if (self && cb) { + /* + * We can't directly connect the provided callback because + * it expects the first parameter to point to the public + * part of ofono_watch_object (i.e. ofono_watch) but glib + * will invoke it with ofono_watch_object as the first + * parameter. ofono_watch_signal_cb() will do the conversion. + */ + struct ofono_watch_closure *closure = + (struct ofono_watch_closure *)g_closure_new_simple + (sizeof(struct ofono_watch_closure), NULL); + + closure->cclosure.closure.data = closure; + closure->cclosure.callback = handler; + closure->cb.ptr = cb; + closure->user_data = user_data; + + return g_signal_connect_closure_by_id(self, + ofono_watch_signals[signal], 0, + &closure->cclosure.closure, FALSE); + } + return 0; +} + +static unsigned long ofono_watch_add_signal_handler(struct ofono_watch *watch, + enum ofono_watch_signal signal, ofono_watch_cb_t cb, void *user_data) +{ + return ofono_watch_add_handler(ofono_watch_object_cast(watch), signal, + G_CALLBACK(ofono_watch_signal_cb), G_CALLBACK(cb), user_data); +} + +#define ADD_SIGNAL_HANDLER_PROC(name,NAME) \ +unsigned long ofono_watch_add_##name##_changed_handler \ + (struct ofono_watch *w, ofono_watch_cb_t cb, void *arg) \ +{ return ofono_watch_add_signal_handler(w, SIGNAL_##NAME##_CHANGED, cb, arg); } + +ADD_SIGNAL_HANDLER_PROC(modem,MODEM) +ADD_SIGNAL_HANDLER_PROC(online,ONLINE) +ADD_SIGNAL_HANDLER_PROC(sim,SIM) +ADD_SIGNAL_HANDLER_PROC(sim_state,SIM_STATE) +ADD_SIGNAL_HANDLER_PROC(iccid,ICCID) +ADD_SIGNAL_HANDLER_PROC(imsi,IMSI) +ADD_SIGNAL_HANDLER_PROC(spn,SPN) +ADD_SIGNAL_HANDLER_PROC(netreg,NETREG) +ADD_SIGNAL_HANDLER_PROC(reg_status,REG_STATUS) +ADD_SIGNAL_HANDLER_PROC(reg_mcc,REG_MCC) +ADD_SIGNAL_HANDLER_PROC(reg_mnc,REG_MNC) +ADD_SIGNAL_HANDLER_PROC(reg_name,REG_NAME) +ADD_SIGNAL_HANDLER_PROC(reg_tech,REG_TECH) +ADD_SIGNAL_HANDLER_PROC(gprs,GPRS) + +static void ofono_watch_gprs_settings_signal_cb(struct ofono_watch_object *src, + enum ofono_gprs_context_type type, + const struct ofono_gprs_primary_context *ctx, + struct ofono_watch_closure *closure) +{ + closure->cb.gprs_settings(&src->pub, type, ctx, closure->user_data); +} + +unsigned long ofono_watch_add_gprs_settings_changed_handler + (struct ofono_watch *watch, ofono_watch_gprs_settings_cb_t cb, + void *user_data) +{ + return ofono_watch_add_handler(ofono_watch_object_cast(watch), + SIGNAL_GPRS_SETTINGS_CHANGED, + G_CALLBACK(ofono_watch_gprs_settings_signal_cb), + G_CALLBACK(cb), user_data); +} + +void ofono_watch_remove_handler(struct ofono_watch *watch, unsigned long id) +{ + if (watch && id) { + g_signal_handler_disconnect(ofono_watch_object_cast(watch), + id); + } +} + +void ofono_watch_remove_handlers(struct ofono_watch *watch, unsigned long *ids, + unsigned int count) +{ + struct ofono_watch_object *self = ofono_watch_object_cast(watch); + + if (self && ids && count) { + unsigned int i; + + for (i = 0; i < count; i++) { + if (ids[i]) { + g_signal_handler_disconnect(self, ids[i]); + ids[i] = 0; + } + } + } +} + +void __ofono_watch_gprs_settings_changed(const char *path, + enum ofono_gprs_context_type type, + const struct ofono_gprs_primary_context *settings) +{ + if (path && ofono_watch_table) { + struct ofono_watch_object *self = + g_hash_table_lookup(ofono_watch_table, path); + + if (self) { + g_object_ref(self); + g_signal_emit(self, ofono_watch_signals + [SIGNAL_GPRS_SETTINGS_CHANGED], 0, type, + settings); + g_object_unref(self); + } + } +} + +static void ofono_watch_object_init(struct ofono_watch_object *self) +{ + struct ofono_watch *watch = &self->pub; + + watch->reg_status = OFONO_NETREG_STATUS_NONE; + watch->reg_tech = OFONO_ACCESS_TECHNOLOGY_NONE; +} + +static void ofono_watch_object_finalize(GObject *object) +{ + struct ofono_watch_object *self = OFONO_WATCH_OBJECT(object); + struct ofono_watch *watch = &self->pub; + + if (watch->modem) { + struct ofono_modem *modem = watch->modem; + + watch->modem = NULL; + ofono_watch_cleanup_modem(self, modem); + } + __ofono_modemwatch_remove(self->modem_watch_id); + ASSERT(!self->modem_watch_id); + g_free(self->path); + G_OBJECT_CLASS(ofono_watch_object_parent_class)->finalize(object); +} + +static void ofono_watch_object_class_init(OfonoWatchObjectClass *klass) +{ + G_OBJECT_CLASS(klass)->finalize = ofono_watch_object_finalize; + NEW_SIGNAL(klass, MODEM); + NEW_SIGNAL(klass, ONLINE); + NEW_SIGNAL(klass, SIM); + NEW_SIGNAL(klass, SIM_STATE); + NEW_SIGNAL(klass, ICCID); + NEW_SIGNAL(klass, IMSI); + NEW_SIGNAL(klass, SPN); + NEW_SIGNAL(klass, NETREG); + NEW_SIGNAL(klass, REG_STATUS); + NEW_SIGNAL(klass, REG_MCC); + NEW_SIGNAL(klass, REG_MNC); + NEW_SIGNAL(klass, REG_NAME); + NEW_SIGNAL(klass, REG_TECH); + NEW_SIGNAL(klass, GPRS); + ofono_watch_signals[SIGNAL_GPRS_SETTINGS_CHANGED] = + g_signal_new(SIGNAL_GPRS_SETTINGS_CHANGED_NAME, + G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + 2, G_TYPE_INT, G_TYPE_POINTER); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/src/sim-info-dbus.c b/src/sim-info-dbus.c new file mode 100644 index 0000000000000000000000000000000000000000..629d76e9d9a04b3b1a9ef4d779c88d87d1844c12 --- /dev/null +++ b/src/sim-info-dbus.c @@ -0,0 +1,285 @@ +/* + * oFono - Open Source Telephony + * + * Copyright (C) 2017-2021 Jolla 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. + */ + +#include "sim-info.h" + +#include <ofono/dbus.h> +#include <ofono/sailfish_watch.h> + +#include <gdbus.h> + +#include "ofono.h" + +enum watch_event_id { + WATCH_EVENT_MODEM, + WATCH_EVENT_COUNT +}; + +enum sim_info_event_id { + SIM_INFO_EVENT_ICCID, + SIM_INFO_EVENT_IMSI, + SIM_INFO_EVENT_SPN, + SIM_INFO_EVENT_COUNT +}; + +typedef struct sim_info_dbus { + struct sim_info *info; + struct ofono_watch *watch; + DBusConnection *conn; + gulong watch_event_id[WATCH_EVENT_COUNT]; + gulong info_event_id[SIM_INFO_EVENT_COUNT]; +} SimInfoDBus; + +#define SIM_INFO_DBUS_INTERFACE "org.nemomobile.ofono.SimInfo" +#define SIM_INFO_DBUS_INTERFACE_VERSION (1) + +#define SIM_INFO_DBUS_ICCID_CHANGED_SIGNAL "CardIdentifierChanged" +#define SIM_INFO_DBUS_IMSI_CHANGED_SIGNAL "SubscriberIdentityChanged" +#define SIM_INFO_DBUS_SPN_CHANGED_SIGNAL "ServiceProviderNameChanged" + +static void sim_info_dbus_append_version(DBusMessageIter *it) +{ + const dbus_int32_t version = SIM_INFO_DBUS_INTERFACE_VERSION; + + dbus_message_iter_append_basic(it, DBUS_TYPE_INT32, &version); +} + +static void sim_info_dbus_append_string(DBusMessageIter *it, const char *str) +{ + if (!str) str = ""; + dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &str); +} + +static DBusMessage *sim_info_dbus_reply_with_string(DBusMessage *msg, + const char *str) +{ + DBusMessage *reply = dbus_message_new_method_return(msg); + DBusMessageIter iter; + + dbus_message_iter_init_append(reply, &iter); + sim_info_dbus_append_string(&iter, str); + return reply; +} + +static DBusMessage *sim_info_dbus_get_all(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + SimInfoDBus *dbus = data; + struct sim_info *info = dbus->info; + DBusMessage *reply = dbus_message_new_method_return(msg); + DBusMessageIter it; + + dbus_message_iter_init_append(reply, &it); + sim_info_dbus_append_version(&it); + sim_info_dbus_append_string(&it, info->iccid); + sim_info_dbus_append_string(&it, info->imsi); + sim_info_dbus_append_string(&it, info->spn); + return reply; +} + +static DBusMessage *sim_info_dbus_get_version(DBusConnection *dc, + DBusMessage *msg, void *data) +{ + DBusMessage *reply = dbus_message_new_method_return(msg); + DBusMessageIter it; + + dbus_message_iter_init_append(reply, &it); + sim_info_dbus_append_version(&it); + return reply; +} + +static DBusMessage *sim_info_dbus_get_iccid(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + SimInfoDBus *dbus = data; + + return sim_info_dbus_reply_with_string(msg, dbus->info->iccid); +} + +static DBusMessage *sim_info_dbus_get_imsi(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + SimInfoDBus *dbus = data; + + return sim_info_dbus_reply_with_string(msg, dbus->info->imsi); +} + +static DBusMessage *sim_info_dbus_get_spn(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + SimInfoDBus *dbus = data; + + return sim_info_dbus_reply_with_string(msg, dbus->info->spn); +} + +#define SIM_INFO_DBUS_VERSION_ARG {"version", "i"} +#define SIM_INFO_DBUS_ICCID_ARG {"iccid", "s"} +#define SIM_INFO_DBUS_IMSI_ARG {"imsi", "s"} +#define SIM_INFO_DBUS_SPN_ARG {"spn" , "s"} + +#define SIM_INFO_DBUS_GET_ALL_ARGS \ + SIM_INFO_DBUS_VERSION_ARG, \ + SIM_INFO_DBUS_ICCID_ARG, \ + SIM_INFO_DBUS_IMSI_ARG, \ + SIM_INFO_DBUS_SPN_ARG + +static const GDBusMethodTable sim_info_dbus_methods[] = { + { GDBUS_METHOD("GetAll", + NULL, GDBUS_ARGS(SIM_INFO_DBUS_GET_ALL_ARGS), + sim_info_dbus_get_all) }, + { GDBUS_METHOD("GetInterfaceVersion", + NULL, GDBUS_ARGS(SIM_INFO_DBUS_VERSION_ARG), + sim_info_dbus_get_version) }, + { GDBUS_METHOD("GetCardIdentifier", + NULL, GDBUS_ARGS(SIM_INFO_DBUS_ICCID_ARG), + sim_info_dbus_get_iccid) }, + { GDBUS_METHOD("GetSubscriberIdentity", + NULL, GDBUS_ARGS(SIM_INFO_DBUS_IMSI_ARG), + sim_info_dbus_get_imsi) }, + { GDBUS_METHOD("GetServiceProviderName", + NULL, GDBUS_ARGS(SIM_INFO_DBUS_SPN_ARG), + sim_info_dbus_get_spn) }, + { } +}; + +static const GDBusSignalTable sim_info_dbus_signals[] = { + { GDBUS_SIGNAL(SIM_INFO_DBUS_ICCID_CHANGED_SIGNAL, + GDBUS_ARGS(SIM_INFO_DBUS_ICCID_ARG)) }, + { GDBUS_SIGNAL(SIM_INFO_DBUS_IMSI_CHANGED_SIGNAL, + GDBUS_ARGS(SIM_INFO_DBUS_IMSI_ARG)) }, + { GDBUS_SIGNAL(SIM_INFO_DBUS_SPN_CHANGED_SIGNAL, + GDBUS_ARGS(SIM_INFO_DBUS_SPN_ARG)) }, + { } +}; + +static void sim_info_dbus_modem_cb(struct ofono_watch *watch, void *data) +{ + if (watch->modem) { + ofono_modem_add_interface(watch->modem, + SIM_INFO_DBUS_INTERFACE); + } +} + +static void sim_info_dbus_emit(SimInfoDBus *dbus, + const char *signal, const char *value) +{ + const char *arg = value; + + if (!arg) arg = ""; + g_dbus_emit_signal(dbus->conn, dbus->info->path, + SIM_INFO_DBUS_INTERFACE, signal, + DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); +} + +static void sim_info_dbus_iccid_cb(struct sim_info *info, void *data) +{ + sim_info_dbus_emit((SimInfoDBus *)data, + SIM_INFO_DBUS_ICCID_CHANGED_SIGNAL, info->iccid); +} + +static void sim_info_dbus_imsi_cb(struct sim_info *info, void *data) +{ + sim_info_dbus_emit((SimInfoDBus *)data, + SIM_INFO_DBUS_IMSI_CHANGED_SIGNAL, info->imsi); +} + +static void sim_info_dbus_spn_cb(struct sim_info *info, void *data) +{ + sim_info_dbus_emit((SimInfoDBus *)data, + SIM_INFO_DBUS_SPN_CHANGED_SIGNAL, info->spn); +} + +SimInfoDBus *sim_info_dbus_new(struct sim_info *info) +{ + SimInfoDBus *dbus = g_new0(SimInfoDBus, 1); + + DBG("%s", info->path); + dbus->info = sim_info_ref(info); + dbus->watch = ofono_watch_new(info->path); + dbus->conn = dbus_connection_ref(ofono_dbus_get_connection()); + + /* Register D-Bus interface */ + if (g_dbus_register_interface(dbus->conn, dbus->info->path, + SIM_INFO_DBUS_INTERFACE, sim_info_dbus_methods, + sim_info_dbus_signals, NULL, dbus, NULL)) { + if (dbus->watch->modem) { + ofono_modem_add_interface(dbus->watch->modem, + SIM_INFO_DBUS_INTERFACE); + } + + dbus->watch_event_id[WATCH_EVENT_MODEM] = + ofono_watch_add_modem_changed_handler(dbus->watch, + sim_info_dbus_modem_cb, dbus); + dbus->info_event_id[SIM_INFO_EVENT_ICCID] = + sim_info_add_iccid_changed_handler(info, + sim_info_dbus_iccid_cb, dbus); + dbus->info_event_id[SIM_INFO_EVENT_IMSI] = + sim_info_add_imsi_changed_handler(info, + sim_info_dbus_imsi_cb, dbus); + dbus->info_event_id[SIM_INFO_EVENT_SPN] = + sim_info_add_spn_changed_handler(info, + sim_info_dbus_spn_cb, dbus); + + return dbus; + } else { + ofono_error("SimInfo D-Bus register failed"); + sim_info_dbus_free(dbus); + return NULL; + } +} + +SimInfoDBus *sim_info_dbus_new_path(const char *path) +{ + SimInfoDBus *dbus = NULL; + struct sim_info *info = sim_info_new(path); + + if (info) { + dbus = sim_info_dbus_new(info); + sim_info_unref(info); + } + + return dbus; +} + +void sim_info_dbus_free(SimInfoDBus *dbus) +{ + if (dbus) { + DBG("%s", dbus->info->path); + g_dbus_unregister_interface(dbus->conn, dbus->info->path, + SIM_INFO_DBUS_INTERFACE); + if (dbus->watch->modem) { + ofono_modem_remove_interface(dbus->watch->modem, + SIM_INFO_DBUS_INTERFACE); + } + dbus_connection_unref(dbus->conn); + + ofono_watch_remove_all_handlers(dbus->watch, + dbus->watch_event_id); + ofono_watch_unref(dbus->watch); + + sim_info_remove_all_handlers(dbus->info, dbus->info_event_id); + sim_info_unref(dbus->info); + + g_free(dbus); + } +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/src/sim-info.c b/src/sim-info.c new file mode 100644 index 0000000000000000000000000000000000000000..71e6fb30f6051f98ac4c3d35c392c54d727c9e4e --- /dev/null +++ b/src/sim-info.c @@ -0,0 +1,623 @@ +/* + * oFono - Open Source Telephony + * + * Copyright (C) 2017-2021 Jolla 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. + */ + +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <ofono/sailfish_watch.h> + +#include <gutil_misc.h> +#include <gutil_log.h> + +#include "ofono.h" +#include "common.h" +#include "storage.h" +#include "sim-info.h" + +#define SIM_INFO_STORE "cache" +#define SIM_INFO_STORE_GROUP "sim" +#define SIM_INFO_STORE_SPN "spn" + +/* ICCID -> IMSI map */ +#define SIM_ICCID_MAP "iccidmap" +#define SIM_ICCID_MAP_IMSI "imsi" + +#define DEFAULT_SPN_BUFSIZE 8 +G_STATIC_ASSERT(DEFAULT_SPN_BUFSIZE >= \ + OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1); + +typedef GObjectClass SimInfoClass; +typedef struct sim_info SimInfo; + +enum ofono_watch_events { + WATCH_EVENT_SIM, + WATCH_EVENT_SIM_STATE, + WATCH_EVENT_ICCID, + WATCH_EVENT_IMSI, + WATCH_EVENT_SPN, + WATCH_EVENT_NETREG, + WATCH_EVENT_COUNT +}; + +typedef struct sim_info_priv { + struct ofono_watch *watch; + struct ofono_netreg *netreg; + char *iccid; + char *imsi; + char *cached_spn; + char *sim_spn; + char *public_spn; + char default_spn[DEFAULT_SPN_BUFSIZE]; + gulong watch_event_id[WATCH_EVENT_COUNT]; + guint netreg_status_watch_id; + gboolean update_imsi_cache; + gboolean update_iccid_map; + int queued_signals; +} SimInfoPriv; + +enum sim_info_signal { + SIGNAL_ICCID_CHANGED, + SIGNAL_IMSI_CHANGED, + SIGNAL_SPN_CHANGED, + SIGNAL_COUNT +}; + +#define SIGNAL_ICCID_CHANGED_NAME "sailfish-siminfo-iccid-changed" +#define SIGNAL_IMSI_CHANGED_NAME "sailfish-siminfo-imsi-changed" +#define SIGNAL_SPN_CHANGED_NAME "sailfish-siminfo-spn-changed" + +static guint sim_info_signals[SIGNAL_COUNT] = { 0 }; + +G_DEFINE_TYPE(SimInfo, sim_info, G_TYPE_OBJECT) +#define SIMINFO_TYPE (sim_info_get_type()) +#define SIMINFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ + SIMINFO_TYPE, SimInfo)) + +#define NEW_SIGNAL(klass,name) \ + sim_info_signals[SIGNAL_##name##_CHANGED] = \ + g_signal_new(SIGNAL_##name##_CHANGED_NAME, \ + G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \ + 0, NULL, NULL, NULL, G_TYPE_NONE, 0) + +/* Skip the leading slash from the modem path: */ +#define DBG_(obj,fmt,args...) DBG("%s " fmt, (obj)->path+1, ##args) + +static int sim_info_signal_bit(enum sim_info_signal id) +{ + return (1 << id); +} + +static void sim_info_signal_emit(SimInfo *self, enum sim_info_signal id) +{ + self->priv->queued_signals &= ~sim_info_signal_bit(id); + g_signal_emit(self, sim_info_signals[id], 0); +} + +static void sim_info_signal_queue(SimInfo *self, enum sim_info_signal id) +{ + self->priv->queued_signals |= sim_info_signal_bit(id); +} + +static void sim_info_emit_queued_signals(SimInfo *self) +{ + SimInfoPriv *priv = self->priv; + int i; + + for (i = 0; priv->queued_signals && i < SIGNAL_COUNT; i++) { + if (priv->queued_signals & sim_info_signal_bit(i)) { + sim_info_signal_emit(self, i); + } + } +} + +static void sim_info_update_imsi_cache(SimInfo *self) +{ + SimInfoPriv *priv = self->priv; + + if (priv->update_imsi_cache && priv->imsi && priv->imsi[0] && + priv->cached_spn && priv->cached_spn[0]) { + gboolean save = FALSE; + const char *store = SIM_INFO_STORE; + GKeyFile *cache = storage_open(priv->imsi, store); + char *spn = g_key_file_get_string(cache, SIM_INFO_STORE_GROUP, + SIM_INFO_STORE_SPN, NULL); + + if (g_strcmp0(priv->cached_spn, spn)) { + save = TRUE; + g_key_file_set_string(cache, SIM_INFO_STORE_GROUP, + SIM_INFO_STORE_SPN, priv->cached_spn); + } + + /* + * Since we are most likely running on flash which + * supports a limited number of writes, don't overwrite + * the file unless something has actually changed. + */ + if (save) { + DBG_(self, "updating " STORAGEDIR "/%s/%s", + priv->imsi, store); + storage_close(priv->imsi, store, cache, TRUE); + } else { + g_key_file_free(cache); + } + + g_free(spn); + priv->update_imsi_cache = FALSE; + } +} + +static void sim_info_update_iccid_map(SimInfo *self) +{ + SimInfoPriv *priv = self->priv; + + if (priv->update_iccid_map && priv->iccid && priv->iccid[0] && + priv->imsi && priv->imsi[0]) { + const char *store = SIM_ICCID_MAP; + GKeyFile *map = storage_open(NULL, store); + char *imsi = g_key_file_get_string(map, + SIM_ICCID_MAP_IMSI, priv->iccid, NULL); + + /* + * Since we are most likely running on flash which + * supports a limited number of writes, don't overwrite + * the file unless something has actually changed. + */ + if (g_strcmp0(imsi, priv->imsi)) { + DBG_(self, "updating " STORAGEDIR "/%s", store); + g_key_file_set_string(map, SIM_ICCID_MAP_IMSI, + priv->iccid, priv->imsi); + storage_close(NULL, store, map, TRUE); + } else { + g_key_file_free(map); + } + + g_free(imsi); + priv->update_iccid_map = FALSE; + } +} + +static void sim_info_update_public_spn(SimInfo *self) +{ + SimInfoPriv *priv = self->priv; + const char *spn = priv->sim_spn ? priv->sim_spn : + priv->cached_spn ? priv->cached_spn : + priv->default_spn; + + if (g_strcmp0(priv->public_spn, spn)) { + g_free(priv->public_spn); + if (spn && spn[0]) { + DBG_(self, "public spn \"%s\"", spn); + priv->public_spn = g_strdup(spn); + } else { + DBG_(self, "no public spn"); + priv->public_spn = NULL; + } + self->spn = priv->public_spn; + sim_info_signal_queue(self, SIGNAL_SPN_CHANGED); + } +} + +static void sim_info_set_cached_spn(SimInfo *self, const char *spn) +{ + SimInfoPriv *priv = self->priv; + + GASSERT(spn); + if (g_strcmp0(priv->cached_spn, spn)) { + DBG_(self, "%s", spn); + g_free(priv->cached_spn); + priv->cached_spn = g_strdup(spn); + priv->update_imsi_cache = TRUE; + sim_info_update_imsi_cache(self); + sim_info_update_public_spn(self); + } +} + +static void sim_info_set_spn(SimInfo *self, const char *spn) +{ + SimInfoPriv *priv = self->priv; + + GASSERT(spn); + if (g_strcmp0(priv->sim_spn, spn)) { + DBG_(self, "%s", spn); + g_free(priv->sim_spn); + priv->sim_spn = g_strdup(spn); + priv->update_imsi_cache = TRUE; + sim_info_set_cached_spn(self, spn); + sim_info_update_imsi_cache(self); + sim_info_update_public_spn(self); + } +} + +static void sim_info_update_spn(SimInfo *self) +{ + struct ofono_watch *watch = self->priv->watch; + + if (watch->spn && watch->spn[0]) { + sim_info_set_spn(self, watch->spn); + } +} + +static void sim_info_update_default_spn(SimInfo *self) +{ + SimInfoPriv *priv = self->priv; + struct ofono_sim *sim = priv->watch->sim; + char buf[DEFAULT_SPN_BUFSIZE]; + const char *mcc = NULL; + const char *mnc = NULL; + + if (sim && ofono_sim_get_state(sim) == OFONO_SIM_STATE_READY) { + mcc = ofono_sim_get_mcc(sim); + mnc = ofono_sim_get_mnc(sim); + } + + if (mcc && mnc) { + snprintf(buf, DEFAULT_SPN_BUFSIZE, "%s%s", mcc, mnc); + buf[DEFAULT_SPN_BUFSIZE - 1] = 0; + } else { + buf[0] = 0; + } + + if (strcmp(buf, priv->default_spn)) { + strncpy(priv->default_spn, buf, DEFAULT_SPN_BUFSIZE); + DBG_(self, "default spn \"%s\"", priv->default_spn); + sim_info_update_public_spn(self); + } +} + +static void sim_info_update_imsi(SimInfo *self) +{ + SimInfoPriv *priv = self->priv; + const char *imsi = priv->watch->imsi; + + /* IMSI only gets reset when ICCID disappears, ignore NULL IMSI here */ + if (imsi && imsi[0] && g_strcmp0(priv->imsi, imsi)) { + DBG_(self, "%s", imsi); + g_free(priv->imsi); + self->imsi = priv->imsi = g_strdup(imsi); + priv->update_iccid_map = TRUE; + sim_info_update_iccid_map(self); + sim_info_update_imsi_cache(self); + sim_info_signal_queue(self, SIGNAL_IMSI_CHANGED); + } + + /* Check if MCC/MNC have changed */ + sim_info_update_default_spn(self); +} + +static void sim_info_network_check(SimInfo *self) +{ + SimInfoPriv *priv = self->priv; + struct ofono_sim *sim = priv->watch->sim; + enum ofono_netreg_status reg_status = + ofono_netreg_get_status(priv->netreg); + + if (sim && ofono_sim_get_state(sim) == OFONO_SIM_STATE_READY && + (reg_status == NETWORK_REGISTRATION_STATUS_REGISTERED || + reg_status == NETWORK_REGISTRATION_STATUS_ROAMING)) { + const char *sim_mcc = ofono_sim_get_mcc(sim); + const char *sim_mnc = ofono_sim_get_mnc(sim); + const char *net_mcc = ofono_netreg_get_mcc(priv->netreg); + const char *net_mnc = ofono_netreg_get_mnc(priv->netreg); + const char *name = ofono_netreg_get_name(priv->netreg); + + if (sim_mcc && sim_mcc[0] && sim_mnc && sim_mnc[0] && + net_mcc && net_mcc[0] && net_mnc && net_mnc[0] && + name && name[0] && !strcmp(sim_mcc, net_mcc) && + !strcmp(sim_mnc, net_mnc)) { + + /* + * If EFspn is present then sim_spn should be set + * before we get registered with the network. + */ + DBG_(self, "home network \"%s\"", name); + if (!priv->sim_spn) { + sim_info_set_cached_spn(self, name); + } + } + } +} + +static void sim_info_load_cache(SimInfo *self) +{ + SimInfoPriv *priv = self->priv; + + if (priv->iccid && priv->iccid[0]) { + GKeyFile *map = storage_open(NULL, SIM_ICCID_MAP); + char *imsi = g_key_file_get_string(map, SIM_ICCID_MAP_IMSI, + priv->iccid, NULL); + + g_key_file_free(map); + if (imsi && imsi[0] && g_strcmp0(priv->imsi, imsi)) { + if (priv->imsi && priv->imsi[0]) { + /* Need to update ICCID -> IMSI map */ + DBG_(self, "IMSI changed %s -> %s", + priv->imsi, imsi); + priv->update_imsi_cache = TRUE; + } + g_free(priv->imsi); + self->imsi = priv->imsi = imsi; + DBG_(self, "imsi[%s] = %s", priv->iccid, imsi); + sim_info_update_iccid_map(self); + sim_info_update_default_spn(self); + sim_info_signal_queue(self, + SIGNAL_IMSI_CHANGED); + } else if (imsi) { + g_free(imsi); + } else { + DBG_(self, "no imsi for iccid %s", priv->iccid); + } + } + + if (priv->imsi && priv->imsi[0]) { + GKeyFile *cache = storage_open(priv->imsi, SIM_INFO_STORE); + char *spn = g_key_file_get_string(cache, SIM_INFO_STORE_GROUP, + SIM_INFO_STORE_SPN, NULL); + + g_key_file_free(cache); + if (spn && spn[0] && g_strcmp0(priv->cached_spn, spn)) { + if (priv->cached_spn && priv->cached_spn[0]) { + /* Need to update the cache file */ + DBG_(self, "spn changing %s -> %s", + priv->cached_spn, spn); + priv->update_imsi_cache = TRUE; + } + g_free(priv->cached_spn); + priv->cached_spn = spn; + DBG_(self, "spn[%s] = \"%s\"", priv->imsi, spn); + sim_info_update_imsi_cache(self); + sim_info_update_public_spn(self); + } else if (spn) { + g_free(spn); + } else { + DBG_(self, "no spn for imsi %s", priv->imsi); + } + } +} + +static void sim_info_set_iccid(SimInfo *self, const char *iccid) +{ + SimInfoPriv *priv = self->priv; + + if (g_strcmp0(priv->iccid, iccid)) { + g_free(priv->iccid); + self->iccid = priv->iccid = g_strdup(iccid); + sim_info_signal_queue(self, SIGNAL_ICCID_CHANGED); + if (iccid) { + sim_info_load_cache(self); + } else { + DBG_(self, "no more iccid"); + if (priv->imsi) { + g_free(priv->imsi); + self->imsi = priv->imsi = NULL; + sim_info_signal_queue(self, + SIGNAL_IMSI_CHANGED); + } + if (priv->sim_spn) { + g_free(priv->sim_spn); + priv->sim_spn = NULL; + } + if (priv->cached_spn) { + g_free(priv->cached_spn); + priv->cached_spn = NULL; + } + /* No more default SPN too */ + priv->default_spn[0] = 0; + sim_info_update_public_spn(self); + } + } +} + +static void sim_info_iccid_watch_cb(struct ofono_watch *watch, void *data) +{ + SimInfo *self = SIMINFO(data); + + DBG_(self, "%s", watch->iccid); + sim_info_set_iccid(self, watch->iccid); + sim_info_emit_queued_signals(self); +} + +static void sim_info_imsi_watch_cb(struct ofono_watch *watch, void *data) +{ + SimInfo *self = SIMINFO(data); + + sim_info_update_imsi(self); + sim_info_emit_queued_signals(self); +} + +static void sim_info_spn_watch_cb(struct ofono_watch *watch, void *data) +{ + SimInfo *self = SIMINFO(data); + + sim_info_update_spn(self); + sim_info_emit_queued_signals(self); +} + +static void sim_info_netreg_watch(int status, int lac, int ci, + int tech, const char *mcc, const char *mnc, void *data) +{ + SimInfo *self = SIMINFO(data); + + sim_info_network_check(self); + sim_info_emit_queued_signals(self); +} + +static void sim_info_netreg_watch_done(void *data) +{ + SimInfo *self = SIMINFO(data); + SimInfoPriv *priv = self->priv; + + GASSERT(priv->netreg_status_watch_id); + priv->netreg_status_watch_id = 0; +} + +static void sim_info_set_netreg(SimInfo *self, struct ofono_netreg *netreg) +{ + SimInfoPriv *priv = self->priv; + + if (priv->netreg != netreg) { + if (netreg) { + DBG_(self, "netreg attached"); + priv->netreg = netreg; + priv->netreg_status_watch_id = + __ofono_netreg_add_status_watch(netreg, + sim_info_netreg_watch, self, + sim_info_netreg_watch_done); + sim_info_network_check(self); + } else if (priv->netreg) { + if (priv->netreg_status_watch_id) { + __ofono_netreg_remove_status_watch(priv->netreg, + priv->netreg_status_watch_id); + GASSERT(!priv->netreg_status_watch_id); + } + DBG_(self, "netreg detached"); + priv->netreg = NULL; + } + } +} + +static void sim_info_netreg_changed(struct ofono_watch *watch, void *data) +{ + SimInfo *self = SIMINFO(data); + + sim_info_set_netreg(self, watch->netreg); + sim_info_emit_queued_signals(self); +} + +SimInfo *sim_info_new(const char *path) +{ + SimInfo *self = NULL; + + if (path) { + struct ofono_watch *watch = ofono_watch_new(path); + SimInfoPriv *priv; + + self = g_object_new(SIMINFO_TYPE, NULL); + priv = self->priv; + priv->watch = watch; + self->path = watch->path; + priv->watch_event_id[WATCH_EVENT_ICCID] = + ofono_watch_add_iccid_changed_handler(watch, + sim_info_iccid_watch_cb, self); + priv->watch_event_id[WATCH_EVENT_IMSI] = + ofono_watch_add_imsi_changed_handler(watch, + sim_info_imsi_watch_cb, self); + priv->watch_event_id[WATCH_EVENT_SPN] = + ofono_watch_add_spn_changed_handler(watch, + sim_info_spn_watch_cb, self); + priv->watch_event_id[WATCH_EVENT_NETREG] = + ofono_watch_add_netreg_changed_handler(watch, + sim_info_netreg_changed, self); + sim_info_set_iccid(self, watch->iccid); + sim_info_set_netreg(self, watch->netreg); + sim_info_update_imsi(self); + sim_info_update_spn(self); + sim_info_network_check(self); + + /* Clear queued events, if any */ + priv->queued_signals = 0; + } + return self; +} + +SimInfo *sim_info_ref(SimInfo *self) +{ + if (self) { + g_object_ref(SIMINFO(self)); + return self; + } else { + return NULL; + } +} + +void sim_info_unref(SimInfo *self) +{ + if (self) { + g_object_unref(SIMINFO(self)); + } +} + +gulong sim_info_add_iccid_changed_handler(SimInfo *s, sim_info_cb_t cb, + void *arg) +{ + return (s && cb) ? g_signal_connect(s, SIGNAL_ICCID_CHANGED_NAME, + G_CALLBACK(cb), arg) : 0; +} + +gulong sim_info_add_imsi_changed_handler(SimInfo *s, sim_info_cb_t cb, + void *arg) +{ + return (s && cb) ? g_signal_connect(s, SIGNAL_IMSI_CHANGED_NAME, + G_CALLBACK(cb), arg) : 0; +} + +gulong sim_info_add_spn_changed_handler(SimInfo *s, + sim_info_cb_t cb, void *arg) +{ + return (s && cb) ? g_signal_connect(s, SIGNAL_SPN_CHANGED_NAME, + G_CALLBACK(cb), arg) : 0; +} + +void sim_info_remove_handler(SimInfo *s, gulong id) +{ + if (s && id) { + g_signal_handler_disconnect(s, id); + } +} + +void sim_info_remove_handlers(SimInfo *self, gulong *ids, int count) +{ + gutil_disconnect_handlers(self, ids, count); +} + +static void sim_info_init(SimInfo *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, SIMINFO_TYPE, + SimInfoPriv); +} + +static void sim_info_finalize(GObject *object) +{ + SimInfo *self = SIMINFO(object); + SimInfoPriv *priv = self->priv; + + ofono_watch_remove_all_handlers(priv->watch, priv->watch_event_id); + ofono_watch_unref(priv->watch); + g_free(priv->iccid); + g_free(priv->imsi); + g_free(priv->sim_spn); + g_free(priv->cached_spn); + g_free(priv->public_spn); + G_OBJECT_CLASS(sim_info_parent_class)->finalize(object); +} + +static void sim_info_class_init(SimInfoClass *klass) +{ + G_OBJECT_CLASS(klass)->finalize = sim_info_finalize; + g_type_class_add_private(klass, sizeof(SimInfoPriv)); + NEW_SIGNAL(klass, ICCID); + NEW_SIGNAL(klass, IMSI); + NEW_SIGNAL(klass, SPN); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/src/sim-info.h b/src/sim-info.h new file mode 100644 index 0000000000000000000000000000000000000000..38a80619df8362422c6c69495762953ef0a56750 --- /dev/null +++ b/src/sim-info.h @@ -0,0 +1,72 @@ +/* + * oFono - Open Source Telephony + * + * Copyright (C) 2017-2021 Jolla 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. + */ + +#ifndef SIM_INFO_H +#define SIM_INFO_H + +#include <ofono/types.h> + +#include <glib.h> +#include <glib-object.h> + +/* + * Note that iccid, imsi and spn provided by this class can be cached, + * i.e. become available before the pin code is entered and before those + * are known to the ofono core. That's the whole purpose of this thing. + */ +struct ofono_modem; +struct sim_info_priv; +struct sim_info { + GObject object; + struct sim_info_priv *priv; + const char *path; + const char *iccid; + const char *imsi; + const char *spn; +}; + +typedef void (*sim_info_cb_t)(struct sim_info *si, void *user_data); + +/* SIM info object associated with the particular slot */ +struct sim_info *sim_info_new(const char *path); +struct sim_info *sim_info_ref(struct sim_info *si); +void sim_info_unref(struct sim_info *si); +gulong sim_info_add_iccid_changed_handler(struct sim_info *si, + sim_info_cb_t cb, void *user_data); +gulong sim_info_add_imsi_changed_handler(struct sim_info *si, + sim_info_cb_t cb, void *user_data); +gulong sim_info_add_spn_changed_handler(struct sim_info *si, + sim_info_cb_t cb, void *user_data); +void sim_info_remove_handler(struct sim_info *si, gulong id); +void sim_info_remove_handlers(struct sim_info *si, gulong *ids, int count); + +#define sim_info_remove_all_handlers(si,ids) \ + sim_info_remove_handlers(si, ids, G_N_ELEMENTS(ids)) + +/* And the D-Bus interface for it */ +struct sim_info_dbus; +struct sim_info_dbus *sim_info_dbus_new (struct sim_info *si); +struct sim_info_dbus *sim_info_dbus_new_path(const char *path); +void sim_info_dbus_free(struct sim_info_dbus *dbus); + +#endif /* SIM_INFO_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/src/sim.c b/src/sim.c index 8a97e87612d966309bc9996019ff11b8b0bfc3a0..2f2cea7d72d622f4f4a9abf92d6aece2953c7657 100644 --- a/src/sim.c +++ b/src/sim.c @@ -72,6 +72,7 @@ struct ofono_sim_aid_session { struct ofono_sim { /* Contents of the SIM file system, in rough initialization order */ char *iccid; + struct ofono_watchlist *iccid_watches; char **language_prefs; unsigned char *efli; @@ -96,6 +97,7 @@ struct ofono_sim { char *imsi; char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; + struct ofono_watchlist *imsi_watches; GSList *own_numbers; GSList *new_numbers; @@ -378,6 +380,94 @@ static void call_state_watches(struct ofono_sim *sim) } } +static unsigned int add_watch_item(struct ofono_watchlist *watchlist, + void *notify, void *notify_data, + ofono_destroy_func destroy) +{ + struct ofono_watchlist_item *item; + + item = g_new0(struct ofono_watchlist_item, 1); + item->notify = notify; + item->notify_data = notify_data; + item->destroy = destroy; + + return __ofono_watchlist_add_item(watchlist, item); +} + +static void iccid_watch_cb(gpointer data, gpointer user_data) +{ + struct ofono_watchlist_item *item = data; + struct ofono_sim *sim = user_data; + ofono_sim_iccid_event_cb_t cb = item->notify; + + cb(sim->iccid, item->notify_data); +} + +static inline void iccid_watches_notify(struct ofono_sim *sim) +{ + g_slist_foreach(sim->iccid_watches->items, iccid_watch_cb, sim); +} + +unsigned int ofono_sim_add_iccid_watch(struct ofono_sim *sim, + ofono_sim_iccid_event_cb_t cb, void *data, + ofono_destroy_func destroy) +{ + unsigned int watch_id; + + DBG("%p", sim); + if (sim == NULL) + return 0; + + watch_id = add_watch_item(sim->iccid_watches, cb, data, destroy); + + if (sim->iccid) + cb(sim->iccid, data); + + return watch_id; +} + +void ofono_sim_remove_iccid_watch(struct ofono_sim *sim, unsigned int id) +{ + __ofono_watchlist_remove_item(sim->iccid_watches, id); +} + +static void imsi_watch_cb(gpointer data, gpointer user_data) +{ + struct ofono_watchlist_item *item = data; + struct ofono_sim *sim = user_data; + ofono_sim_imsi_event_cb_t cb = item->notify; + + cb(sim->imsi, item->notify_data); +} + +static inline void imsi_watches_notify(struct ofono_sim *sim) +{ + g_slist_foreach(sim->imsi_watches->items, imsi_watch_cb, sim); +} + +unsigned int ofono_sim_add_imsi_watch(struct ofono_sim *sim, + ofono_sim_imsi_event_cb_t cb, void *data, + ofono_destroy_func destroy) +{ + unsigned int watch_id; + + DBG("%p", sim); + if (sim == NULL) + return 0; + + watch_id = add_watch_item(sim->imsi_watches, cb, data, destroy); + + if (sim->imsi) + cb(sim->imsi, data); + + return watch_id; +} + +void ofono_sim_remove_imsi_watch(struct ofono_sim *sim, unsigned int id) +{ + __ofono_watchlist_remove_item(sim->imsi_watches, id); +} + static DBusMessage *sim_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -1742,6 +1832,7 @@ static void sim_imsi_obtained(struct ofono_sim *sim, const char *imsi) DBUS_TYPE_STRING, &str); } + imsi_watches_notify(sim); sim_set_ready(sim); } @@ -2298,6 +2389,8 @@ static void sim_iccid_read_cb(int ok, int length, int record, "CardIdentifier", DBUS_TYPE_STRING, &sim->iccid); + + iccid_watches_notify(sim); } static void sim_iccid_changed(int id, void *userdata) @@ -3260,6 +3353,11 @@ static void sim_unregister(struct ofono_atom *atom) __ofono_modem_remove_atom_watch(modem, sim->hfp_watch); + __ofono_watchlist_free(sim->iccid_watches); + sim->iccid_watches = NULL; + __ofono_watchlist_free(sim->imsi_watches); + sim->imsi_watches = NULL; + __ofono_watchlist_free(sim->state_watches); sim->state_watches = NULL; __ofono_watchlist_free(sim->spn_watches); @@ -3367,6 +3465,8 @@ void ofono_sim_register(struct ofono_sim *sim) } ofono_modem_add_interface(modem, OFONO_SIM_MANAGER_INTERFACE); + sim->iccid_watches = __ofono_watchlist_new(g_free); + sim->imsi_watches = __ofono_watchlist_new(g_free); sim->state_watches = __ofono_watchlist_new(g_free); sim->spn_watches = __ofono_watchlist_new(g_free); sim->simfs = sim_fs_new(sim, sim->driver);