mirror of git://git.sysmocom.de/ofono
1203 lines
30 KiB
C
1203 lines
30 KiB
C
/*
|
|
*
|
|
* oFono - Open Source Telephony
|
|
*
|
|
* Copyright (C) 2008-2010 Intel Corporation. All rights reserved.
|
|
*
|
|
* 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 <config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#include <glib.h>
|
|
#include <gdbus.h>
|
|
|
|
#include "ofono.h"
|
|
|
|
#include "common.h"
|
|
|
|
#define CALL_BARRING_FLAG_CACHED 0x1
|
|
#define NUM_OF_BARRINGS 5
|
|
|
|
static GSList *g_drivers = NULL;
|
|
|
|
static void cb_ss_query_next_lock(struct ofono_call_barring *cb);
|
|
static void get_query_next_lock(struct ofono_call_barring *cb);
|
|
static void set_query_next_lock(struct ofono_call_barring *cb);
|
|
|
|
struct ofono_call_barring {
|
|
int flags;
|
|
DBusMessage *pending;
|
|
int cur_locks[NUM_OF_BARRINGS];
|
|
int new_locks[NUM_OF_BARRINGS];
|
|
int query_start;
|
|
int query_end;
|
|
int query_next;
|
|
int ss_req_type;
|
|
int ss_req_cls;
|
|
int ss_req_lock;
|
|
struct ofono_ssn *ssn;
|
|
struct ofono_ussd *ussd;
|
|
unsigned int incoming_bar_watch;
|
|
unsigned int outgoing_bar_watch;
|
|
unsigned int ssn_watch;
|
|
unsigned int ussd_watch;
|
|
const struct ofono_call_barring_driver *driver;
|
|
void *driver_data;
|
|
struct ofono_atom *atom;
|
|
};
|
|
|
|
struct call_barring_lock {
|
|
const char *name;
|
|
const char *value;
|
|
const char *fac;
|
|
};
|
|
|
|
static struct call_barring_lock cb_locks[] = {
|
|
{ "AllOutgoing", "all", "AO" },
|
|
{ "InternationalOutgoing", "international", "OI" },
|
|
{ "InternationalOutgoingExceptHome", "internationalnothome", "OX" },
|
|
{ "AllIncoming", "always", "AI" },
|
|
{ "IncomingWhenRoaming", "whenroaming", "IR" },
|
|
{ "AllBarringServices", NULL, "AB" },
|
|
{ "AllOutgoingServices", NULL, "AG" },
|
|
{ "AllIncomingServices", NULL, "AC" },
|
|
{ NULL, NULL, NULL },
|
|
};
|
|
|
|
/* These are inclusive */
|
|
#define CB_OUTGOING_START 0
|
|
#define CB_OUTGOING_END 2
|
|
#define CB_INCOMING_START 3
|
|
#define CB_INCOMING_END 4
|
|
#define CB_ALL_START 0
|
|
#define CB_ALL_END 4
|
|
#define CB_ALL_OUTGOING 6
|
|
#define CB_ALL_INCOMING 7
|
|
|
|
static inline void emit_barring_changed(struct ofono_call_barring *cb,
|
|
int start, int end,
|
|
const char *type, int cls)
|
|
{
|
|
DBusConnection *conn = ofono_dbus_get_connection();
|
|
const char *path = __ofono_atom_get_path(cb->atom);
|
|
char property_name[64];
|
|
const char *value;
|
|
int i;
|
|
int j;
|
|
|
|
for (i = start; i <= end; i++)
|
|
if (cb->cur_locks[i] & cls)
|
|
break;
|
|
|
|
for (j = start; j <= end; j++)
|
|
if (cb->new_locks[j] & cls)
|
|
break;
|
|
|
|
if (i == j)
|
|
return;
|
|
|
|
if (j > end)
|
|
value = "disabled";
|
|
else
|
|
value = cb_locks[j].value;
|
|
|
|
snprintf(property_name, sizeof(property_name), "%s%s",
|
|
bearer_class_to_string(cls), type);
|
|
|
|
ofono_dbus_signal_property_changed(conn, path,
|
|
OFONO_CALL_BARRING_INTERFACE,
|
|
property_name, DBUS_TYPE_STRING,
|
|
&value);
|
|
}
|
|
|
|
static void update_barrings(struct ofono_call_barring *cb, int mask)
|
|
{
|
|
int cls;
|
|
int i;
|
|
|
|
/* We're only interested in emitting signals for Voice, Fax & Data */
|
|
for (cls = 1; cls <= BEARER_CLASS_PAD; cls = cls << 1) {
|
|
if ((cls & mask) == 0)
|
|
continue;
|
|
|
|
emit_barring_changed(cb, cb->query_start, CB_OUTGOING_END,
|
|
"Outgoing", cls);
|
|
emit_barring_changed(cb, CB_INCOMING_START, cb->query_end,
|
|
"Incoming", cls);
|
|
}
|
|
|
|
for (i = cb->query_start; i <= cb->query_end; i++)
|
|
cb->cur_locks[i] = cb->new_locks[i];
|
|
}
|
|
|
|
static void cb_ss_property_append(struct ofono_call_barring *cb,
|
|
DBusMessageIter *dict, int lock,
|
|
int mask)
|
|
{
|
|
int i;
|
|
char property_name[64];
|
|
const char *strvalue;
|
|
|
|
for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) {
|
|
if (!(mask & i))
|
|
continue;
|
|
|
|
strvalue = (cb->new_locks[lock] & i) ? "enabled" : "disabled";
|
|
|
|
snprintf(property_name, sizeof(property_name), "%s%s",
|
|
bearer_class_to_string(i),
|
|
cb_locks[lock].name);
|
|
|
|
ofono_dbus_dict_append(dict, property_name, DBUS_TYPE_STRING,
|
|
&strvalue);
|
|
}
|
|
}
|
|
|
|
static void cb_set_query_bounds(struct ofono_call_barring *cb,
|
|
const char *fac, gboolean fac_only)
|
|
{
|
|
int i;
|
|
|
|
if (!strcmp("AB", fac)) {
|
|
cb->query_start = CB_ALL_START;
|
|
cb->query_end = CB_ALL_END;
|
|
cb->query_next = CB_ALL_START;
|
|
return;
|
|
}
|
|
|
|
if (!strcmp("AG", fac))
|
|
goto outgoing;
|
|
|
|
if (!strcmp("AC", fac))
|
|
goto incoming;
|
|
|
|
for (i = 0; cb_locks[i].name; i++) {
|
|
if (strcmp(cb_locks[i].fac, fac))
|
|
continue;
|
|
|
|
if (fac_only) {
|
|
cb->query_start = i;
|
|
cb->query_end = i;
|
|
cb->query_next = i;
|
|
return;
|
|
}
|
|
|
|
if ((i >= CB_OUTGOING_START) &&
|
|
(i <= CB_OUTGOING_END))
|
|
goto outgoing;
|
|
else if ((i >= CB_INCOMING_START) &&
|
|
(i <= CB_INCOMING_END))
|
|
goto incoming;
|
|
}
|
|
|
|
ofono_error("Unable to set query boundaries for %s", fac);
|
|
return;
|
|
|
|
outgoing:
|
|
cb->query_start = CB_OUTGOING_START;
|
|
cb->query_end = CB_OUTGOING_END;
|
|
cb->query_next = CB_OUTGOING_START;
|
|
return;
|
|
|
|
incoming:
|
|
cb->query_start = CB_INCOMING_START;
|
|
cb->query_end = CB_INCOMING_END;
|
|
cb->query_next = CB_INCOMING_START;
|
|
return;
|
|
}
|
|
|
|
static void generate_ss_query_reply(struct ofono_call_barring *cb)
|
|
{
|
|
const char *context = "CallBarring";
|
|
const char *sig = "(ssa{sv})";
|
|
const char *ss_type = ss_control_type_to_string(cb->ss_req_type);
|
|
const char *ss_fac = cb_locks[cb->ss_req_lock].name;
|
|
DBusMessageIter iter;
|
|
DBusMessageIter variant;
|
|
DBusMessageIter vstruct;
|
|
DBusMessageIter dict;
|
|
DBusMessage *reply;
|
|
int lock;
|
|
int start, end;
|
|
|
|
reply = dbus_message_new_method_return(cb->pending);
|
|
|
|
dbus_message_iter_init_append(reply, &iter);
|
|
|
|
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
|
|
|
|
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig,
|
|
&variant);
|
|
|
|
dbus_message_iter_open_container(&variant, DBUS_TYPE_STRUCT, NULL,
|
|
&vstruct);
|
|
|
|
dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &ss_type);
|
|
|
|
dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &ss_fac);
|
|
|
|
dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY,
|
|
OFONO_PROPERTIES_ARRAY_SIGNATURE,
|
|
&dict);
|
|
|
|
/* We report all affected locks only for the special case ones */
|
|
if (cb->ss_req_lock <= CB_ALL_END) {
|
|
start = cb->ss_req_lock;
|
|
end = cb->ss_req_lock;
|
|
} else {
|
|
start = cb->query_start;
|
|
end = cb->query_end;
|
|
}
|
|
|
|
for (lock = start; lock <= end; lock++)
|
|
cb_ss_property_append(cb, &dict, lock, cb->ss_req_cls);
|
|
|
|
dbus_message_iter_close_container(&vstruct, &dict);
|
|
|
|
dbus_message_iter_close_container(&variant, &vstruct);
|
|
|
|
dbus_message_iter_close_container(&iter, &variant);
|
|
|
|
__ofono_dbus_pending_reply(&cb->pending, reply);
|
|
}
|
|
|
|
static void cb_ss_query_next_lock_callback(const struct ofono_error *error,
|
|
int status, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
|
if (cb->ss_req_type != SS_CONTROL_TYPE_QUERY)
|
|
ofono_error("Enabling/disabling Call Barring via SS "
|
|
"successful, but query was not");
|
|
|
|
cb->flags &= ~CALL_BARRING_FLAG_CACHED;
|
|
|
|
__ofono_dbus_pending_reply(&cb->pending,
|
|
__ofono_error_failed(cb->pending));
|
|
return;
|
|
}
|
|
|
|
cb->new_locks[cb->query_next] = status;
|
|
|
|
if (cb->query_next < cb->query_end) {
|
|
cb->query_next += 1;
|
|
cb_ss_query_next_lock(cb);
|
|
return;
|
|
}
|
|
|
|
generate_ss_query_reply(cb);
|
|
update_barrings(cb, BEARER_CLASS_VOICE);
|
|
}
|
|
|
|
static void cb_ss_query_next_lock(struct ofono_call_barring *cb)
|
|
{
|
|
int cls;
|
|
|
|
cls = cb->ss_req_cls | BEARER_CLASS_DEFAULT;
|
|
|
|
cb->driver->query(cb, cb_locks[cb->query_next].fac, cls,
|
|
cb_ss_query_next_lock_callback, cb);
|
|
}
|
|
|
|
static void cb_ss_set_lock_callback(const struct ofono_error *error,
|
|
void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
|
DBG("Enabling/disabling Call Barring via SS failed");
|
|
__ofono_dbus_pending_reply(&cb->pending,
|
|
__ofono_error_failed(cb->pending));
|
|
return;
|
|
}
|
|
|
|
/* Assume we have query always */
|
|
cb_ss_query_next_lock(cb);
|
|
}
|
|
|
|
static const char *cb_ss_service_to_fac(const char *svc)
|
|
{
|
|
if (!strcmp(svc, "33"))
|
|
return "AO";
|
|
else if (!strcmp(svc, "331"))
|
|
return "OI";
|
|
else if (!strcmp(svc, "332"))
|
|
return "OX";
|
|
else if (!strcmp(svc, "35"))
|
|
return "AI";
|
|
else if (!strcmp(svc, "351"))
|
|
return "IR";
|
|
else if (!strcmp(svc, "330"))
|
|
return "AB";
|
|
else if (!strcmp(svc, "333"))
|
|
return "AG";
|
|
else if (!strcmp(svc, "353"))
|
|
return "AC";
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean cb_ss_control(int type, const char *sc,
|
|
const char *sia, const char *sib,
|
|
const char *sic, const char *dn,
|
|
DBusMessage *msg, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
DBusConnection *conn = ofono_dbus_get_connection();
|
|
int cls = BEARER_CLASS_DEFAULT;
|
|
const char *fac;
|
|
DBusMessage *reply;
|
|
void *operation = NULL;
|
|
int i;
|
|
|
|
if (cb->pending) {
|
|
reply = __ofono_error_busy(msg);
|
|
g_dbus_send_message(conn, reply);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DBG("Received call barring ss control request");
|
|
|
|
DBG("type: %d, sc: %s, sia: %s, sib: %s, sic: %s, dn: %s",
|
|
type, sc, sia, sib, sic, dn);
|
|
|
|
fac = cb_ss_service_to_fac(sc);
|
|
if (!fac)
|
|
return FALSE;
|
|
|
|
cb_set_query_bounds(cb, fac, type == SS_CONTROL_TYPE_QUERY);
|
|
|
|
i = 0;
|
|
while (cb_locks[i].name && strcmp(cb_locks[i].fac, fac))
|
|
i++;
|
|
|
|
cb->ss_req_lock = i;
|
|
|
|
if (strlen(sic) > 0)
|
|
goto bad_format;
|
|
|
|
if (strlen(dn) > 0)
|
|
goto bad_format;
|
|
|
|
if (type != SS_CONTROL_TYPE_QUERY && !is_valid_pin(sia, PIN_TYPE_NET))
|
|
goto bad_format;
|
|
|
|
switch (type) {
|
|
case SS_CONTROL_TYPE_ACTIVATION:
|
|
case SS_CONTROL_TYPE_DEACTIVATION:
|
|
case SS_CONTROL_TYPE_REGISTRATION:
|
|
case SS_CONTROL_TYPE_ERASURE:
|
|
operation = cb->driver->set;
|
|
break;
|
|
case SS_CONTROL_TYPE_QUERY:
|
|
operation = cb->driver->query;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!operation) {
|
|
reply = __ofono_error_not_implemented(msg);
|
|
g_dbus_send_message(conn, reply);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* According to 27.007, AG, AC and AB only work with mode = 0
|
|
* We support query by querying all relevant types, since we must
|
|
* do this for the deactivation case anyway
|
|
*/
|
|
if ((!strcmp(fac, "AG") || !strcmp(fac, "AC") || !strcmp(fac, "AB")) &&
|
|
(type == SS_CONTROL_TYPE_ACTIVATION ||
|
|
type == SS_CONTROL_TYPE_REGISTRATION))
|
|
goto bad_format;
|
|
|
|
if (strlen(sib) > 0) {
|
|
long service_code;
|
|
char *end;
|
|
|
|
service_code = strtoul(sib, &end, 10);
|
|
|
|
if (end == sib || *end != '\0')
|
|
goto bad_format;
|
|
|
|
cls = mmi_service_code_to_bearer_class(service_code);
|
|
|
|
if (cls == 0)
|
|
goto bad_format;
|
|
}
|
|
|
|
cb->ss_req_cls = cls;
|
|
cb->pending = dbus_message_ref(msg);
|
|
|
|
switch (type) {
|
|
case SS_CONTROL_TYPE_ACTIVATION:
|
|
case SS_CONTROL_TYPE_REGISTRATION:
|
|
cb->ss_req_type = SS_CONTROL_TYPE_ACTIVATION;
|
|
cb->driver->set(cb, fac, 1, sia, cls,
|
|
cb_ss_set_lock_callback, cb);
|
|
break;
|
|
case SS_CONTROL_TYPE_ERASURE:
|
|
case SS_CONTROL_TYPE_DEACTIVATION:
|
|
cb->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION;
|
|
cb->driver->set(cb, fac, 0, sia, cls,
|
|
cb_ss_set_lock_callback, cb);
|
|
break;
|
|
case SS_CONTROL_TYPE_QUERY:
|
|
cb->ss_req_type = SS_CONTROL_TYPE_QUERY;
|
|
cb_ss_query_next_lock(cb);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
bad_format:
|
|
reply = __ofono_error_invalid_format(msg);
|
|
g_dbus_send_message(conn, reply);
|
|
return TRUE;
|
|
}
|
|
|
|
static void cb_set_passwd_callback(const struct ofono_error *error, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
DBusMessage *reply;
|
|
|
|
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
|
reply = dbus_message_new_method_return(cb->pending);
|
|
else {
|
|
reply = __ofono_error_failed(cb->pending);
|
|
DBG("Changing Call Barring password via SS failed");
|
|
}
|
|
|
|
__ofono_dbus_pending_reply(&cb->pending, reply);
|
|
}
|
|
|
|
static gboolean cb_ss_passwd(const char *sc,
|
|
const char *old, const char *new,
|
|
DBusMessage *msg, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
DBusConnection *conn = ofono_dbus_get_connection();
|
|
DBusMessage *reply;
|
|
const char *fac;
|
|
|
|
if (cb->pending) {
|
|
reply = __ofono_error_busy(msg);
|
|
g_dbus_send_message(conn, reply);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DBG("Received call barring ss password change request");
|
|
|
|
DBG("sc: %s", sc);
|
|
|
|
if (!strcmp(sc, ""))
|
|
fac = "AB";
|
|
else
|
|
fac = cb_ss_service_to_fac(sc);
|
|
|
|
if (!fac)
|
|
return FALSE;
|
|
|
|
if (!is_valid_pin(old, PIN_TYPE_NET) || !is_valid_pin(new, PIN_TYPE_NET))
|
|
goto bad_format;
|
|
|
|
cb->pending = dbus_message_ref(msg);
|
|
cb->driver->set_passwd(cb, fac, old, new, cb_set_passwd_callback, cb);
|
|
|
|
return TRUE;
|
|
bad_format:
|
|
reply = __ofono_error_invalid_format(msg);
|
|
g_dbus_send_message(conn, reply);
|
|
return TRUE;
|
|
}
|
|
|
|
static void cb_register_ss_controls(struct ofono_call_barring *cb)
|
|
{
|
|
__ofono_ussd_ssc_register(cb->ussd, "33", cb_ss_control, cb, NULL);
|
|
__ofono_ussd_ssc_register(cb->ussd, "331", cb_ss_control, cb, NULL);
|
|
__ofono_ussd_ssc_register(cb->ussd, "332", cb_ss_control, cb, NULL);
|
|
__ofono_ussd_ssc_register(cb->ussd, "35", cb_ss_control, cb, NULL);
|
|
__ofono_ussd_ssc_register(cb->ussd, "351", cb_ss_control, cb, NULL);
|
|
__ofono_ussd_ssc_register(cb->ussd, "330", cb_ss_control, cb, NULL);
|
|
__ofono_ussd_ssc_register(cb->ussd, "333", cb_ss_control, cb, NULL);
|
|
__ofono_ussd_ssc_register(cb->ussd, "353", cb_ss_control, cb, NULL);
|
|
|
|
__ofono_ussd_passwd_register(cb->ussd, "", cb_ss_passwd, cb, NULL);
|
|
__ofono_ussd_passwd_register(cb->ussd, "33", cb_ss_passwd, cb, NULL);
|
|
__ofono_ussd_passwd_register(cb->ussd, "331", cb_ss_passwd, cb, NULL);
|
|
__ofono_ussd_passwd_register(cb->ussd, "332", cb_ss_passwd, cb, NULL);
|
|
__ofono_ussd_passwd_register(cb->ussd, "35", cb_ss_passwd, cb, NULL);
|
|
__ofono_ussd_passwd_register(cb->ussd, "351", cb_ss_passwd, cb, NULL);
|
|
__ofono_ussd_passwd_register(cb->ussd, "330", cb_ss_passwd, cb, NULL);
|
|
__ofono_ussd_passwd_register(cb->ussd, "333", cb_ss_passwd, cb, NULL);
|
|
__ofono_ussd_passwd_register(cb->ussd, "353", cb_ss_passwd, cb, NULL);
|
|
}
|
|
|
|
static void cb_unregister_ss_controls(struct ofono_call_barring *cb)
|
|
{
|
|
__ofono_ussd_ssc_unregister(cb->ussd, "33");
|
|
__ofono_ussd_ssc_unregister(cb->ussd, "331");
|
|
__ofono_ussd_ssc_unregister(cb->ussd, "332");
|
|
__ofono_ussd_ssc_unregister(cb->ussd, "35");
|
|
__ofono_ussd_ssc_unregister(cb->ussd, "351");
|
|
__ofono_ussd_ssc_unregister(cb->ussd, "330");
|
|
__ofono_ussd_ssc_unregister(cb->ussd, "333");
|
|
__ofono_ussd_ssc_unregister(cb->ussd, "353");
|
|
|
|
__ofono_ussd_passwd_unregister(cb->ussd, "");
|
|
__ofono_ussd_passwd_unregister(cb->ussd, "33");
|
|
__ofono_ussd_passwd_unregister(cb->ussd, "331");
|
|
__ofono_ussd_passwd_unregister(cb->ussd, "332");
|
|
__ofono_ussd_passwd_unregister(cb->ussd, "35");
|
|
__ofono_ussd_passwd_unregister(cb->ussd, "351");
|
|
__ofono_ussd_passwd_unregister(cb->ussd, "330");
|
|
__ofono_ussd_passwd_unregister(cb->ussd, "333");
|
|
__ofono_ussd_passwd_unregister(cb->ussd, "353");
|
|
}
|
|
|
|
static inline void cb_append_property(struct ofono_call_barring *cb,
|
|
DBusMessageIter *dict, int start,
|
|
int end, int cls, const char *property)
|
|
{
|
|
char property_name[64];
|
|
const char *value = "disabled";
|
|
int i;
|
|
|
|
for (i = start; i <= end; i++)
|
|
if (cb->new_locks[i] & cls)
|
|
break;
|
|
|
|
if (i <= end)
|
|
value = cb_locks[i].value;
|
|
|
|
snprintf(property_name, sizeof(property_name), "%s%s",
|
|
bearer_class_to_string(cls), property);
|
|
|
|
ofono_dbus_dict_append(dict, property_name, DBUS_TYPE_STRING,
|
|
&value);
|
|
}
|
|
|
|
static void cb_get_properties_reply(struct ofono_call_barring *cb, int mask)
|
|
{
|
|
DBusMessage *reply;
|
|
DBusMessageIter iter, dict;
|
|
int j;
|
|
|
|
if (!(cb->flags & CALL_BARRING_FLAG_CACHED))
|
|
ofono_error("Generating a get_properties reply with no cache");
|
|
|
|
reply = dbus_message_new_method_return(cb->pending);
|
|
if (!reply)
|
|
return;
|
|
|
|
dbus_message_iter_init_append(reply, &iter);
|
|
|
|
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
|
OFONO_PROPERTIES_ARRAY_SIGNATURE,
|
|
&dict);
|
|
|
|
for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) {
|
|
if ((j & mask) == 0)
|
|
continue;
|
|
|
|
cb_append_property(cb, &dict, CB_OUTGOING_START,
|
|
CB_OUTGOING_END, j, "Outgoing");
|
|
cb_append_property(cb, &dict, CB_INCOMING_START,
|
|
CB_INCOMING_END, j, "Incoming");
|
|
}
|
|
|
|
dbus_message_iter_close_container(&iter, &dict);
|
|
|
|
__ofono_dbus_pending_reply(&cb->pending, reply);
|
|
}
|
|
|
|
static void get_query_lock_callback(const struct ofono_error *error,
|
|
int status, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
|
|
if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
|
|
cb->new_locks[cb->query_next] = status;
|
|
|
|
if (cb->query_next == CB_ALL_END)
|
|
cb->flags |= CALL_BARRING_FLAG_CACHED;
|
|
}
|
|
|
|
if (cb->query_next < CB_ALL_END) {
|
|
cb->query_next = cb->query_next + 1;
|
|
get_query_next_lock(cb);
|
|
return;
|
|
}
|
|
|
|
cb_get_properties_reply(cb, BEARER_CLASS_VOICE);
|
|
update_barrings(cb, BEARER_CLASS_VOICE);
|
|
}
|
|
|
|
static void get_query_next_lock(struct ofono_call_barring *cb)
|
|
{
|
|
cb->driver->query(cb, cb_locks[cb->query_next].fac,
|
|
BEARER_CLASS_DEFAULT, get_query_lock_callback, cb);
|
|
}
|
|
|
|
static DBusMessage *cb_get_properties(DBusConnection *conn, DBusMessage *msg,
|
|
void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
|
|
if (cb->pending)
|
|
return __ofono_error_busy(msg);
|
|
|
|
if (!cb->driver->query)
|
|
return __ofono_error_not_implemented(msg);
|
|
|
|
cb->pending = dbus_message_ref(msg);
|
|
|
|
if (cb->flags & CALL_BARRING_FLAG_CACHED)
|
|
cb_get_properties_reply(cb, BEARER_CLASS_VOICE);
|
|
else {
|
|
cb->query_next = CB_ALL_START;
|
|
get_query_next_lock(cb);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void set_query_lock_callback(const struct ofono_error *error,
|
|
int status, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
|
ofono_error("Disabling all barring successful, "
|
|
"but query was not");
|
|
|
|
cb->flags &= ~CALL_BARRING_FLAG_CACHED;
|
|
|
|
__ofono_dbus_pending_reply(&cb->pending,
|
|
__ofono_error_failed(cb->pending));
|
|
return;
|
|
}
|
|
|
|
cb->new_locks[cb->query_next] = status;
|
|
|
|
if (cb->query_next < cb->query_end) {
|
|
cb->query_next += 1;
|
|
set_query_next_lock(cb);
|
|
return;
|
|
}
|
|
|
|
__ofono_dbus_pending_reply(&cb->pending,
|
|
dbus_message_new_method_return(cb->pending));
|
|
update_barrings(cb, BEARER_CLASS_VOICE);
|
|
}
|
|
|
|
static void set_query_next_lock(struct ofono_call_barring *cb)
|
|
{
|
|
cb->driver->query(cb, cb_locks[cb->query_next].fac,
|
|
BEARER_CLASS_DEFAULT, set_query_lock_callback, cb);
|
|
}
|
|
|
|
static void set_lock_callback(const struct ofono_error *error, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
|
DBG("Enabling/disabling a lock failed");
|
|
__ofono_dbus_pending_reply(&cb->pending,
|
|
__ofono_error_failed(cb->pending));
|
|
return;
|
|
}
|
|
|
|
/* If we successfully set the value, we must query it back
|
|
* Call Barring is a special case, since according to 22.088 2.2.1:
|
|
* "The PLMN will ensure that only one of the barring programs is
|
|
* active per basic service group. The activation of one specific
|
|
* barring program will override an already active one (i.e. the
|
|
* old one will be permanently deactivated)."
|
|
* So we actually query all outgoing / incoming barrings depending
|
|
* on what kind we set.
|
|
*/
|
|
set_query_next_lock(cb);
|
|
}
|
|
|
|
static gboolean cb_lock_property_lookup(const char *property, const char *value,
|
|
int mask, int *out_which, int *out_cls,
|
|
int *out_mode)
|
|
{
|
|
int i, j;
|
|
const char *prefix;
|
|
size_t len;
|
|
int start, end;
|
|
|
|
for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) {
|
|
if ((i & mask) == 0)
|
|
continue;
|
|
|
|
prefix = bearer_class_to_string(i);
|
|
len = strlen(prefix);
|
|
|
|
if (!strncmp(property, prefix, len))
|
|
break;
|
|
}
|
|
|
|
if (i > BEARER_CLASS_PAD)
|
|
return FALSE;
|
|
|
|
property += len;
|
|
|
|
if (!strcmp(property, "Outgoing")) {
|
|
start = CB_OUTGOING_START;
|
|
end = CB_OUTGOING_END;
|
|
} else if (!strcmp(property, "Incoming")) {
|
|
start = CB_INCOMING_START;
|
|
end = CB_INCOMING_END;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
/* Gah, this is a special case. If we're setting a barring to
|
|
* disabled, then generate a disable all outgoing/incoming
|
|
* request for a particular basic service
|
|
*/
|
|
if (!strcmp(value, "disabled")) {
|
|
*out_mode = 0;
|
|
*out_cls = i;
|
|
|
|
if (!strcmp(property, "Outgoing"))
|
|
*out_which = CB_ALL_OUTGOING;
|
|
else
|
|
*out_which = CB_ALL_INCOMING;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
for (j = start; j <= end; j++) {
|
|
if (strcmp(value, cb_locks[j].value))
|
|
continue;
|
|
|
|
*out_mode = 1;
|
|
*out_cls = i;
|
|
*out_which = j;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static DBusMessage *cb_set_property(DBusConnection *conn, DBusMessage *msg,
|
|
void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
DBusMessageIter iter;
|
|
DBusMessageIter var;
|
|
const char *name, *passwd = "";
|
|
const char *value;
|
|
int lock;
|
|
int cls;
|
|
int mode;
|
|
|
|
if (cb->pending)
|
|
return __ofono_error_busy(msg);
|
|
|
|
if (!dbus_message_iter_init(msg, &iter))
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
dbus_message_iter_get_basic(&iter, &name);
|
|
|
|
dbus_message_iter_next(&iter);
|
|
|
|
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
dbus_message_iter_recurse(&iter, &var);
|
|
|
|
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
dbus_message_iter_get_basic(&var, &value);
|
|
|
|
if (!cb_lock_property_lookup(name, value, BEARER_CLASS_VOICE,
|
|
&lock, &cls, &mode))
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
if (dbus_message_iter_next(&iter)) {
|
|
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
dbus_message_iter_get_basic(&iter, &passwd);
|
|
if (!is_valid_pin(passwd, PIN_TYPE_NET))
|
|
return __ofono_error_invalid_format(msg);
|
|
}
|
|
|
|
if (!cb->driver->set)
|
|
return __ofono_error_not_implemented(msg);
|
|
|
|
cb_set_query_bounds(cb, cb_locks[lock].fac, FALSE);
|
|
|
|
cb->pending = dbus_message_ref(msg);
|
|
cb->driver->set(cb, cb_locks[lock].fac, mode, passwd, cls,
|
|
set_lock_callback, cb);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void disable_all_callback(const struct ofono_error *error, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
|
DBG("Disabling all barring failed");
|
|
__ofono_dbus_pending_reply(&cb->pending,
|
|
__ofono_error_failed(cb->pending));
|
|
return;
|
|
}
|
|
|
|
/* Assume if we have set, we have query */
|
|
set_query_next_lock(cb);
|
|
}
|
|
|
|
static DBusMessage *cb_disable_all(DBusConnection *conn, DBusMessage *msg,
|
|
void *data, const char *fac)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
const char *passwd;
|
|
|
|
if (!cb->driver->set)
|
|
return __ofono_error_not_implemented(msg);
|
|
|
|
if (cb->pending)
|
|
return __ofono_error_busy(msg);
|
|
|
|
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &passwd,
|
|
DBUS_TYPE_INVALID) == FALSE)
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
if (!is_valid_pin(passwd, PIN_TYPE_NET))
|
|
return __ofono_error_invalid_format(msg);
|
|
|
|
cb_set_query_bounds(cb, fac, FALSE);
|
|
|
|
cb->pending = dbus_message_ref(msg);
|
|
cb->driver->set(cb, fac, 0, passwd,
|
|
BEARER_CLASS_DEFAULT, disable_all_callback, cb);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static DBusMessage *cb_disable_ab(DBusConnection *conn, DBusMessage *msg,
|
|
void *data)
|
|
{
|
|
return cb_disable_all(conn, msg, data, "AB");
|
|
}
|
|
|
|
static DBusMessage *cb_disable_ac(DBusConnection *conn, DBusMessage *msg,
|
|
void *data)
|
|
{
|
|
return cb_disable_all(conn, msg, data, "AC");
|
|
}
|
|
|
|
static DBusMessage *cb_disable_ag(DBusConnection *conn, DBusMessage *msg,
|
|
void *data)
|
|
{
|
|
return cb_disable_all(conn, msg, data, "AG");
|
|
}
|
|
|
|
static DBusMessage *cb_set_passwd(DBusConnection *conn, DBusMessage *msg,
|
|
void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
const char *old_passwd;
|
|
const char *new_passwd;
|
|
|
|
if (!cb->driver->set_passwd)
|
|
return __ofono_error_not_implemented(msg);
|
|
|
|
if (cb->pending)
|
|
return __ofono_error_busy(msg);
|
|
|
|
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &old_passwd,
|
|
DBUS_TYPE_STRING, &new_passwd,
|
|
DBUS_TYPE_INVALID) == FALSE)
|
|
return __ofono_error_invalid_args(msg);
|
|
|
|
if (!is_valid_pin(old_passwd, PIN_TYPE_NET))
|
|
return __ofono_error_invalid_format(msg);
|
|
|
|
if (!is_valid_pin(new_passwd, PIN_TYPE_NET))
|
|
return __ofono_error_invalid_format(msg);
|
|
|
|
cb->pending = dbus_message_ref(msg);
|
|
cb->driver->set_passwd(cb, "AB", old_passwd, new_passwd,
|
|
cb_set_passwd_callback, cb);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GDBusMethodTable cb_methods[] = {
|
|
{ "GetProperties", "", "a{sv}", cb_get_properties,
|
|
G_DBUS_METHOD_FLAG_ASYNC },
|
|
{ "SetProperty", "svs", "", cb_set_property,
|
|
G_DBUS_METHOD_FLAG_ASYNC },
|
|
{ "DisableAll", "s", "", cb_disable_ab,
|
|
G_DBUS_METHOD_FLAG_ASYNC },
|
|
{ "DisableAllIncoming", "s", "", cb_disable_ac,
|
|
G_DBUS_METHOD_FLAG_ASYNC },
|
|
{ "DisableAllOutgoing", "s", "", cb_disable_ag,
|
|
G_DBUS_METHOD_FLAG_ASYNC },
|
|
{ "ChangePassword", "ss", "", cb_set_passwd,
|
|
G_DBUS_METHOD_FLAG_ASYNC },
|
|
{ }
|
|
};
|
|
|
|
static GDBusSignalTable cb_signals[] = {
|
|
{ "IncomingBarringInEffect", "" },
|
|
{ "OutgoingBarringInEffect", "" },
|
|
{ "PropertyChanged", "sv" },
|
|
{ }
|
|
};
|
|
|
|
static void call_barring_incoming_enabled_notify(int idx, void *userdata)
|
|
{
|
|
struct ofono_call_barring *cb = userdata;
|
|
DBusConnection *conn = ofono_dbus_get_connection();
|
|
const char *path = __ofono_atom_get_path(cb->atom);
|
|
|
|
g_dbus_emit_signal(conn, path, OFONO_CALL_BARRING_INTERFACE,
|
|
"IncomingBarringInEffect", DBUS_TYPE_INVALID);
|
|
}
|
|
|
|
static void call_barring_outgoing_enabled_notify(int idx, void *userdata)
|
|
{
|
|
struct ofono_call_barring *cb = userdata;
|
|
DBusConnection *conn = ofono_dbus_get_connection();
|
|
const char *path = __ofono_atom_get_path(cb->atom);
|
|
DBusMessage *signal;
|
|
|
|
signal = dbus_message_new_signal(path, OFONO_CALL_BARRING_INTERFACE,
|
|
"OutgoingBarringInEffect");
|
|
|
|
if (!signal) {
|
|
ofono_error("Unable to allocate new %s.OutgoingBarringInEffect"
|
|
" signal", OFONO_CALL_BARRING_INTERFACE);
|
|
return;
|
|
}
|
|
|
|
g_dbus_send_message(conn, signal);
|
|
}
|
|
|
|
int ofono_call_barring_driver_register(const struct ofono_call_barring_driver *d)
|
|
{
|
|
DBG("driver: %p, name: %s", d, d->name);
|
|
|
|
if (d->probe == NULL)
|
|
return -EINVAL;
|
|
|
|
g_drivers = g_slist_prepend(g_drivers, (void *)d);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ofono_call_barring_driver_unregister(const struct ofono_call_barring_driver *d)
|
|
{
|
|
DBG("driver: %p, name: %s", d, d->name);
|
|
|
|
g_drivers = g_slist_remove(g_drivers, (void *)d);
|
|
}
|
|
|
|
static void call_barring_unregister(struct ofono_atom *atom)
|
|
{
|
|
struct ofono_call_barring *cb = __ofono_atom_get_data(atom);
|
|
const char *path = __ofono_atom_get_path(cb->atom);
|
|
DBusConnection *conn = ofono_dbus_get_connection();
|
|
struct ofono_modem *modem = __ofono_atom_get_modem(cb->atom);
|
|
|
|
ofono_modem_remove_interface(modem, OFONO_CALL_BARRING_INTERFACE);
|
|
g_dbus_unregister_interface(conn, path, OFONO_CALL_BARRING_INTERFACE);
|
|
|
|
if (cb->ussd)
|
|
cb_unregister_ss_controls(cb);
|
|
|
|
if (cb->incoming_bar_watch)
|
|
__ofono_ssn_mo_watch_remove(cb->ssn, cb->incoming_bar_watch);
|
|
if (cb->outgoing_bar_watch)
|
|
__ofono_ssn_mt_watch_remove(cb->ssn, cb->outgoing_bar_watch);
|
|
|
|
if (cb->ssn_watch)
|
|
__ofono_modem_remove_atom_watch(modem, cb->ssn_watch);
|
|
|
|
if (cb->ussd_watch)
|
|
__ofono_modem_remove_atom_watch(modem, cb->ussd_watch);
|
|
}
|
|
|
|
static void call_barring_remove(struct ofono_atom *atom)
|
|
{
|
|
struct ofono_call_barring *cb = __ofono_atom_get_data(atom);
|
|
|
|
DBG("atom: %p", atom);
|
|
|
|
if (cb == NULL)
|
|
return;
|
|
|
|
if (cb->driver && cb->driver->remove)
|
|
cb->driver->remove(cb);
|
|
|
|
g_free(cb);
|
|
}
|
|
|
|
struct ofono_call_barring *ofono_call_barring_create(struct ofono_modem *modem,
|
|
unsigned int vendor,
|
|
const char *driver,
|
|
void *data)
|
|
{
|
|
struct ofono_call_barring *cb;
|
|
GSList *l;
|
|
|
|
if (driver == NULL)
|
|
return NULL;
|
|
|
|
cb = g_try_new0(struct ofono_call_barring, 1);
|
|
|
|
if (cb == NULL)
|
|
return NULL;
|
|
|
|
cb->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CALL_BARRING,
|
|
call_barring_remove, cb);
|
|
|
|
for (l = g_drivers; l; l = l->next) {
|
|
const struct ofono_call_barring_driver *drv = l->data;
|
|
|
|
if (g_strcmp0(drv->name, driver))
|
|
continue;
|
|
|
|
if (drv->probe(cb, vendor, data) < 0)
|
|
continue;
|
|
|
|
cb->driver = drv;
|
|
break;
|
|
}
|
|
|
|
return cb;
|
|
}
|
|
|
|
static void ssn_watch(struct ofono_atom *atom,
|
|
enum ofono_atom_watch_condition cond, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
|
|
if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
|
|
cb->ssn = NULL;
|
|
cb->incoming_bar_watch = 0;
|
|
cb->outgoing_bar_watch = 0;
|
|
return;
|
|
}
|
|
|
|
cb->ssn = __ofono_atom_get_data(atom);
|
|
|
|
cb->incoming_bar_watch =
|
|
__ofono_ssn_mo_watch_add(cb->ssn, SS_MO_INCOMING_BARRING,
|
|
call_barring_incoming_enabled_notify, cb, NULL);
|
|
|
|
cb->outgoing_bar_watch =
|
|
__ofono_ssn_mo_watch_add(cb->ssn, SS_MO_OUTGOING_BARRING,
|
|
call_barring_outgoing_enabled_notify, cb, NULL);
|
|
}
|
|
|
|
static void ussd_watch(struct ofono_atom *atom,
|
|
enum ofono_atom_watch_condition cond, void *data)
|
|
{
|
|
struct ofono_call_barring *cb = data;
|
|
|
|
if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
|
|
cb->ussd = NULL;
|
|
return;
|
|
}
|
|
|
|
cb->ussd = __ofono_atom_get_data(atom);
|
|
cb_register_ss_controls(cb);
|
|
}
|
|
|
|
void ofono_call_barring_register(struct ofono_call_barring *cb)
|
|
{
|
|
DBusConnection *conn = ofono_dbus_get_connection();
|
|
const char *path = __ofono_atom_get_path(cb->atom);
|
|
struct ofono_modem *modem = __ofono_atom_get_modem(cb->atom);
|
|
struct ofono_atom *ssn_atom;
|
|
struct ofono_atom *ussd_atom;
|
|
|
|
if (!g_dbus_register_interface(conn, path,
|
|
OFONO_CALL_BARRING_INTERFACE,
|
|
cb_methods, cb_signals, NULL, cb,
|
|
NULL)) {
|
|
ofono_error("Could not create %s interface",
|
|
OFONO_CALL_BARRING_INTERFACE);
|
|
|
|
return;
|
|
}
|
|
|
|
ofono_modem_add_interface(modem, OFONO_CALL_BARRING_INTERFACE);
|
|
|
|
cb->ssn_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SSN,
|
|
ssn_watch, cb, NULL);
|
|
|
|
ssn_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_SSN);
|
|
|
|
if (ssn_atom && __ofono_atom_get_registered(ssn_atom))
|
|
ssn_watch(ssn_atom, OFONO_ATOM_WATCH_CONDITION_REGISTERED, cb);
|
|
|
|
cb->ussd_watch = __ofono_modem_add_atom_watch(modem,
|
|
OFONO_ATOM_TYPE_USSD,
|
|
ussd_watch, cb, NULL);
|
|
|
|
ussd_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_USSD);
|
|
|
|
if (ussd_atom && __ofono_atom_get_registered(ussd_atom))
|
|
ussd_watch(ussd_atom, OFONO_ATOM_WATCH_CONDITION_REGISTERED,
|
|
cb);
|
|
|
|
__ofono_atom_register(cb->atom, call_barring_unregister);
|
|
}
|
|
|
|
void ofono_call_barring_remove(struct ofono_call_barring *cb)
|
|
{
|
|
__ofono_atom_free(cb->atom);
|
|
}
|
|
|
|
void ofono_call_barring_set_data(struct ofono_call_barring *cb, void *data)
|
|
{
|
|
cb->driver_data = data;
|
|
}
|
|
|
|
void *ofono_call_barring_get_data(struct ofono_call_barring *cb)
|
|
{
|
|
return cb->driver_data;
|
|
}
|