1/* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2008-2010 Nokia Corporation 6 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> 7 * 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 */ 24 25#ifdef HAVE_CONFIG_H 26#include <config.h> 27#endif 28 29#include <stdlib.h> 30#include <stdio.h> 31#include <unistd.h> 32#include <fcntl.h> 33#include <stdint.h> 34#include <string.h> 35#include <glib.h> 36#include <dbus/dbus.h> 37#include <gdbus.h> 38 39#include "log.h" 40#include "telephony.h" 41 42/* SSC D-Bus definitions */ 43#define SSC_DBUS_NAME "com.nokia.phone.SSC" 44#define SSC_DBUS_IFACE "com.nokia.phone.SSC" 45#define SSC_DBUS_PATH "/com/nokia/phone/SSC" 46 47/* libcsnet D-Bus definitions */ 48#define CSD_CSNET_BUS_NAME "com.nokia.csd.CSNet" 49#define CSD_CSNET_PATH "/com/nokia/csd/csnet" 50#define CSD_CSNET_IFACE "com.nokia.csd.CSNet" 51#define CSD_CSNET_REGISTRATION "com.nokia.csd.CSNet.NetworkRegistration" 52#define CSD_CSNET_OPERATOR "com.nokia.csd.CSNet.NetworkOperator" 53#define CSD_CSNET_SIGNAL "com.nokia.csd.CSNet.SignalStrength" 54 55enum net_registration_status { 56 NETWORK_REG_STATUS_HOME, 57 NETWORK_REG_STATUS_ROAMING, 58 NETWORK_REG_STATUS_OFFLINE, 59 NETWORK_REG_STATUS_SEARCHING, 60 NETWORK_REG_STATUS_NO_SIM, 61 NETWORK_REG_STATUS_POWEROFF, 62 NETWORK_REG_STATUS_POWERSAFE, 63 NETWORK_REG_STATUS_NO_COVERAGE, 64 NETWORK_REG_STATUS_REJECTED, 65 NETWORK_REG_STATUS_UNKOWN 66}; 67 68/* Driver definitions */ 69#define TELEPHONY_MAEMO_PATH "/com/nokia/MaemoTelephony" 70#define TELEPHONY_MAEMO_INTERFACE "com.nokia.MaemoTelephony" 71 72#define CALLERID_BASE "/var/lib/bluetooth/maemo-callerid-" 73#define ALLOWED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-allowed" 74#define RESTRICTED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-restricted" 75#define NONE_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-none" 76 77static uint32_t callerid = 0; 78 79/* CSD CALL plugin D-Bus definitions */ 80#define CSD_CALL_BUS_NAME "com.nokia.csd.Call" 81#define CSD_CALL_INTERFACE "com.nokia.csd.Call" 82#define CSD_CALL_INSTANCE "com.nokia.csd.Call.Instance" 83#define CSD_CALL_CONFERENCE "com.nokia.csd.Call.Conference" 84#define CSD_CALL_PATH "/com/nokia/csd/call" 85#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference" 86 87/* Call status values as exported by the CSD CALL plugin */ 88#define CSD_CALL_STATUS_IDLE 0 89#define CSD_CALL_STATUS_CREATE 1 90#define CSD_CALL_STATUS_COMING 2 91#define CSD_CALL_STATUS_PROCEEDING 3 92#define CSD_CALL_STATUS_MO_ALERTING 4 93#define CSD_CALL_STATUS_MT_ALERTING 5 94#define CSD_CALL_STATUS_WAITING 6 95#define CSD_CALL_STATUS_ANSWERED 7 96#define CSD_CALL_STATUS_ACTIVE 8 97#define CSD_CALL_STATUS_MO_RELEASE 9 98#define CSD_CALL_STATUS_MT_RELEASE 10 99#define CSD_CALL_STATUS_HOLD_INITIATED 11 100#define CSD_CALL_STATUS_HOLD 12 101#define CSD_CALL_STATUS_RETRIEVE_INITIATED 13 102#define CSD_CALL_STATUS_RECONNECT_PENDING 14 103#define CSD_CALL_STATUS_TERMINATED 15 104#define CSD_CALL_STATUS_SWAP_INITIATED 16 105 106#define CALL_FLAG_NONE 0 107#define CALL_FLAG_PRESENTATION_ALLOWED 0x01 108#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02 109 110/* SIM Phonebook D-Bus definitions */ 111#define CSD_SIMPB_BUS_NAME "com.nokia.csd.SIM" 112#define CSD_SIMPB_INTERFACE "com.nokia.csd.SIM.Phonebook" 113#define CSD_SIMPB_PATH "/com/nokia/csd/sim/phonebook" 114 115#define CSD_SIMPB_TYPE_ADN "ADN" 116#define CSD_SIMPB_TYPE_FDN "FDN" 117#define CSD_SIMPB_TYPE_SDN "SDN" 118#define CSD_SIMPB_TYPE_VMBX "VMBX" 119#define CSD_SIMPB_TYPE_MBDN "MBDN" 120#define CSD_SIMPB_TYPE_EN "EN" 121#define CSD_SIMPB_TYPE_MSISDN "MSISDN" 122 123struct csd_call { 124 char *object_path; 125 int status; 126 gboolean originating; 127 gboolean emergency; 128 gboolean on_hold; 129 gboolean conference; 130 char *number; 131 gboolean setup; 132}; 133 134static struct { 135 char *operator_name; 136 uint8_t status; 137 int32_t signal_bars; 138} net = { 139 .operator_name = NULL, 140 .status = NETWORK_REG_STATUS_UNKOWN, 141 /* Init as 0 meaning inactive mode. In modem power off state 142 * can be be -1, but we treat all values as 0s regardless 143 * inactive or power off. */ 144 .signal_bars = 0, 145}; 146 147static int get_property(const char *iface, const char *prop); 148 149static DBusConnection *connection = NULL; 150 151static GSList *calls = NULL; 152 153/* Reference count for determining the call indicator status */ 154static GSList *active_calls = NULL; 155 156static char *msisdn = NULL; /* Subscriber number */ 157static char *vmbx = NULL; /* Voice mailbox number */ 158 159/* HAL battery namespace key values */ 160static int battchg_cur = -1; /* "battery.charge_level.current" */ 161static int battchg_last = -1; /* "battery.charge_level.last_full" */ 162static int battchg_design = -1; /* "battery.charge_level.design" */ 163 164static gboolean get_calls_active = FALSE; 165 166static gboolean events_enabled = FALSE; 167 168/* Supported set of call hold operations */ 169static const char *chld_str = "0,1,1x,2,2x,3,4"; 170 171/* Response and hold state 172 * -1 = none 173 * 0 = incoming call is put on hold in the AG 174 * 1 = held incoming call is accepted in the AG 175 * 2 = held incoming call is rejected in the AG 176 */ 177static int response_and_hold = -1; 178 179static char *last_dialed_number = NULL; 180 181/* Timer for tracking call creation requests */ 182static guint create_request_timer = 0; 183 184static struct indicator maemo_indicators[] = 185{ 186 { "battchg", "0-5", 5, TRUE }, 187 /* signal strength in terms of bars */ 188 { "signal", "0-5", 0, TRUE }, 189 { "service", "0,1", 0, TRUE }, 190 { "call", "0,1", 0, TRUE }, 191 { "callsetup", "0-3", 0, TRUE }, 192 { "callheld", "0-2", 0, FALSE }, 193 { "roam", "0,1", 0, TRUE }, 194 { NULL } 195}; 196 197static char *call_status_str[] = { 198 "IDLE", 199 "CREATE", 200 "COMING", 201 "PROCEEDING", 202 "MO_ALERTING", 203 "MT_ALERTING", 204 "WAITING", 205 "ANSWERED", 206 "ACTIVE", 207 "MO_RELEASE", 208 "MT_RELEASE", 209 "HOLD_INITIATED", 210 "HOLD", 211 "RETRIEVE_INITIATED", 212 "RECONNECT_PENDING", 213 "TERMINATED", 214 "SWAP_INITIATED", 215 "???" 216}; 217 218static struct csd_call *find_call(const char *path) 219{ 220 GSList *l; 221 222 for (l = calls; l != NULL; l = l->next) { 223 struct csd_call *call = l->data; 224 225 if (g_str_equal(call->object_path, path)) 226 return call; 227 } 228 229 return NULL; 230} 231 232static struct csd_call *find_non_held_call(void) 233{ 234 GSList *l; 235 236 for (l = calls; l != NULL; l = l->next) { 237 struct csd_call *call = l->data; 238 239 if (call->status == CSD_CALL_STATUS_IDLE) 240 continue; 241 242 if (call->status != CSD_CALL_STATUS_HOLD) 243 return call; 244 } 245 246 return NULL; 247} 248 249static struct csd_call *find_non_idle_call(void) 250{ 251 GSList *l; 252 253 for (l = calls; l != NULL; l = l->next) { 254 struct csd_call *call = l->data; 255 256 if (call->status != CSD_CALL_STATUS_IDLE) 257 return call; 258 } 259 260 return NULL; 261} 262 263static struct csd_call *find_call_with_status(int status) 264{ 265 GSList *l; 266 267 for (l = calls; l != NULL; l = l->next) { 268 struct csd_call *call = l->data; 269 270 if (call->status == status) 271 return call; 272 } 273 274 return NULL; 275} 276 277static int release_conference(void) 278{ 279 DBusMessage *msg; 280 281 DBG("telephony-maemo6: releasing conference call"); 282 283 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, 284 CSD_CALL_CONFERENCE_PATH, 285 CSD_CALL_INSTANCE, 286 "Release"); 287 if (!msg) { 288 error("Unable to allocate new D-Bus message"); 289 return -ENOMEM; 290 } 291 292 g_dbus_send_message(connection, msg); 293 294 return 0; 295} 296 297static int release_call(struct csd_call *call) 298{ 299 DBusMessage *msg; 300 301 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, 302 call->object_path, 303 CSD_CALL_INSTANCE, 304 "Release"); 305 if (!msg) { 306 error("Unable to allocate new D-Bus message"); 307 return -ENOMEM; 308 } 309 310 g_dbus_send_message(connection, msg); 311 312 return 0; 313} 314 315static int answer_call(struct csd_call *call) 316{ 317 DBusMessage *msg; 318 319 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, 320 call->object_path, 321 CSD_CALL_INSTANCE, 322 "Answer"); 323 if (!msg) { 324 error("Unable to allocate new D-Bus message"); 325 return -ENOMEM; 326 } 327 328 g_dbus_send_message(connection, msg); 329 330 return 0; 331} 332 333static int split_call(struct csd_call *call) 334{ 335 DBusMessage *msg; 336 337 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, 338 call->object_path, 339 CSD_CALL_INSTANCE, 340 "Split"); 341 if (!msg) { 342 error("Unable to allocate new D-Bus message"); 343 return -ENOMEM; 344 } 345 346 g_dbus_send_message(connection, msg); 347 348 return 0; 349} 350 351static int unhold_call(struct csd_call *call) 352{ 353 DBusMessage *msg; 354 355 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, 356 CSD_CALL_INTERFACE, 357 "Unhold"); 358 if (!msg) { 359 error("Unable to allocate new D-Bus message"); 360 return -ENOMEM; 361 } 362 363 g_dbus_send_message(connection, msg); 364 365 return 0; 366} 367 368static int hold_call(struct csd_call *call) 369{ 370 DBusMessage *msg; 371 372 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, 373 CSD_CALL_INTERFACE, 374 "Hold"); 375 if (!msg) { 376 error("Unable to allocate new D-Bus message"); 377 return -ENOMEM; 378 } 379 380 g_dbus_send_message(connection, msg); 381 382 return 0; 383} 384 385static int swap_calls(void) 386{ 387 DBusMessage *msg; 388 389 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, 390 CSD_CALL_INTERFACE, 391 "Swap"); 392 if (!msg) { 393 error("Unable to allocate new D-Bus message"); 394 return -ENOMEM; 395 } 396 397 g_dbus_send_message(connection, msg); 398 399 return 0; 400} 401 402static int create_conference(void) 403{ 404 DBusMessage *msg; 405 406 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, 407 CSD_CALL_INTERFACE, 408 "Conference"); 409 if (!msg) { 410 error("Unable to allocate new D-Bus message"); 411 return -ENOMEM; 412 } 413 414 g_dbus_send_message(connection, msg); 415 416 return 0; 417} 418 419static int call_transfer(void) 420{ 421 DBusMessage *msg; 422 423 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, 424 CSD_CALL_INTERFACE, 425 "Transfer"); 426 if (!msg) { 427 error("Unable to allocate new D-Bus message"); 428 return -ENOMEM; 429 } 430 431 g_dbus_send_message(connection, msg); 432 433 return 0; 434} 435 436static int number_type(const char *number) 437{ 438 if (number == NULL) 439 return NUMBER_TYPE_TELEPHONY; 440 441 if (number[0] == '+' || strncmp(number, "00", 2) == 0) 442 return NUMBER_TYPE_INTERNATIONAL; 443 444 return NUMBER_TYPE_TELEPHONY; 445} 446 447void telephony_device_connected(void *telephony_device) 448{ 449 struct csd_call *coming; 450 451 DBG("telephony-maemo6: device %p connected", telephony_device); 452 453 coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING); 454 if (coming) { 455 if (find_call_with_status(CSD_CALL_STATUS_ACTIVE)) 456 telephony_call_waiting_ind(coming->number, 457 number_type(coming->number)); 458 else 459 telephony_incoming_call_ind(coming->number, 460 number_type(coming->number)); 461 } 462} 463 464void telephony_device_disconnected(void *telephony_device) 465{ 466 DBG("telephony-maemo6: device %p disconnected", telephony_device); 467 events_enabled = FALSE; 468} 469 470void telephony_event_reporting_req(void *telephony_device, int ind) 471{ 472 events_enabled = ind == 1 ? TRUE : FALSE; 473 474 telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE); 475} 476 477void telephony_response_and_hold_req(void *telephony_device, int rh) 478{ 479 response_and_hold = rh; 480 481 telephony_response_and_hold_ind(response_and_hold); 482 483 telephony_response_and_hold_rsp(telephony_device, CME_ERROR_NONE); 484} 485 486void telephony_last_dialed_number_req(void *telephony_device) 487{ 488 DBG("telephony-maemo6: last dialed number request"); 489 490 if (last_dialed_number) 491 telephony_dial_number_req(telephony_device, 492 last_dialed_number); 493 else 494 telephony_last_dialed_number_rsp(telephony_device, 495 CME_ERROR_NOT_ALLOWED); 496} 497 498void telephony_terminate_call_req(void *telephony_device) 499{ 500 struct csd_call *call; 501 int err; 502 503 call = find_call_with_status(CSD_CALL_STATUS_ACTIVE); 504 if (!call) 505 call = find_non_idle_call(); 506 507 if (!call) { 508 error("No active call"); 509 telephony_terminate_call_rsp(telephony_device, 510 CME_ERROR_NOT_ALLOWED); 511 return; 512 } 513 514 if (call->conference) 515 err = release_conference(); 516 else 517 err = release_call(call); 518 519 if (err < 0) 520 telephony_terminate_call_rsp(telephony_device, 521 CME_ERROR_AG_FAILURE); 522 else 523 telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE); 524} 525 526void telephony_answer_call_req(void *telephony_device) 527{ 528 struct csd_call *call; 529 530 call = find_call_with_status(CSD_CALL_STATUS_COMING); 531 if (!call) 532 call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING); 533 534 if (!call) 535 call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING); 536 537 if (!call) 538 call = find_call_with_status(CSD_CALL_STATUS_WAITING); 539 540 if (!call) { 541 telephony_answer_call_rsp(telephony_device, 542 CME_ERROR_NOT_ALLOWED); 543 return; 544 } 545 546 if (answer_call(call) < 0) 547 telephony_answer_call_rsp(telephony_device, 548 CME_ERROR_AG_FAILURE); 549 else 550 telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE); 551} 552 553static int send_method_call(const char *dest, const char *path, 554 const char *interface, const char *method, 555 DBusPendingCallNotifyFunction cb, 556 void *user_data, int type, ...) 557{ 558 DBusMessage *msg; 559 DBusPendingCall *call; 560 va_list args; 561 562 msg = dbus_message_new_method_call(dest, path, interface, method); 563 if (!msg) { 564 error("Unable to allocate new D-Bus %s message", method); 565 return -ENOMEM; 566 } 567 568 va_start(args, type); 569 570 if (!dbus_message_append_args_valist(msg, type, args)) { 571 dbus_message_unref(msg); 572 va_end(args); 573 return -EIO; 574 } 575 576 va_end(args); 577 578 if (!cb) { 579 g_dbus_send_message(connection, msg); 580 return 0; 581 } 582 583 if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) { 584 error("Sending %s failed", method); 585 dbus_message_unref(msg); 586 return -EIO; 587 } 588 589 dbus_pending_call_set_notify(call, cb, user_data, NULL); 590 dbus_pending_call_unref(call); 591 dbus_message_unref(msg); 592 593 return 0; 594} 595 596static const char *memory_dial_lookup(int location) 597{ 598 if (location == 1) 599 return vmbx; 600 else 601 return NULL; 602} 603 604void telephony_dial_number_req(void *telephony_device, const char *number) 605{ 606 uint32_t flags = callerid; 607 int ret; 608 609 DBG("telephony-maemo6: dial request to %s", number); 610 611 if (strncmp(number, "*31#", 4) == 0) { 612 number += 4; 613 flags = CALL_FLAG_PRESENTATION_ALLOWED; 614 } else if (strncmp(number, "#31#", 4) == 0) { 615 number += 4; 616 flags = CALL_FLAG_PRESENTATION_RESTRICTED; 617 } else if (number[0] == '>') { 618 const char *location = &number[1]; 619 620 number = memory_dial_lookup(strtol(&number[1], NULL, 0)); 621 if (!number) { 622 error("No number at memory location %s", location); 623 telephony_dial_number_rsp(telephony_device, 624 CME_ERROR_INVALID_INDEX); 625 return; 626 } 627 } 628 629 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, 630 CSD_CALL_INTERFACE, "CreateWith", 631 NULL, NULL, 632 DBUS_TYPE_STRING, &number, 633 DBUS_TYPE_UINT32, &flags, 634 DBUS_TYPE_INVALID); 635 if (ret < 0) { 636 telephony_dial_number_rsp(telephony_device, 637 CME_ERROR_AG_FAILURE); 638 return; 639 } 640 641 telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE); 642} 643 644void telephony_transmit_dtmf_req(void *telephony_device, char tone) 645{ 646 int ret; 647 char buf[2] = { tone, '\0' }, *buf_ptr = buf; 648 649 DBG("telephony-maemo6: transmit dtmf: %s", buf); 650 651 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, 652 CSD_CALL_INTERFACE, "SendDTMF", 653 NULL, NULL, 654 DBUS_TYPE_STRING, &buf_ptr, 655 DBUS_TYPE_INVALID); 656 if (ret < 0) { 657 telephony_transmit_dtmf_rsp(telephony_device, 658 CME_ERROR_AG_FAILURE); 659 return; 660 } 661 662 telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE); 663} 664 665void telephony_subscriber_number_req(void *telephony_device) 666{ 667 DBG("telephony-maemo6: subscriber number request"); 668 if (msisdn) 669 telephony_subscriber_number_ind(msisdn, 670 number_type(msisdn), 671 SUBSCRIBER_SERVICE_VOICE); 672 telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE); 673} 674 675static int csd_status_to_hfp(struct csd_call *call) 676{ 677 switch (call->status) { 678 case CSD_CALL_STATUS_IDLE: 679 case CSD_CALL_STATUS_MO_RELEASE: 680 case CSD_CALL_STATUS_MT_RELEASE: 681 case CSD_CALL_STATUS_TERMINATED: 682 return -1; 683 case CSD_CALL_STATUS_CREATE: 684 return CALL_STATUS_DIALING; 685 case CSD_CALL_STATUS_WAITING: 686 return CALL_STATUS_WAITING; 687 case CSD_CALL_STATUS_PROCEEDING: 688 /* PROCEEDING can happen in outgoing/incoming */ 689 if (call->originating) 690 return CALL_STATUS_DIALING; 691 else 692 return CALL_STATUS_INCOMING; 693 case CSD_CALL_STATUS_COMING: 694 return CALL_STATUS_INCOMING; 695 case CSD_CALL_STATUS_MO_ALERTING: 696 return CALL_STATUS_ALERTING; 697 case CSD_CALL_STATUS_MT_ALERTING: 698 return CALL_STATUS_INCOMING; 699 case CSD_CALL_STATUS_ANSWERED: 700 case CSD_CALL_STATUS_ACTIVE: 701 case CSD_CALL_STATUS_RECONNECT_PENDING: 702 case CSD_CALL_STATUS_SWAP_INITIATED: 703 case CSD_CALL_STATUS_HOLD_INITIATED: 704 return CALL_STATUS_ACTIVE; 705 case CSD_CALL_STATUS_RETRIEVE_INITIATED: 706 case CSD_CALL_STATUS_HOLD: 707 return CALL_STATUS_HELD; 708 default: 709 return -1; 710 } 711} 712 713void telephony_list_current_calls_req(void *telephony_device) 714{ 715 GSList *l; 716 int i; 717 718 DBG("telephony-maemo6: list current calls request"); 719 720 for (l = calls, i = 1; l != NULL; l = l->next, i++) { 721 struct csd_call *call = l->data; 722 int status, direction, multiparty; 723 724 status = csd_status_to_hfp(call); 725 if (status < 0) 726 continue; 727 728 direction = call->originating ? 729 CALL_DIR_OUTGOING : CALL_DIR_INCOMING; 730 731 multiparty = call->conference ? 732 CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO; 733 734 telephony_list_current_call_ind(i, direction, status, 735 CALL_MODE_VOICE, multiparty, 736 call->number, 737 number_type(call->number)); 738 } 739 740 telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE); 741} 742 743void telephony_operator_selection_req(void *telephony_device) 744{ 745 telephony_operator_selection_ind(OPERATOR_MODE_AUTO, 746 net.operator_name ? net.operator_name : ""); 747 telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE); 748} 749 750static void foreach_call_with_status(int status, 751 int (*func)(struct csd_call *call)) 752{ 753 GSList *l; 754 755 for (l = calls; l != NULL; l = l->next) { 756 struct csd_call *call = l->data; 757 758 if (call->status == status) 759 func(call); 760 } 761} 762 763void telephony_call_hold_req(void *telephony_device, const char *cmd) 764{ 765 const char *idx; 766 struct csd_call *call; 767 int err = 0; 768 769 DBG("telephony-maemo6: got call hold request %s", cmd); 770 771 if (strlen(cmd) > 1) 772 idx = &cmd[1]; 773 else 774 idx = NULL; 775 776 if (idx) 777 call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1); 778 else 779 call = NULL; 780 781 switch (cmd[0]) { 782 case '0': 783 foreach_call_with_status(CSD_CALL_STATUS_HOLD, release_call); 784 foreach_call_with_status(CSD_CALL_STATUS_WAITING, 785 release_call); 786 break; 787 case '1': 788 if (idx) { 789 if (call) 790 err = release_call(call); 791 break; 792 } 793 foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call); 794 call = find_call_with_status(CSD_CALL_STATUS_WAITING); 795 if (call) 796 err = answer_call(call); 797 break; 798 case '2': 799 if (idx) { 800 if (call) 801 err = split_call(call); 802 } else { 803 struct csd_call *held, *wait; 804 805 call = find_call_with_status(CSD_CALL_STATUS_ACTIVE); 806 held = find_call_with_status(CSD_CALL_STATUS_HOLD); 807 wait = find_call_with_status(CSD_CALL_STATUS_WAITING); 808 809 if (wait) 810 err = answer_call(wait); 811 else if (call && held) 812 err = swap_calls(); 813 else { 814 if (call) 815 err = hold_call(call); 816 if (held) 817 err = unhold_call(held); 818 } 819 } 820 break; 821 case '3': 822 if (find_call_with_status(CSD_CALL_STATUS_HOLD) || 823 find_call_with_status(CSD_CALL_STATUS_WAITING)) 824 err = create_conference(); 825 break; 826 case '4': 827 err = call_transfer(); 828 break; 829 default: 830 DBG("Unknown call hold request"); 831 break; 832 } 833 834 if (err) 835 telephony_call_hold_rsp(telephony_device, 836 CME_ERROR_AG_FAILURE); 837 else 838 telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE); 839} 840 841void telephony_nr_and_ec_req(void *telephony_device, gboolean enable) 842{ 843 DBG("telephony-maemo6: got %s NR and EC request", 844 enable ? "enable" : "disable"); 845 telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE); 846} 847 848void telephony_key_press_req(void *telephony_device, const char *keys) 849{ 850 struct csd_call *active, *waiting; 851 int err; 852 853 DBG("telephony-maemo6: got key press request for %s", keys); 854 855 waiting = find_call_with_status(CSD_CALL_STATUS_COMING); 856 if (!waiting) 857 waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING); 858 if (!waiting) 859 waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING); 860 861 active = find_call_with_status(CSD_CALL_STATUS_ACTIVE); 862 863 if (waiting) 864 err = answer_call(waiting); 865 else if (active) 866 err = release_call(active); 867 else 868 err = 0; 869 870 if (err < 0) 871 telephony_key_press_rsp(telephony_device, 872 CME_ERROR_AG_FAILURE); 873 else 874 telephony_key_press_rsp(telephony_device, CME_ERROR_NONE); 875} 876 877void telephony_voice_dial_req(void *telephony_device, gboolean enable) 878{ 879 DBG("telephony-maemo6: got %s voice dial request", 880 enable ? "enable" : "disable"); 881 882 telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED); 883} 884 885static void handle_incoming_call(DBusMessage *msg) 886{ 887 const char *number, *call_path; 888 struct csd_call *call; 889 890 if (!dbus_message_get_args(msg, NULL, 891 DBUS_TYPE_OBJECT_PATH, &call_path, 892 DBUS_TYPE_STRING, &number, 893 DBUS_TYPE_INVALID)) { 894 error("Unexpected parameters in Call.Coming() signal"); 895 return; 896 } 897 898 call = find_call(call_path); 899 if (!call) { 900 error("Didn't find any matching call object for %s", 901 call_path); 902 return; 903 } 904 905 DBG("Incoming call to %s from number %s", call_path, number); 906 907 g_free(call->number); 908 call->number = g_strdup(number); 909 910 telephony_update_indicator(maemo_indicators, "callsetup", 911 EV_CALLSETUP_INCOMING); 912 913 if (find_call_with_status(CSD_CALL_STATUS_ACTIVE)) 914 telephony_call_waiting_ind(call->number, 915 number_type(call->number)); 916 else 917 telephony_incoming_call_ind(call->number, 918 number_type(call->number)); 919} 920 921static void handle_outgoing_call(DBusMessage *msg) 922{ 923 const char *number, *call_path; 924 struct csd_call *call; 925 926 if (!dbus_message_get_args(msg, NULL, 927 DBUS_TYPE_OBJECT_PATH, &call_path, 928 DBUS_TYPE_STRING, &number, 929 DBUS_TYPE_INVALID)) { 930 error("Unexpected parameters in Call.Created() signal"); 931 return; 932 } 933 934 call = find_call(call_path); 935 if (!call) { 936 error("Didn't find any matching call object for %s", 937 call_path); 938 return; 939 } 940 941 DBG("Outgoing call from %s to number %s", call_path, number); 942 943 g_free(call->number); 944 call->number = g_strdup(number); 945 946 g_free(last_dialed_number); 947 last_dialed_number = g_strdup(number); 948 949 if (create_request_timer) { 950 g_source_remove(create_request_timer); 951 create_request_timer = 0; 952 } 953} 954 955static gboolean create_timeout(gpointer user_data) 956{ 957 telephony_update_indicator(maemo_indicators, "callsetup", 958 EV_CALLSETUP_INACTIVE); 959 create_request_timer = 0; 960 return FALSE; 961} 962 963static void handle_create_requested(DBusMessage *msg) 964{ 965 DBG("Call.CreateRequested()"); 966 967 if (create_request_timer) 968 g_source_remove(create_request_timer); 969 970 create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL); 971 972 telephony_update_indicator(maemo_indicators, "callsetup", 973 EV_CALLSETUP_OUTGOING); 974} 975 976static void handle_call_status(DBusMessage *msg, const char *call_path) 977{ 978 struct csd_call *call; 979 dbus_uint32_t status, cause_type, cause; 980 int callheld = telephony_get_indicator(maemo_indicators, "callheld"); 981 982 if (!dbus_message_get_args(msg, NULL, 983 DBUS_TYPE_UINT32, &status, 984 DBUS_TYPE_UINT32, &cause_type, 985 DBUS_TYPE_UINT32, &cause, 986 DBUS_TYPE_INVALID)) { 987 error("Unexpected paramters in Instance.CallStatus() signal"); 988 return; 989 } 990 991 call = find_call(call_path); 992 if (!call) { 993 error("Didn't find any matching call object for %s", 994 call_path); 995 return; 996 } 997 998 if (status > 16) { 999 error("Invalid call status %u", status); 1000 return; 1001 } 1002 1003 DBG("Call %s changed from %s to %s", call_path, 1004 call_status_str[call->status], call_status_str[status]); 1005 1006 if (call->status == (int) status) { 1007 DBG("Ignoring CSD Call state change to existing state"); 1008 return; 1009 } 1010 1011 call->status = (int) status; 1012 1013 switch (status) { 1014 case CSD_CALL_STATUS_IDLE: 1015 if (call->setup) { 1016 telephony_update_indicator(maemo_indicators, 1017 "callsetup", 1018 EV_CALLSETUP_INACTIVE); 1019 if (!call->originating) 1020 telephony_calling_stopped_ind(); 1021 } 1022 1023 g_free(call->number); 1024 call->number = NULL; 1025 call->originating = FALSE; 1026 call->emergency = FALSE; 1027 call->on_hold = FALSE; 1028 call->conference = FALSE; 1029 call->setup = FALSE; 1030 break; 1031 case CSD_CALL_STATUS_CREATE: 1032 call->originating = TRUE; 1033 call->setup = TRUE; 1034 break; 1035 case CSD_CALL_STATUS_COMING: 1036 call->originating = FALSE; 1037 call->setup = TRUE; 1038 break; 1039 case CSD_CALL_STATUS_PROCEEDING: 1040 break; 1041 case CSD_CALL_STATUS_MO_ALERTING: 1042 telephony_update_indicator(maemo_indicators, "callsetup", 1043 EV_CALLSETUP_ALERTING); 1044 break; 1045 case CSD_CALL_STATUS_MT_ALERTING: 1046 break; 1047 case CSD_CALL_STATUS_WAITING: 1048 break; 1049 case CSD_CALL_STATUS_ANSWERED: 1050 break; 1051 case CSD_CALL_STATUS_ACTIVE: 1052 if (call->on_hold) { 1053 call->on_hold = FALSE; 1054 if (find_call_with_status(CSD_CALL_STATUS_HOLD)) 1055 telephony_update_indicator(maemo_indicators, 1056 "callheld", 1057 EV_CALLHELD_MULTIPLE); 1058 else 1059 telephony_update_indicator(maemo_indicators, 1060 "callheld", 1061 EV_CALLHELD_NONE); 1062 } else { 1063 if (!g_slist_find(active_calls, call)) 1064 active_calls = g_slist_prepend(active_calls, call); 1065 if (g_slist_length(active_calls) == 1) 1066 telephony_update_indicator(maemo_indicators, 1067 "call", 1068 EV_CALL_ACTIVE); 1069 /* Upgrade callheld status if necessary */ 1070 if (callheld == EV_CALLHELD_ON_HOLD) 1071 telephony_update_indicator(maemo_indicators, 1072 "callheld", 1073 EV_CALLHELD_MULTIPLE); 1074 telephony_update_indicator(maemo_indicators, 1075 "callsetup", 1076 EV_CALLSETUP_INACTIVE); 1077 if (!call->originating) 1078 telephony_calling_stopped_ind(); 1079 call->setup = FALSE; 1080 } 1081 break; 1082 case CSD_CALL_STATUS_MO_RELEASE: 1083 case CSD_CALL_STATUS_MT_RELEASE: 1084 active_calls = g_slist_remove(active_calls, call); 1085 if (g_slist_length(active_calls) == 0) 1086 telephony_update_indicator(maemo_indicators, "call", 1087 EV_CALL_INACTIVE); 1088 break; 1089 case CSD_CALL_STATUS_HOLD_INITIATED: 1090 break; 1091 case CSD_CALL_STATUS_HOLD: 1092 call->on_hold = TRUE; 1093 if (find_non_held_call()) 1094 telephony_update_indicator(maemo_indicators, 1095 "callheld", 1096 EV_CALLHELD_MULTIPLE); 1097 else 1098 telephony_update_indicator(maemo_indicators, 1099 "callheld", 1100 EV_CALLHELD_ON_HOLD); 1101 break; 1102 case CSD_CALL_STATUS_RETRIEVE_INITIATED: 1103 break; 1104 case CSD_CALL_STATUS_RECONNECT_PENDING: 1105 break; 1106 case CSD_CALL_STATUS_TERMINATED: 1107 if (call->on_hold && 1108 !find_call_with_status(CSD_CALL_STATUS_HOLD)) 1109 telephony_update_indicator(maemo_indicators, 1110 "callheld", 1111 EV_CALLHELD_NONE); 1112 else if (callheld == EV_CALLHELD_MULTIPLE && 1113 find_call_with_status(CSD_CALL_STATUS_HOLD)) 1114 telephony_update_indicator(maemo_indicators, 1115 "callheld", 1116 EV_CALLHELD_ON_HOLD); 1117 break; 1118 case CSD_CALL_STATUS_SWAP_INITIATED: 1119 break; 1120 default: 1121 error("Unknown call status %u", status); 1122 break; 1123 } 1124} 1125 1126static void handle_conference(DBusMessage *msg, gboolean joined) 1127{ 1128 const char *path; 1129 struct csd_call *call; 1130 1131 if (!dbus_message_get_args(msg, NULL, 1132 DBUS_TYPE_OBJECT_PATH, &path, 1133 DBUS_TYPE_INVALID)) { 1134 error("Unexpected parameters in Conference.%s", 1135 dbus_message_get_member(msg)); 1136 return; 1137 } 1138 1139 call = find_call(path); 1140 if (!call) { 1141 error("Conference signal for unknown call %s", path); 1142 return; 1143 } 1144 1145 DBG("Call %s %s the conference", path, joined ? "joined" : "left"); 1146 1147 call->conference = joined; 1148} 1149 1150static uint8_t str2status(const char *state) 1151{ 1152 if (g_strcmp0(state, "Home") == 0) 1153 return NETWORK_REG_STATUS_HOME; 1154 else if (g_strcmp0(state, "Roaming") == 0) 1155 return NETWORK_REG_STATUS_ROAMING; 1156 else if (g_strcmp0(state, "Offline") == 0) 1157 return NETWORK_REG_STATUS_OFFLINE; 1158 else if (g_strcmp0(state, "Searching") == 0) 1159 return NETWORK_REG_STATUS_SEARCHING; 1160 else if (g_strcmp0(state, "NoSim") == 0) 1161 return NETWORK_REG_STATUS_NO_SIM; 1162 else if (g_strcmp0(state, "Poweroff") == 0) 1163 return NETWORK_REG_STATUS_POWEROFF; 1164 else if (g_strcmp0(state, "Powersafe") == 0) 1165 return NETWORK_REG_STATUS_POWERSAFE; 1166 else if (g_strcmp0(state, "NoCoverage") == 0) 1167 return NETWORK_REG_STATUS_NO_COVERAGE; 1168 else if (g_strcmp0(state, "Reject") == 0) 1169 return NETWORK_REG_STATUS_REJECTED; 1170 else 1171 return NETWORK_REG_STATUS_UNKOWN; 1172} 1173 1174static void update_registration_status(const char *status) 1175{ 1176 uint8_t new_status; 1177 1178 new_status = str2status(status); 1179 1180 if (net.status == new_status) 1181 return; 1182 1183 switch (new_status) { 1184 case NETWORK_REG_STATUS_HOME: 1185 telephony_update_indicator(maemo_indicators, "roam", 1186 EV_ROAM_INACTIVE); 1187 if (net.status > NETWORK_REG_STATUS_ROAMING) 1188 telephony_update_indicator(maemo_indicators, 1189 "service", 1190 EV_SERVICE_PRESENT); 1191 break; 1192 case NETWORK_REG_STATUS_ROAMING: 1193 telephony_update_indicator(maemo_indicators, "roam", 1194 EV_ROAM_ACTIVE); 1195 if (net.status > NETWORK_REG_STATUS_ROAMING) 1196 telephony_update_indicator(maemo_indicators, 1197 "service", 1198 EV_SERVICE_PRESENT); 1199 break; 1200 case NETWORK_REG_STATUS_OFFLINE: 1201 case NETWORK_REG_STATUS_SEARCHING: 1202 case NETWORK_REG_STATUS_NO_SIM: 1203 case NETWORK_REG_STATUS_POWEROFF: 1204 case NETWORK_REG_STATUS_POWERSAFE: 1205 case NETWORK_REG_STATUS_NO_COVERAGE: 1206 case NETWORK_REG_STATUS_REJECTED: 1207 case NETWORK_REG_STATUS_UNKOWN: 1208 if (net.status < NETWORK_REG_STATUS_OFFLINE) 1209 telephony_update_indicator(maemo_indicators, 1210 "service", 1211 EV_SERVICE_NONE); 1212 break; 1213 } 1214 1215 net.status = new_status; 1216 1217 DBG("telephony-maemo6: registration status changed: %s", status); 1218} 1219 1220static void handle_registration_changed(DBusMessage *msg) 1221{ 1222 const char *status; 1223 1224 if (!dbus_message_get_args(msg, NULL, 1225 DBUS_TYPE_STRING, &status, 1226 DBUS_TYPE_INVALID)) { 1227 error("Unexpected parameters in RegistrationChanged"); 1228 return; 1229 } 1230 1231 update_registration_status(status); 1232} 1233 1234static void update_signal_strength(int32_t signal_bars) 1235{ 1236 if (signal_bars < 0) { 1237 DBG("signal strength smaller than expected: %d < 0", 1238 signal_bars); 1239 signal_bars = 0; 1240 } else if (signal_bars > 5) { 1241 DBG("signal strength greater than expected: %d > 5", 1242 signal_bars); 1243 signal_bars = 5; 1244 } 1245 1246 if (net.signal_bars == signal_bars) 1247 return; 1248 1249 telephony_update_indicator(maemo_indicators, "signal", signal_bars); 1250 1251 net.signal_bars = signal_bars; 1252 DBG("telephony-maemo6: signal strength updated: %d/5", signal_bars); 1253} 1254 1255static void handle_signal_bars_changed(DBusMessage *msg) 1256{ 1257 int32_t signal_bars; 1258 1259 if (!dbus_message_get_args(msg, NULL, 1260 DBUS_TYPE_INT32, &signal_bars, 1261 DBUS_TYPE_INVALID)) { 1262 error("Unexpected parameters in SignalBarsChanged"); 1263 return; 1264 } 1265 1266 update_signal_strength(signal_bars); 1267} 1268 1269static gboolean iter_get_basic_args(DBusMessageIter *iter, 1270 int first_arg_type, ...) 1271{ 1272 int type; 1273 va_list ap; 1274 1275 va_start(ap, first_arg_type); 1276 1277 for (type = first_arg_type; type != DBUS_TYPE_INVALID; 1278 type = va_arg(ap, int)) { 1279 void *value = va_arg(ap, void *); 1280 int real_type = dbus_message_iter_get_arg_type(iter); 1281 1282 if (real_type != type) { 1283 error("iter_get_basic_args: expected %c but got %c", 1284 (char) type, (char) real_type); 1285 break; 1286 } 1287 1288 dbus_message_iter_get_basic(iter, value); 1289 dbus_message_iter_next(iter); 1290 } 1291 1292 va_end(ap); 1293 1294 return type == DBUS_TYPE_INVALID ? TRUE : FALSE; 1295} 1296 1297static void hal_battery_level_reply(DBusPendingCall *call, void *user_data) 1298{ 1299 DBusError err; 1300 DBusMessage *reply; 1301 dbus_int32_t level; 1302 int *value = user_data; 1303 1304 reply = dbus_pending_call_steal_reply(call); 1305 1306 dbus_error_init(&err); 1307 if (dbus_set_error_from_message(&err, reply)) { 1308 error("hald replied with an error: %s, %s", 1309 err.name, err.message); 1310 dbus_error_free(&err); 1311 goto done; 1312 } 1313 1314 if (!dbus_message_get_args(reply, NULL, 1315 DBUS_TYPE_INT32, &level, 1316 DBUS_TYPE_INVALID)) { 1317 error("Unexpected args in hald reply"); 1318 goto done; 1319 } 1320 1321 *value = (int) level; 1322 1323 if (value == &battchg_last) 1324 DBG("telephony-maemo6: battery.charge_level.last_full is %d", 1325 *value); 1326 else if (value == &battchg_design) 1327 DBG("telephony-maemo6: battery.charge_level.design is %d", 1328 *value); 1329 else 1330 DBG("telephony-maemo6: battery.charge_level.current is %d", 1331 *value); 1332 1333 if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) { 1334 int new, max; 1335 1336 if (battchg_last > 0) 1337 max = battchg_last; 1338 else 1339 max = battchg_design; 1340 1341 new = battchg_cur * 5 / max; 1342 1343 telephony_update_indicator(maemo_indicators, "battchg", new); 1344 } 1345done: 1346 dbus_message_unref(reply); 1347} 1348 1349static void hal_get_integer(const char *path, const char *key, void *user_data) 1350{ 1351 send_method_call("org.freedesktop.Hal", path, 1352 "org.freedesktop.Hal.Device", 1353 "GetPropertyInteger", 1354 hal_battery_level_reply, user_data, 1355 DBUS_TYPE_STRING, &key, 1356 DBUS_TYPE_INVALID); 1357} 1358 1359static void handle_hal_property_modified(DBusMessage *msg) 1360{ 1361 DBusMessageIter iter, array; 1362 dbus_int32_t num_changes; 1363 const char *path; 1364 1365 path = dbus_message_get_path(msg); 1366 1367 dbus_message_iter_init(msg, &iter); 1368 1369 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) { 1370 error("Unexpected signature in hal PropertyModified signal"); 1371 return; 1372 } 1373 1374 dbus_message_iter_get_basic(&iter, &num_changes); 1375 dbus_message_iter_next(&iter); 1376 1377 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { 1378 error("Unexpected signature in hal PropertyModified signal"); 1379 return; 1380 } 1381 1382 dbus_message_iter_recurse(&iter, &array); 1383 1384 while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) { 1385 DBusMessageIter prop; 1386 const char *name; 1387 dbus_bool_t added, removed; 1388 1389 dbus_message_iter_recurse(&array, &prop); 1390 1391 if (!iter_get_basic_args(&prop, 1392 DBUS_TYPE_STRING, &name, 1393 DBUS_TYPE_BOOLEAN, &added, 1394 DBUS_TYPE_BOOLEAN, &removed, 1395 DBUS_TYPE_INVALID)) { 1396 error("Invalid hal PropertyModified parameters"); 1397 break; 1398 } 1399 1400 if (g_str_equal(name, "battery.charge_level.last_full")) 1401 hal_get_integer(path, name, &battchg_last); 1402 else if (g_str_equal(name, "battery.charge_level.current")) 1403 hal_get_integer(path, name, &battchg_cur); 1404 else if (g_str_equal(name, "battery.charge_level.design")) 1405 hal_get_integer(path, name, &battchg_design); 1406 1407 dbus_message_iter_next(&array); 1408 } 1409} 1410 1411static void csd_call_free(struct csd_call *call) 1412{ 1413 if (!call) 1414 return; 1415 1416 g_free(call->object_path); 1417 g_free(call->number); 1418 1419 g_free(call); 1420} 1421 1422static void parse_call_list(DBusMessageIter *iter) 1423{ 1424 do { 1425 DBusMessageIter call_iter; 1426 struct csd_call *call; 1427 const char *object_path, *number; 1428 dbus_uint32_t status; 1429 dbus_bool_t originating, terminating, emerg, on_hold, conf; 1430 1431 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) { 1432 error("Unexpected signature in GetCallInfoAll reply"); 1433 break; 1434 } 1435 1436 dbus_message_iter_recurse(iter, &call_iter); 1437 1438 if (!iter_get_basic_args(&call_iter, 1439 DBUS_TYPE_OBJECT_PATH, &object_path, 1440 DBUS_TYPE_UINT32, &status, 1441 DBUS_TYPE_BOOLEAN, &originating, 1442 DBUS_TYPE_BOOLEAN, &terminating, 1443 DBUS_TYPE_BOOLEAN, &emerg, 1444 DBUS_TYPE_BOOLEAN, &on_hold, 1445 DBUS_TYPE_BOOLEAN, &conf, 1446 DBUS_TYPE_STRING, &number, 1447 DBUS_TYPE_INVALID)) { 1448 error("Parsing call D-Bus parameters failed"); 1449 break; 1450 } 1451 1452 call = find_call(object_path); 1453 if (!call) { 1454 call = g_new0(struct csd_call, 1); 1455 call->object_path = g_strdup(object_path); 1456 call->status = (int) status; 1457 calls = g_slist_append(calls, call); 1458 DBG("telephony-maemo6: new csd call instance at %s", 1459 object_path); 1460 } 1461 1462 if (call->status == CSD_CALL_STATUS_IDLE) 1463 continue; 1464 1465 /* CSD gives incorrect call_hold property sometimes */ 1466 if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) || 1467 (call->status == CSD_CALL_STATUS_HOLD && 1468 !on_hold)) { 1469 error("Conflicting call status and on_hold property!"); 1470 on_hold = call->status == CSD_CALL_STATUS_HOLD; 1471 } 1472 1473 call->originating = originating; 1474 call->on_hold = on_hold; 1475 call->conference = conf; 1476 g_free(call->number); 1477 call->number = g_strdup(number); 1478 1479 } while (dbus_message_iter_next(iter)); 1480} 1481 1482static void update_operator_name(const char *name) 1483{ 1484 if (name == NULL) 1485 return; 1486 1487 g_free(net.operator_name); 1488 net.operator_name = g_strdup(name); 1489 1490 DBG("telephony-maemo6: operator name updated: %s", name); 1491} 1492 1493static void get_property_reply(DBusPendingCall *call, void *user_data) 1494{ 1495 char *prop = user_data; 1496 DBusError err; 1497 DBusMessage *reply; 1498 DBusMessageIter iter, sub; 1499 1500 reply = dbus_pending_call_steal_reply(call); 1501 1502 dbus_error_init(&err); 1503 if (dbus_set_error_from_message(&err, reply)) { 1504 error("csd replied with an error: %s, %s", 1505 err.name, err.message); 1506 dbus_error_free(&err); 1507 goto done; 1508 } 1509 1510 dbus_message_iter_init(reply, &iter); 1511 1512 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { 1513 error("Unexpected signature in Get return"); 1514 goto done; 1515 } 1516 1517 dbus_message_iter_recurse(&iter, &sub); 1518 1519 if (g_strcmp0(prop, "RegistrationStatus") == 0) { 1520 const char *status; 1521 1522 dbus_message_iter_get_basic(&sub, &status); 1523 update_registration_status(status); 1524 1525 get_property(CSD_CSNET_OPERATOR, "OperatorName"); 1526 get_property(CSD_CSNET_SIGNAL, "SignalBars"); 1527 } else if (g_strcmp0(prop, "OperatorName") == 0) { 1528 const char *name; 1529 1530 dbus_message_iter_get_basic(&sub, &name); 1531 update_operator_name(name); 1532 } else if (g_strcmp0(prop, "SignalBars") == 0) { 1533 int32_t signal_bars; 1534 1535 dbus_message_iter_get_basic(&sub, &signal_bars); 1536 update_signal_strength(signal_bars); 1537 } 1538 1539done: 1540 g_free(prop); 1541 dbus_message_unref(reply); 1542} 1543 1544static int get_property(const char *iface, const char *prop) 1545{ 1546 return send_method_call(CSD_CSNET_BUS_NAME, CSD_CSNET_PATH, 1547 DBUS_INTERFACE_PROPERTIES, "Get", 1548 get_property_reply, g_strdup(prop), 1549 DBUS_TYPE_STRING, &iface, 1550 DBUS_TYPE_STRING, &prop, 1551 DBUS_TYPE_INVALID); 1552} 1553 1554static void handle_operator_name_changed(DBusMessage *msg) 1555{ 1556 const char *name; 1557 1558 if (!dbus_message_get_args(msg, NULL, 1559 DBUS_TYPE_STRING, &name, 1560 DBUS_TYPE_INVALID)) { 1561 error("Unexpected parameters in OperatorNameChanged"); 1562 return; 1563 } 1564 1565 update_operator_name(name); 1566} 1567 1568static void call_info_reply(DBusPendingCall *call, void *user_data) 1569{ 1570 DBusError err; 1571 DBusMessage *reply; 1572 DBusMessageIter iter, sub;; 1573 1574 get_calls_active = FALSE; 1575 1576 reply = dbus_pending_call_steal_reply(call); 1577 1578 dbus_error_init(&err); 1579 if (dbus_set_error_from_message(&err, reply)) { 1580 error("csd replied with an error: %s, %s", 1581 err.name, err.message); 1582 dbus_error_free(&err); 1583 goto done; 1584 } 1585 1586 dbus_message_iter_init(reply, &iter); 1587 1588 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { 1589 error("Unexpected signature in GetCallInfoAll return"); 1590 goto done; 1591 } 1592 1593 dbus_message_iter_recurse(&iter, &sub); 1594 1595 parse_call_list(&sub); 1596 1597 get_property(CSD_CSNET_REGISTRATION, "RegistrationStatus"); 1598 1599done: 1600 dbus_message_unref(reply); 1601} 1602 1603static void hal_find_device_reply(DBusPendingCall *call, void *user_data) 1604{ 1605 DBusError err; 1606 DBusMessage *reply; 1607 DBusMessageIter iter, sub; 1608 const char *path; 1609 char match_string[256]; 1610 int type; 1611 1612 reply = dbus_pending_call_steal_reply(call); 1613 1614 dbus_error_init(&err); 1615 if (dbus_set_error_from_message(&err, reply)) { 1616 error("hald replied with an error: %s, %s", 1617 err.name, err.message); 1618 dbus_error_free(&err); 1619 goto done; 1620 } 1621 1622 dbus_message_iter_init(reply, &iter); 1623 1624 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { 1625 error("Unexpected signature in FindDeviceByCapability return"); 1626 goto done; 1627 } 1628 1629 dbus_message_iter_recurse(&iter, &sub); 1630 1631 type = dbus_message_iter_get_arg_type(&sub); 1632 1633 if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) { 1634 error("No hal device with battery capability found"); 1635 goto done; 1636 } 1637 1638 dbus_message_iter_get_basic(&sub, &path); 1639 1640 DBG("telephony-maemo6: found battery device at %s", path); 1641 1642 snprintf(match_string, sizeof(match_string), 1643 "type='signal'," 1644 "path='%s'," 1645 "interface='org.freedesktop.Hal.Device'," 1646 "member='PropertyModified'", path); 1647 dbus_bus_add_match(connection, match_string, NULL); 1648 1649 hal_get_integer(path, "battery.charge_level.last_full", &battchg_last); 1650 hal_get_integer(path, "battery.charge_level.current", &battchg_cur); 1651 hal_get_integer(path, "battery.charge_level.design", &battchg_design); 1652 1653done: 1654 dbus_message_unref(reply); 1655} 1656 1657static void phonebook_read_reply(DBusPendingCall *call, void *user_data) 1658{ 1659 DBusError derr; 1660 DBusMessage *reply; 1661 const char *name, *number, *secondname, *additionalnumber, *email; 1662 int index; 1663 char **number_type = user_data; 1664 1665 reply = dbus_pending_call_steal_reply(call); 1666 1667 dbus_error_init(&derr); 1668 if (dbus_set_error_from_message(&derr, reply)) { 1669 error("%s.ReadFirst replied with an error: %s, %s", 1670 CSD_SIMPB_INTERFACE, derr.name, derr.message); 1671 dbus_error_free(&derr); 1672 if (number_type == &vmbx) 1673 vmbx = g_strdup(getenv("VMBX_NUMBER")); 1674 goto done; 1675 } 1676 1677 dbus_error_init(&derr); 1678 if (dbus_message_get_args(reply, NULL, 1679 DBUS_TYPE_INT32, &index, 1680 DBUS_TYPE_STRING, &name, 1681 DBUS_TYPE_STRING, &number, 1682 DBUS_TYPE_STRING, &secondname, 1683 DBUS_TYPE_STRING, &additionalnumber, 1684 DBUS_TYPE_STRING, &email, 1685 DBUS_TYPE_INVALID) == FALSE) { 1686 error("Unable to parse %s.ReadFirst arguments: %s, %s", 1687 CSD_SIMPB_INTERFACE, derr.name, derr.message); 1688 dbus_error_free(&derr); 1689 goto done; 1690 } 1691 1692 if (number_type == &msisdn) { 1693 g_free(msisdn); 1694 msisdn = g_strdup(number); 1695 DBG("Got MSISDN %s (%s)", number, name); 1696 } else { 1697 g_free(vmbx); 1698 vmbx = g_strdup(number); 1699 DBG("Got voice mailbox number %s (%s)", number, name); 1700 } 1701 1702done: 1703 dbus_message_unref(reply); 1704} 1705 1706static void csd_init(void) 1707{ 1708 const char *pb_type; 1709 int ret; 1710 1711 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, 1712 CSD_CALL_INTERFACE, "GetCallInfoAll", 1713 call_info_reply, NULL, DBUS_TYPE_INVALID); 1714 if (ret < 0) { 1715 error("Unable to sent GetCallInfoAll method call"); 1716 return; 1717 } 1718 1719 get_calls_active = TRUE; 1720 1721 pb_type = CSD_SIMPB_TYPE_MSISDN; 1722 1723 ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH, 1724 CSD_SIMPB_INTERFACE, "ReadFirst", 1725 phonebook_read_reply, &msisdn, 1726 DBUS_TYPE_STRING, &pb_type, 1727 DBUS_TYPE_INVALID); 1728 if (ret < 0) { 1729 error("Unable to send " CSD_SIMPB_INTERFACE ".read()"); 1730 return; 1731 } 1732 1733 /* Voicemail should be in MBDN index 0 */ 1734 pb_type = CSD_SIMPB_TYPE_MBDN; 1735 1736 ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH, 1737 CSD_SIMPB_INTERFACE, "ReadFirst", 1738 phonebook_read_reply, &vmbx, 1739 DBUS_TYPE_STRING, &pb_type, 1740 DBUS_TYPE_INVALID); 1741 if (ret < 0) { 1742 error("Unable to send " CSD_SIMPB_INTERFACE ".read()"); 1743 return; 1744 } 1745} 1746 1747static inline DBusMessage *invalid_args(DBusMessage *msg) 1748{ 1749 return g_dbus_create_error(msg,"org.bluez.Error.InvalidArguments", 1750 "Invalid arguments in method call"); 1751} 1752 1753static uint32_t get_callflag(const char *callerid_setting) 1754{ 1755 if (callerid_setting != NULL) { 1756 if (g_str_equal(callerid_setting, "allowed")) 1757 return CALL_FLAG_PRESENTATION_ALLOWED; 1758 else if (g_str_equal(callerid_setting, "restricted")) 1759 return CALL_FLAG_PRESENTATION_RESTRICTED; 1760 else 1761 return CALL_FLAG_NONE; 1762 } else 1763 return CALL_FLAG_NONE; 1764} 1765 1766static void generate_flag_file(const char *filename) 1767{ 1768 int fd; 1769 1770 if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS) || 1771 g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS) || 1772 g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS)) 1773 return; 1774 1775 fd = open(filename, O_WRONLY | O_CREAT, 0); 1776 if (fd >= 0) 1777 close(fd); 1778} 1779 1780static void save_callerid_to_file(const char *callerid_setting) 1781{ 1782 char callerid_file[FILENAME_MAX]; 1783 1784 snprintf(callerid_file, sizeof(callerid_file), "%s%s", 1785 CALLERID_BASE, callerid_setting); 1786 1787 if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS)) 1788 rename(ALLOWED_FLAG_FILE, callerid_file); 1789 else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS)) 1790 rename(RESTRICTED_FLAG_FILE, callerid_file); 1791 else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS)) 1792 rename(NONE_FLAG_FILE, callerid_file); 1793 else 1794 generate_flag_file(callerid_file); 1795} 1796 1797static uint32_t callerid_from_file(void) 1798{ 1799 if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS)) 1800 return CALL_FLAG_PRESENTATION_ALLOWED; 1801 else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS)) 1802 return CALL_FLAG_PRESENTATION_RESTRICTED; 1803 else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS)) 1804 return CALL_FLAG_NONE; 1805 else 1806 return CALL_FLAG_NONE; 1807} 1808 1809static DBusMessage *set_callerid(DBusConnection *conn, DBusMessage *msg, 1810 void *data) 1811{ 1812 const char *callerid_setting; 1813 1814 if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, 1815 &callerid_setting, 1816 DBUS_TYPE_INVALID) == FALSE) 1817 return invalid_args(msg); 1818 1819 if (g_str_equal(callerid_setting, "allowed") || 1820 g_str_equal(callerid_setting, "restricted") || 1821 g_str_equal(callerid_setting, "none")) { 1822 save_callerid_to_file(callerid_setting); 1823 callerid = get_callflag(callerid_setting); 1824 DBG("telephony-maemo6 setting callerid flag: %s", 1825 callerid_setting); 1826 return dbus_message_new_method_return(msg); 1827 } 1828 1829 error("telephony-maemo6: invalid argument %s for method call" 1830 " SetCallerId", callerid_setting); 1831 return invalid_args(msg); 1832} 1833 1834static DBusMessage *clear_lastnumber(DBusConnection *conn, DBusMessage *msg, 1835 void *data) 1836{ 1837 g_free(last_dialed_number); 1838 last_dialed_number = NULL; 1839 1840 return dbus_message_new_method_return(msg); 1841} 1842 1843static GDBusMethodTable telephony_maemo_methods[] = { 1844 { "SetCallerId", "s", "", set_callerid, 1845 G_DBUS_METHOD_FLAG_ASYNC }, 1846 { "ClearLastNumber", "", "", clear_lastnumber }, 1847 { } 1848}; 1849 1850static void handle_modem_state(DBusMessage *msg) 1851{ 1852 const char *state; 1853 1854 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state, 1855 DBUS_TYPE_INVALID)) { 1856 error("Unexpected modem state parameters"); 1857 return; 1858 } 1859 1860 DBG("SSC modem state: %s", state); 1861 1862 if (calls != NULL || get_calls_active) 1863 return; 1864 1865 if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online")) 1866 csd_init(); 1867} 1868 1869static void modem_state_reply(DBusPendingCall *call, void *user_data) 1870{ 1871 DBusMessage *reply = dbus_pending_call_steal_reply(call); 1872 DBusError err; 1873 1874 dbus_error_init(&err); 1875 if (dbus_set_error_from_message(&err, reply)) { 1876 error("get_modem_state: %s, %s", err.name, err.message); 1877 dbus_error_free(&err); 1878 } else 1879 handle_modem_state(reply); 1880 1881 dbus_message_unref(reply); 1882} 1883 1884static DBusHandlerResult signal_filter(DBusConnection *conn, 1885 DBusMessage *msg, void *data) 1886{ 1887 const char *path = dbus_message_get_path(msg); 1888 1889 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) 1890 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1891 1892 if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming")) 1893 handle_incoming_call(msg); 1894 else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created")) 1895 handle_outgoing_call(msg); 1896 else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, 1897 "CreateRequested")) 1898 handle_create_requested(msg); 1899 else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus")) 1900 handle_call_status(msg, path); 1901 else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined")) 1902 handle_conference(msg, TRUE); 1903 else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left")) 1904 handle_conference(msg, FALSE); 1905 else if (dbus_message_is_signal(msg, CSD_CSNET_REGISTRATION, 1906 "RegistrationChanged")) 1907 handle_registration_changed(msg); 1908 else if (dbus_message_is_signal(msg, CSD_CSNET_OPERATOR, 1909 "OperatorNameChanged")) 1910 handle_operator_name_changed(msg); 1911 else if (dbus_message_is_signal(msg, CSD_CSNET_SIGNAL, 1912 "SignalBarsChanged")) 1913 handle_signal_bars_changed(msg); 1914 else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device", 1915 "PropertyModified")) 1916 handle_hal_property_modified(msg); 1917 else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE, 1918 "modem_state_changed_ind")) 1919 handle_modem_state(msg); 1920 1921 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1922} 1923 1924int telephony_init(void) 1925{ 1926 const char *battery_cap = "battery"; 1927 uint32_t features = AG_FEATURE_EC_ANDOR_NR | 1928 AG_FEATURE_INBAND_RINGTONE | 1929 AG_FEATURE_REJECT_A_CALL | 1930 AG_FEATURE_ENHANCED_CALL_STATUS | 1931 AG_FEATURE_ENHANCED_CALL_CONTROL | 1932 AG_FEATURE_EXTENDED_ERROR_RESULT_CODES | 1933 AG_FEATURE_THREE_WAY_CALLING; 1934 1935 connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); 1936 1937 if (!dbus_connection_add_filter(connection, signal_filter, 1938 NULL, NULL)) 1939 error("Can't add signal filter"); 1940 1941 dbus_bus_add_match(connection, 1942 "type=signal,interface=" CSD_CALL_INTERFACE, NULL); 1943 dbus_bus_add_match(connection, 1944 "type=signal,interface=" CSD_CALL_INSTANCE, NULL); 1945 dbus_bus_add_match(connection, 1946 "type=signal,interface=" CSD_CALL_CONFERENCE, NULL); 1947 dbus_bus_add_match(connection, 1948 "type=signal,interface=" CSD_CSNET_REGISTRATION, 1949 NULL); 1950 dbus_bus_add_match(connection, 1951 "type=signal,interface=" CSD_CSNET_OPERATOR, 1952 NULL); 1953 dbus_bus_add_match(connection, 1954 "type=signal,interface=" CSD_CSNET_SIGNAL, 1955 NULL); 1956 dbus_bus_add_match(connection, 1957 "type=signal,interface=" SSC_DBUS_IFACE 1958 ",member=modem_state_changed_ind", NULL); 1959 1960 if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE, 1961 "get_modem_state", modem_state_reply, 1962 NULL, DBUS_TYPE_INVALID) < 0) 1963 error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()"); 1964 1965 generate_flag_file(NONE_FLAG_FILE); 1966 callerid = callerid_from_file(); 1967 1968 if (!g_dbus_register_interface(connection, TELEPHONY_MAEMO_PATH, 1969 TELEPHONY_MAEMO_INTERFACE, telephony_maemo_methods, 1970 NULL, NULL, NULL, NULL)) { 1971 error("telephony-maemo6 interface %s init failed on path %s", 1972 TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH); 1973 } 1974 1975 DBG("telephony-maemo6 registering %s interface on path %s", 1976 TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH); 1977 1978 telephony_ready_ind(features, maemo_indicators, response_and_hold, 1979 chld_str); 1980 if (send_method_call("org.freedesktop.Hal", 1981 "/org/freedesktop/Hal/Manager", 1982 "org.freedesktop.Hal.Manager", 1983 "FindDeviceByCapability", 1984 hal_find_device_reply, NULL, 1985 DBUS_TYPE_STRING, &battery_cap, 1986 DBUS_TYPE_INVALID) < 0) 1987 error("Unable to send HAL method call"); 1988 1989 return 0; 1990} 1991 1992void telephony_exit(void) 1993{ 1994 g_free(net.operator_name); 1995 net.operator_name = NULL; 1996 1997 g_free(last_dialed_number); 1998 last_dialed_number = NULL; 1999 2000 g_slist_foreach(calls, (GFunc) csd_call_free, NULL); 2001 g_slist_free(calls); 2002 calls = NULL; 2003 2004 dbus_connection_remove_filter(connection, signal_filter, NULL); 2005 2006 dbus_connection_unref(connection); 2007 connection = NULL; 2008} 2009