From 3ba458b87c1974f26e9b50835c3c8e3cabb60a25 Mon Sep 17 00:00:00 2001 From: Wolfgang Wiedmeyer Date: Sun, 28 May 2017 04:14:34 +0200 Subject: first version --- Android.mk | 1 + samsung-ril.c | 14 +++ samsung-ril.h | 8 ++ ss.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.c | 155 ++++++++++++++++++++++++++++++++ utils.h | 11 +++ 6 files changed, 473 insertions(+) create mode 100644 ss.c diff --git a/Android.mk b/Android.mk index 888d890..a7f53c6 100644 --- a/Android.mk +++ b/Android.mk @@ -33,6 +33,7 @@ LOCAL_SRC_FILES := \ network.c \ sound.c \ misc.c \ + ss.c \ oem.c \ data.c \ rfs.c \ diff --git a/samsung-ril.c b/samsung-ril.c index bcfb7ff..9f13b86 100644 --- a/samsung-ril.c +++ b/samsung-ril.c @@ -170,6 +170,11 @@ struct ipc_dispatch_handler ipc_fmt_dispatch_handlers[] = { .command = IPC_MISC_TIME_INFO, .handler = ipc_misc_time_info, }, + /* SS */ + { + .command = IPC_SS_USSD, + .handler = ipc_ss_ussd, + }, /* OEM */ { .command = IPC_SVC_DISPLAY_SCREEN, @@ -440,6 +445,15 @@ struct ril_request_handler ril_request_handlers[] = { .request = RIL_REQUEST_SCREEN_STATE, .handler = ril_request_screen_state, }, + /* SS */ + { + .request = RIL_REQUEST_SEND_USSD, + .handler = ril_request_send_ussd, + }, + { + .request = RIL_REQUEST_CANCEL_USSD, + .handler = ril_request_cancel_ussd, + }, /* OEM */ { .request = RIL_REQUEST_OEM_HOOK_RAW, diff --git a/samsung-ril.h b/samsung-ril.h index 497e72c..28a731f 100644 --- a/samsung-ril.h +++ b/samsung-ril.h @@ -366,6 +366,14 @@ int ipc_misc_time_info(struct ipc_message *message); int ril_request_screen_state(void *data, size_t size, RIL_Token token); /* + * SS + */ + +int ipc_ss_ussd(struct ipc_message *message); +int ril_request_send_ussd(void *data, size_t size, RIL_Token token); +int ril_request_cancel_ussd(void *data, size_t size, RIL_Token token); + +/* * OEM */ diff --git a/ss.c b/ss.c new file mode 100644 index 0000000..45fc48d --- /dev/null +++ b/ss.c @@ -0,0 +1,284 @@ +/* + * This file is part of Samsung-RIL. + * + * Copyright (C) 2013 Paul Kocialkowski + * Copyright (C) 2017 Wolfgang Wiedmeyer + * + * Samsung-RIL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Samsung-RIL 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 Samsung-RIL. If not, see . + */ + +#define LOG_TAG "RIL-SS" +#include + +#include +#include + +unsigned char global_ussd_state; + +int ipc_ss_ussd_callback(struct ipc_message *message) +{ + struct ipc_gen_phone_res_data *data; + int rc; + + if (message == NULL || message->data == NULL || message->size < sizeof(struct ipc_gen_phone_res_data)) + return -1; + + data = (struct ipc_gen_phone_res_data *) message->data; + + rc = ipc_gen_phone_res_check(data); + if (rc < 0) { + RIL_LOGE("There was an error, aborting USSD request"); + goto error; + } + + ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_SUCCESS, NULL, 0); + global_ussd_state = 0; + goto complete; + +error: + ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_GENERIC_FAILURE, NULL, 0); + +complete: + return 0; +} + +int ril_request_send_ussd(void *data, size_t size, RIL_Token token) +{ + char *data_enc = NULL; + int data_enc_len = 0; + char *message =NULL; + struct ipc_ss_ussd_header *ussd = NULL; + int message_size = 0xc0; + int rc; + + if (data == NULL || size < sizeof(char *)) + goto error; + + rc = ril_radio_state_check(RADIO_STATE_SIM_NOT_READY); + if (rc < 0) + return RIL_REQUEST_UNHANDLED; + + switch (global_ussd_state) { + case 0: + case IPC_SS_USSD_NO_ACTION_REQUIRE: + case IPC_SS_USSD_TERMINATED_BY_NET: + case IPC_SS_USSD_OTHER_CLIENT: + case IPC_SS_USSD_NOT_SUPPORT: + case IPC_SS_USSD_TIME_OUT: + RIL_LOGD("USSD Tx encoding is GSM7"); + + data_enc_len = ascii2gsm7_ussd(data, (unsigned char**)&data_enc, (int) size); + if (data_enc_len > message_size) { + RIL_LOGE("USSD message size is too long, aborting"); + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + + free(data_enc); + + return RIL_REQUEST_COMPLETED; + } + + message = malloc(message_size); + memset(message, 0, message_size); + + ussd = (struct ipc_ss_ussd_header *) message; + ussd->state = IPC_SS_USSD_NO_ACTION_REQUIRE; + ussd->dcs = 0x0f; // GSM7 in that case + ussd->length = data_enc_len; + + memcpy((void *) (message + sizeof(struct ipc_ss_ussd_header)), data_enc, data_enc_len); + + free(data_enc); + + break; + case IPC_SS_USSD_ACTION_REQUIRE: + default: + RIL_LOGD("USSD Tx encoding is ASCII"); + + data_enc_len = asprintf(&data_enc, "%s", (char*)data); + + if (data_enc_len > message_size) { + RIL_LOGE("USSD message size is too long, aborting"); + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + + free(data_enc); + + return RIL_REQUEST_COMPLETED; + } + + message = malloc(message_size); + memset(message, 0, message_size); + + ussd = (struct ipc_ss_ussd_header *) message; + ussd->state = IPC_SS_USSD_ACTION_REQUIRE; + ussd->dcs = 0x0f; // ASCII in that case + ussd->length = data_enc_len; + + memcpy((void *) (message + sizeof(struct ipc_ss_ussd_header)), data_enc, data_enc_len); + + free(data_enc); + + break; + } + + if (message == NULL) { + RIL_LOGE("USSD message is empty, aborting"); + + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + return RIL_REQUEST_COMPLETED; + } + + ipc_gen_phone_res_expect_callback(ipc_fmt_request_seq(token), IPC_SS_USSD, + ipc_ss_ussd_callback); + + rc = ipc_fmt_send(ipc_fmt_request_seq(token), IPC_SS_USSD, IPC_TYPE_EXEC, (void *) message, message_size); + if (rc < 0) + goto error; + + rc = RIL_REQUEST_HANDLED; + goto complete; + +error: + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + return RIL_REQUEST_COMPLETED; +complete: + return rc; +} + +int ril_request_cancel_ussd(void *data, size_t size, RIL_Token token) +{ + struct ipc_ss_ussd_header ussd; + int rc; + + rc = ril_radio_state_check(RADIO_STATE_SIM_READY); + if (rc < 0) + return RIL_REQUEST_UNHANDLED; + + memset(&ussd, 0, sizeof(ussd)); + + ussd.state = IPC_SS_USSD_TERMINATED_BY_NET; + global_ussd_state = IPC_SS_USSD_TERMINATED_BY_NET; + + rc = ipc_gen_phone_res_expect_complete(ipc_fmt_request_seq(token), IPC_SS_USSD); + if (rc < 0) + goto error; + + rc = ipc_fmt_send(ipc_fmt_request_seq(token), IPC_SS_USSD, IPC_TYPE_EXEC, (void *) &ussd, sizeof(ussd)); + if (rc < 0) + goto error; + + rc = RIL_REQUEST_HANDLED; + goto complete; + +error: + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + rc = RIL_REQUEST_COMPLETED; + +complete: + return rc; +} + +void ipc2ril_ussd_state(struct ipc_ss_ussd_header *ussd, char *message[2]) +{ + if (ussd == NULL || message == NULL) + return; + + switch (ussd->state) { + case IPC_SS_USSD_NO_ACTION_REQUIRE: + asprintf(&message[0], "%d", 0); + break; + case IPC_SS_USSD_ACTION_REQUIRE: + asprintf(&message[0], "%d", 1); + break; + case IPC_SS_USSD_TERMINATED_BY_NET: + asprintf(&message[0], "%d", 2); + break; + case IPC_SS_USSD_OTHER_CLIENT: + asprintf(&message[0], "%d", 3); + break; + case IPC_SS_USSD_NOT_SUPPORT: + asprintf(&message[0], "%d", 4); + break; + case IPC_SS_USSD_TIME_OUT: + asprintf(&message[0], "%d", 5); + break; + } +} + +int ipc_ss_ussd(struct ipc_message *message) +{ + char *data_dec = NULL; + int data_dec_len = 0; + sms_coding_scheme coding_scheme; + + char *ussd_message[2]; + + struct ipc_ss_ussd_header *ussd = NULL; + unsigned char state; + + if (message == NULL || message->data == NULL || message->size < sizeof(struct ipc_ss_ussd_header)) + goto error; + + memset(ussd_message, 0, sizeof(ussd_message)); + + ussd = (struct ipc_ss_ussd_header *) message->data; + + ipc2ril_ussd_state(ussd, ussd_message); + + global_ussd_state = ussd->state; + + if (ussd->length > 0 && message->size > 0 && message->data != NULL) { + coding_scheme = sms_get_coding_scheme(ussd->dcs); + switch (coding_scheme) { + case SMS_CODING_SCHEME_GSM7: + RIL_LOGD("USSD Rx encoding is GSM7"); + + data_dec_len = gsm72ascii((unsigned char *) message->data + + sizeof(struct ipc_ss_ussd_header), &data_dec, message->size - sizeof(struct ipc_ss_ussd_header)); + asprintf(&ussd_message[1], "%s", data_dec); + ussd_message[1][data_dec_len] = '\0'; + + break; + case SMS_CODING_SCHEME_UCS2: + RIL_LOGD("USSD Rx encoding %x is UCS2", ussd->dcs); + + data_dec_len = message->size - sizeof(struct ipc_ss_ussd_header); + ussd_message[1] = malloc(data_dec_len * 4 + 1); + + int i, result = 0; + char *ucs2 = (char*)message->data + sizeof(struct ipc_ss_ussd_header); + for (i = 0; i < data_dec_len; i += 2) { + int c = (ucs2[i] << 8) | ucs2[1 + i]; + result += utf8_write(ussd_message[1], result, c); + } + ussd_message[1][result] = '\0'; + break; + default: + RIL_LOGD("USSD Rx encoding %x is unknown, assuming ASCII", + ussd->dcs); + + data_dec_len = message->size - sizeof(struct ipc_ss_ussd_header); + asprintf(&ussd_message[1], "%s", (unsigned char *) message->data + sizeof(struct ipc_ss_ussd_header)); + ussd_message[1][data_dec_len] = '\0'; + break; + } + } + + ril_request_unsolicited(RIL_UNSOL_ON_USSD, ussd_message, sizeof(ussd_message)); + + return 0; + +error: + ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_GENERIC_FAILURE, NULL, 0); + return 0; +} diff --git a/utils.c b/utils.c index 164812e..80b95e7 100644 --- a/utils.c +++ b/utils.c @@ -61,6 +61,161 @@ void list_head_free(struct list_head *list) free(list); } +/* + * Converts GSM7 (8 bits) data to ASCII (7 bits) + */ +int gsm72ascii(unsigned char *data, char **data_dec, int length) +{ + int t, u, d, o = 0; + int i; + + int dec_length; + char *dec; + + dec_length = ((length * 8) - ((length * 8) % 7) ) / 7; + dec = malloc(dec_length); + + memset(dec, 0, dec_length); + + for (i = 0 ; i < length ; i++) + { + d = 7 - i % 7; + if (d == 7 && i != 0) + o++; + + t = (data[i] - (((data[i] >> d) & 0xff) << d)); + u = (data[i] >> d) & 0xff; + + dec[i+o]+=t << (i + o) % 8; + + if (u) + dec[i+1+o]+=u; + } + + *data_dec = dec; + + return dec_length; +} + +/* + * Converts ASCII (7 bits) data to GSM7 (8 bits) + */ +int ascii2gsm7_ussd(char *data, unsigned char **data_enc, int length) +{ + int d_off, d_pos, a_off, a_pos = 0; + int i; + + int enc_length; + unsigned char *enc; + + enc_length = ((length * 7) - (length * 7) % 8) / 8; + enc_length += (length * 7) % 8 > 0 ? 1 : 0; + + //FIXME: why does samsung does that? + enc_length++; + + enc = malloc(enc_length); + memset(enc, 0, enc_length); + + for (i = 0 ; i < length ; i++) + { + // offset from the right of data to keep + d_off = i % 8; + + // position of the data we keep + d_pos = ((i * 7) - (i * 7) % 8) / 8; + d_pos += (i * 7) % 8 > 0 ? 1 : 0; + + // adding the data with correct offset + enc[d_pos] |= data[i] >> d_off; + + // numbers of bits to omit to get data to add another place + a_off = 8 - d_off; + // position (on the encoded feed) of the data to add + a_pos = d_pos - 1; + + // adding the data to add at the correct position + enc[a_pos] |= data[i] << a_off; + } + + *data_enc = enc; + + //FIXME: what is going on here? + enc[enc_length - 2] |= 0x30; + enc[enc_length - 1] = 0x02; + + return enc_length; +} + +/* writes the utf8 character encoded in v + * to the buffer utf8 at the specified offset + */ +int utf8_write(char *utf8, int offset, int v) +{ + + int result; + + if (v < 0x80) { + result = 1; + if (utf8) + utf8[offset] = (char)v; + } else if (v < 0x800) { + result = 2; + if (utf8) { + utf8[offset + 0] = (char)(0xc0 | (v >> 6)); + utf8[offset + 1] = (char)(0x80 | (v & 0x3f)); + } + } else if (v < 0x10000) { + result = 3; + if (utf8) { + utf8[offset + 0] = (char)(0xe0 | (v >> 12)); + utf8[offset + 1] = (char)(0x80 | ((v >> 6) & 0x3f)); + utf8[offset + 2] = (char)(0x80 | (v & 0x3f)); + } + } else { + result = 4; + if (utf8) { + utf8[offset + 0] = (char)(0xf0 | ((v >> 18) & 0x7)); + utf8[offset + 1] = (char)(0x80 | ((v >> 12) & 0x3f)); + utf8[offset + 2] = (char)(0x80 | ((v >> 6) & 0x3f)); + utf8[offset + 3] = (char)(0x80 | (v & 0x3f)); + } + } + return result; +} + +sms_coding_scheme sms_get_coding_scheme(int data_encoding) +{ + switch (data_encoding >> 4) { + case 0x00: + case 0x02: + case 0x03: + return SMS_CODING_SCHEME_GSM7; + case 0x01: + if (data_encoding == 0x10) + return SMS_CODING_SCHEME_GSM7; + if (data_encoding == 0x11) + return SMS_CODING_SCHEME_UCS2; + break; + case 0x04: + case 0x05: + case 0x06: + case 0x07: + if (data_encoding & 0x20) + return SMS_CODING_SCHEME_UNKNOWN; + if (((data_encoding >> 2) & 3) == 0) + return SMS_CODING_SCHEME_GSM7; + if (((data_encoding >> 2) & 3) == 2) + return SMS_CODING_SCHEME_UCS2; + break; + case 0xF: + if (!(data_encoding & 4)) + return SMS_CODING_SCHEME_GSM7; + break; + } + return SMS_CODING_SCHEME_UNKNOWN; +} + int data_dump(const void *data, size_t size) { unsigned int cols = 8; diff --git a/utils.h b/utils.h index f651d35..21a8add 100644 --- a/utils.h +++ b/utils.h @@ -33,10 +33,21 @@ struct list_head { struct list_head *list_head_alloc(struct list_head *prev, struct list_head *next, const void *data); void list_head_free(struct list_head *list); +int gsm72ascii(unsigned char *data, char **data_dec, int length); +int ascii2gsm7_ussd(char *data, unsigned char **data_enc, int length); +int utf8_write(char *utf8, int offset, int v); int data_dump(const void *data, size_t size); int strings_array_free(char **array, size_t size); int eventfd_flush(int fd); int eventfd_recv(int fd, eventfd_t *event); int eventfd_send(int fd, eventfd_t event); +typedef enum { + SMS_CODING_SCHEME_UNKNOWN = 0, + SMS_CODING_SCHEME_GSM7, + SMS_CODING_SCHEME_UCS2 +} sms_coding_scheme; + +sms_coding_scheme sms_get_coding_scheme(int data_encoding); + #endif -- cgit v1.1