ofono/drivers/rilmodem/phonebook.c

1055 lines
25 KiB
C

/*
*
* oFono - Open Source Telephony - RIL Modem Support
*
* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
* Copyright (C) ST-Ericsson SA 2010.
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2013 Jolla Ltd
* Contact: Jussi Kangas <jussi.kangas@tieto.com>
* Copyright (C) 2014 Canonical Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <glib.h>
#include <ofono.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/phonebook.h>
#include <sim.h>
#include <simfs.h>
#include <util.h>
#include "gril.h"
#include "simutil.h"
#include "common.h"
#include "rilmodem.h"
#define UNUSED 0xFF
#define EXT1_CP_SUBADDRESS 1
#define EXT1_ADDITIONAL_DATA 2
/* TON (Type Of Number) See TS 24.008 */
#define TON_MASK 0x70
#define TON_INTERNATIONAL 0x10
enum constructed_tag {
TYPE_1_TAG = 0xA8,
TYPE_2_TAG = 0xA9,
TYPE_3_TAG = 0xAA
};
enum file_type_tag {
TYPE_ADN = 0xC0,
TYPE_IAP = 0xC1,
TYPE_EXT1 = 0xC2,
TYPE_SNE = 0xC3,
TYPE_ANR = 0xC4,
TYPE_PBC = 0xC5,
TYPE_GPR = 0xC6,
TYPE_AAS = 0xC7,
TYPE_GAS = 0xC8,
TYPE_UID = 0xC9,
TYPE_EMAIL = 0xCA,
TYPE_CCP1 = 0xCB
};
struct pb_file_info {
enum constructed_tag pbr_type;
int file_id;
enum file_type_tag file_type;
int file_length;
int record_length;
};
struct record_to_read {
int file_id;
enum file_type_tag type_tag;
int record_length;
int record;
int adn_idx;
gboolean anr_ext; /* Is it an EXT1 record for ANR? */
gboolean set_by_iap; /* Type 2 file? */
};
struct phonebook_entry {
int entry;
char *name;
char *number;
char *email;
char *anr;
char *sne;
};
unsigned char sim_path[] = { 0x3F, 0x00, 0x7F, 0x10 };
unsigned char usim_path[] = { 0x3F, 0x00, 0x7F, 0x10, 0x5F, 0x3A };
/*
* Table for BCD to utf8 conversion. See table 4.4 in TS 31.102.
* BCD 0x0C indicates pause before sending following digits as DTMF tones.
* BCD 0x0D is a wildcard that means "any digit". These values are mapped to
* ',' and '?', following the Android/iPhone convention for the first and Nokia
* convention for the second (only OEM that I have seen that supports this
* feature). BCD 0x0E is reserved, we convert it to 'r'.
*/
static const char digit_to_utf8[] = "0123456789*#,?r\0";
/* One of these for each record in EF_PBR */
struct pb_ref_rec {
GSList *pb_files; /* File ids to read (pb_file_info nodes) */
GSList *pb_next; /* Next file info to read */
GSList *pending_records; /* List of record_to_read */
GSList *next_record; /* Next record_to_read to process */
GTree *phonebook; /* Container of phonebook_entry structures */
};
struct pb_data {
GSList *pb_refs;
GSList *pb_ref_next;
struct ofono_sim *sim;
struct ofono_sim_context *sim_context;
const unsigned char *df_path;
size_t df_size;
};
static void read_info_cb(int ok, unsigned char file_status,
int total_length, int record_length,
void *userdata);
static gint comp_int(gconstpointer a, gconstpointer b)
{
int a_val = GPOINTER_TO_INT(a);
int b_val = GPOINTER_TO_INT(b);
return a_val - b_val;
}
static const struct pb_file_info *
ext1_info(const GSList *pb_files)
{
const GSList *l;
for (l = pb_files; l; l = l->next) {
const struct pb_file_info *f_info = l->data;
if (f_info->file_type == TYPE_EXT1)
return f_info;
}
return NULL;
}
static struct phonebook_entry *handle_adn(size_t len, const unsigned char *msg,
struct pb_ref_rec *ref, int adn_idx)
{
unsigned name_length = len - 14;
unsigned number_start = name_length;
unsigned number_length;
unsigned extension_record = UNUSED;
unsigned i, prefix;
char *number = NULL;
char *name = sim_string_to_utf8(msg, name_length);
struct phonebook_entry *new_entry;
/* Length contains also TON & NPI */
number_length = msg[number_start];
if (number_length != UNUSED && number_length != 0) {
number_length--;
/* '+' + number + terminator */
number = g_try_malloc0(2 * number_length + 2);
if (number) {
prefix = 0;
if ((msg[number_start + 1] & TON_MASK)
== TON_INTERNATIONAL) {
number[0] = '+';
prefix = 1;
}
for (i = 0; i < number_length; i++) {
number[2 * i + prefix] =
digit_to_utf8[msg[number_start + 2 + i]
& 0x0f];
number[2 * i + 1 + prefix] =
digit_to_utf8[msg[number_start + 2 + i]
>> 4];
}
extension_record = msg[len - 1];
}
}
DBG("ADN name %s, number %s ", name, number);
DBG("number length %d extension_record %d",
2 * number_length, extension_record);
if ((name == NULL || *name == '\0') && number == NULL)
goto end;
new_entry = g_try_malloc0(sizeof(*new_entry));
if (new_entry == NULL) {
ofono_error("%s: out of memory", __func__);
goto end;
}
new_entry->name = name;
new_entry->number = number;
DBG("Creating PB entry %d with", adn_idx);
DBG("name %s and number %s", new_entry->name, new_entry->number);
g_tree_insert(ref->phonebook, GINT_TO_POINTER(adn_idx), new_entry);
if (extension_record != UNUSED) {
struct record_to_read *ext_rec =
g_try_malloc0(sizeof(*ext_rec));
const struct pb_file_info *f_info = ext1_info(ref->pb_files);
if (ext_rec && f_info) {
ext_rec->file_id = f_info->file_id;
ext_rec->type_tag = TYPE_EXT1;
ext_rec->record_length = f_info->record_length;
ext_rec->record = extension_record;
ext_rec->adn_idx = adn_idx;
ref->pending_records =
g_slist_prepend(ref->pending_records, ext_rec);
}
}
return new_entry;
end:
g_free(name);
g_free(number);
return NULL;
}
static void handle_iap(size_t len, const unsigned char *msg,
struct pb_ref_rec *ref,
const struct record_to_read *rec_data)
{
GSList *l;
size_t i = 0;
for (l = ref->pb_files; l; l = l->next) {
struct pb_file_info *f_info = l->data;
if (f_info->pbr_type == TYPE_2_TAG) {
if (i >= len) {
ofono_error("%s: EF_IAP record too small",
__func__);
return;
}
if (msg[i] != UNUSED) {
struct record_to_read *new_rec =
g_try_malloc0(sizeof(*new_rec));
if (new_rec == NULL) {
ofono_error("%s: OOM", __func__);
return;
}
DBG("type 0x%X record %d",
f_info->file_type, msg[i]);
new_rec->file_id = f_info->file_id;
new_rec->type_tag = f_info->file_type;
new_rec->record_length = f_info->record_length;
new_rec->record = msg[i];
new_rec->adn_idx = rec_data->adn_idx;
new_rec->anr_ext = FALSE;
new_rec->set_by_iap = TRUE;
ref->pending_records =
g_slist_prepend(ref->pending_records,
new_rec);
}
++i;
}
}
}
static void handle_sne(size_t len, const unsigned char *msg,
struct pb_ref_rec *ref,
const struct record_to_read *rec_data)
{
char *sne;
/* There are additional fields for type 2 files */
if (rec_data->set_by_iap)
len -= 2;
sne = sim_string_to_utf8(msg, len);
if (sne && *sne != '\0') {
struct phonebook_entry *entry;
entry = g_tree_lookup(ref->phonebook,
GINT_TO_POINTER(rec_data->adn_idx));
if (entry) {
/* If one already exists, delete it */
if (entry->sne)
g_free(entry->sne);
DBG("Adding SNE %s to %d", sne, rec_data->adn_idx);
DBG("name %s", entry->name);
entry->sne = sne;
} else {
g_free(sne);
}
} else {
g_free(sne);
}
}
static void handle_anr(size_t len,
const unsigned char *msg,
struct pb_ref_rec *ref,
const struct record_to_read *rec_data)
{
unsigned number_length;
unsigned extension_record;
unsigned aas_record;
unsigned i, prefix;
char *anr;
struct phonebook_entry *entry;
if (len < 15) {
ofono_error("%s: bad EF_ANR record size", __func__);
return;
}
aas_record = msg[0];
if (aas_record == UNUSED)
return;
DBG("ANR %d", aas_record);
/* Length contains also TON & NPI */
number_length = msg[1];
if (number_length < 2)
return;
number_length--;
/* '+' + number + terminator */
anr = g_try_malloc0(2 * number_length + 2);
if (anr == NULL)
return;
prefix = 0;
if ((msg[2] & TON_MASK) == TON_INTERNATIONAL) {
anr[0] = '+';
prefix = 1;
}
for (i = 0; i < number_length; i++) {
anr[2 * i + prefix] = digit_to_utf8[msg[3 + i] & 0x0f];
anr[2 * i + 1 + prefix] = digit_to_utf8[msg[3 + i] >> 4];
}
entry = g_tree_lookup(ref->phonebook,
GINT_TO_POINTER(rec_data->adn_idx));
if (entry == NULL) {
g_free(anr);
return;
}
/* If one already exists, delete it */
if (entry->anr)
g_free(entry->anr);
DBG("Adding ANR %s to %d", anr, rec_data->adn_idx);
DBG("name %s", entry->name);
entry->anr = anr;
extension_record = msg[14];
DBG("ANR to entry %d number %s number length %d",
rec_data->adn_idx, anr, number_length);
DBG("extension_record %d aas %d", extension_record, aas_record);
if (extension_record != UNUSED) {
struct record_to_read *ext_rec =
g_try_malloc0(sizeof(*ext_rec));
const struct pb_file_info *f_info = ext1_info(ref->pb_files);
if (ext_rec && f_info) {
ext_rec->file_id = f_info->file_id;
ext_rec->type_tag = TYPE_EXT1;
ext_rec->record_length = f_info->record_length;
ext_rec->record = extension_record;
ext_rec->adn_idx = rec_data->adn_idx;
ext_rec->anr_ext = TRUE;
ref->pending_records =
g_slist_prepend(ref->pending_records, ext_rec);
}
}
}
static void handle_email(size_t len, const unsigned char *msg,
struct pb_ref_rec *ref,
const struct record_to_read *rec_data)
{
char *email;
struct phonebook_entry *entry;
/* There are additional fields for type 2 files */
if (rec_data->set_by_iap)
len -= 2;
email = sim_string_to_utf8(msg, len);
if (email == NULL || *email == '\0') {
g_free(email);
return;
}
entry = g_tree_lookup(ref->phonebook,
GINT_TO_POINTER(rec_data->adn_idx));
if (entry == NULL) {
g_free(email);
return;
}
/* if one already exists, delete it */
if (entry->email)
g_free(entry->email);
DBG("Adding email to entry %d", rec_data->adn_idx);
DBG("name %s", entry->name);
entry->email = email;
}
static void handle_ext1(size_t len, const unsigned char *msg,
struct pb_ref_rec *ref,
const struct record_to_read *rec_data)
{
unsigned number_length, i, next_extension_record;
struct phonebook_entry *entry;
char *ext_number;
if (len < 13) {
ofono_error("%s: bad EF_EXT1 record size", __func__);
return;
}
/* Check if there is more extension data */
next_extension_record = msg[12];
if (next_extension_record != UNUSED) {
struct record_to_read *ext_rec =
g_try_malloc0(sizeof(*ext_rec));
const struct pb_file_info *f_info = ext1_info(ref->pb_files);
if (ext_rec && f_info) {
DBG("next_extension_record %d", next_extension_record);
ext_rec->file_id = f_info->file_id;
ext_rec->record_length = f_info->record_length;
ext_rec->type_tag = TYPE_EXT1;
ext_rec->record = next_extension_record;
ext_rec->adn_idx = rec_data->adn_idx;
ext_rec->anr_ext = rec_data->anr_ext;
ref->pending_records =
g_slist_prepend(ref->pending_records, ext_rec);
}
}
if (msg[0] != EXT1_ADDITIONAL_DATA) {
DBG("EXT1 record with subaddress ignored");
return;
}
number_length = msg[1];
ext_number = g_try_malloc0(2 * number_length + 1);
if (ext_number == NULL)
return;
for (i = 0; i < number_length; i++) {
ext_number[2 * i] = digit_to_utf8[msg[2 + i] & 0x0f];
ext_number[2 * i + 1] = digit_to_utf8[msg[2 + i] >> 4];
}
DBG("Number extension %s", ext_number);
DBG("number length %d", number_length);
DBG("Looking for ADN entry %d", rec_data->adn_idx);
entry = g_tree_lookup(ref->phonebook,
GINT_TO_POINTER(rec_data->adn_idx));
if (entry == NULL) {
g_free(ext_number);
return;
}
if (rec_data->anr_ext) {
char *anr = entry->anr;
entry->anr = g_strconcat(anr, ext_number, NULL);
g_free(anr);
} else {
char *number = entry->number;
entry->number = g_strconcat(number, ext_number, NULL);
g_free(number);
}
g_free(ext_number);
}
static const char *file_tag_to_string(enum file_type_tag tag)
{
switch (tag) {
case TYPE_ADN: return "ADN";
case TYPE_IAP: return "IAP";
case TYPE_EXT1: return "EXT1";
case TYPE_SNE: return "SNE";
case TYPE_ANR: return "ANR";
case TYPE_PBC: return "PBC";
case TYPE_GPR: return "GPR";
case TYPE_AAS: return "AAS";
case TYPE_GAS: return "GAS";
case TYPE_UID: return "UID";
case TYPE_EMAIL: return "EMAIL";
case TYPE_CCP1: return "CCP1";
default: return "<UNKNOWN>";
}
}
static void decode_read_response(const struct record_to_read *rec_data,
const unsigned char *msg, size_t len,
struct pb_ref_rec *ref)
{
DBG("Decoding %s type record", file_tag_to_string(rec_data->type_tag));
switch (rec_data->type_tag) {
case TYPE_IAP:
handle_iap(len, msg, ref, rec_data);
break;
case TYPE_SNE:
handle_sne(len, msg, ref, rec_data);
break;
case TYPE_ANR:
handle_anr(len, msg, ref, rec_data);
break;
case TYPE_EMAIL:
handle_email(len, msg, ref, rec_data);
break;
case TYPE_EXT1:
handle_ext1(len, msg, ref, rec_data);
break;
default:
DBG("Skipping type");
break;
}
}
static gboolean export_entry(gpointer key, gpointer value, gpointer data)
{
struct ofono_phonebook *pb = data;
struct phonebook_entry *entry = value;
ofono_phonebook_entry(pb, -1,
entry->number, -1,
entry->name, -1,
NULL,
entry->anr, -1,
entry->sne,
entry->email,
NULL, NULL);
g_free(entry->name);
g_free(entry->number);
g_free(entry->email);
g_free(entry->anr);
g_free(entry->sne);
g_free(entry);
return FALSE;
}
static void export_and_return(gboolean ok, struct cb_data *cbd)
{
struct ofono_phonebook *pb = cbd->user;
ofono_phonebook_cb_t cb = cbd->cb;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
GSList *l;
DBG("phonebook fully read");
for (l = pbd->pb_refs; l != NULL; l = l->next) {
struct pb_ref_rec *ref = l->data;
g_tree_foreach(ref->phonebook, export_entry, pb);
g_tree_destroy(ref->phonebook);
g_slist_free_full(ref->pending_records, g_free);
g_slist_free_full(ref->pb_files, g_free);
}
g_slist_free_full(pbd->pb_refs, g_free);
pbd->pb_refs = NULL;
if (ok)
CALLBACK_WITH_SUCCESS(cb, cbd->data);
else
CALLBACK_WITH_FAILURE(cb, cbd->data);
g_free(cbd);
}
static void read_record_cb(int ok, int total_length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct cb_data *cbd = userdata;
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
struct pb_ref_rec *ref = pbd->pb_ref_next->data;
struct record_to_read *rec;
if (!ok) {
ofono_error("%s: error %d", __func__, ok);
export_and_return(FALSE, cbd);
return;
}
DBG("ok %d; total_length %d; record %d; record_length %d",
ok, total_length, record, record_length);
rec = ref->next_record->data;
/* This call might add elements to pending_records */
decode_read_response(rec, data, record_length, ref);
ref->pending_records = g_slist_remove(ref->pending_records, rec);
g_free(rec);
if (ref->pending_records) {
struct record_to_read *rec;
ref->next_record = ref->pending_records;
rec = ref->next_record->data;
ofono_sim_read_record(pbd->sim_context, rec->file_id,
OFONO_SIM_FILE_STRUCTURE_FIXED,
rec->record,
rec->record_length,
pbd->df_path, pbd->df_size,
read_record_cb, cbd);
} else {
/* Read files from next EF_PBR record, if any */
pbd->pb_ref_next = pbd->pb_ref_next->next;
if (pbd->pb_ref_next == NULL) {
export_and_return(TRUE, cbd);
} else {
struct pb_ref_rec *ref;
DBG("Next EFpbr record");
ref = pbd->pb_ref_next->data;
if (!ref->pb_files) {
export_and_return(TRUE, cbd);
} else {
struct pb_file_info *file_info;
ref->pb_next = ref->pb_files;
file_info = ref->pb_files->data;
ofono_sim_read_info(pbd->sim_context,
file_info->file_id,
OFONO_SIM_FILE_STRUCTURE_FIXED,
pbd->df_path, pbd->df_size,
read_info_cb, cbd);
}
}
}
}
static void pb_adn_cb(int ok, int total_length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct cb_data *cbd = userdata;
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
struct pb_ref_rec *ref = pbd->pb_ref_next->data;
GSList *l;
if (!ok) {
ofono_error("%s: error %d", __func__, ok);
export_and_return(FALSE, cbd);
return;
}
DBG("ok %d; total_length %d; record %d; record_length %d",
ok, total_length, record, record_length);
if (handle_adn(record_length, data, ref, record) != NULL) {
/* Add type 1 records */
for (l = ref->pb_files; l; l = l->next) {
const struct pb_file_info *f_info = l->data;
struct record_to_read *ext_rec;
if (f_info->pbr_type == TYPE_1_TAG &&
f_info->file_type != TYPE_ADN) {
ext_rec = g_try_malloc0(sizeof(*ext_rec));
if (ext_rec == NULL)
break;
ext_rec->file_id = f_info->file_id;
ext_rec->type_tag = f_info->file_type;
ext_rec->record_length = f_info->record_length;
ext_rec->record = record;
ext_rec->adn_idx = record;
ref->pending_records =
g_slist_prepend(ref->pending_records,
ext_rec);
}
}
}
if (record*record_length >= total_length) {
DBG("All ADN records read: reading additional files");
if (ref->pending_records) {
struct record_to_read *rec;
ref->next_record = ref->pending_records;
rec = ref->next_record->data;
ofono_sim_read_record(pbd->sim_context, rec->file_id,
OFONO_SIM_FILE_STRUCTURE_FIXED,
rec->record,
rec->record_length,
pbd->df_path, pbd->df_size,
read_record_cb, cbd);
} else {
export_and_return(TRUE, cbd);
}
}
}
static void read_info_cb(int ok, unsigned char file_status,
int total_length, int record_length,
void *userdata)
{
struct cb_data *cbd = userdata;
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
struct pb_file_info *file_info;
struct pb_ref_rec *ref = pbd->pb_ref_next->data;
file_info = ref->pb_next->data;
ref->pb_next = ref->pb_next->next;
if (ok) {
file_info->record_length = record_length;
file_info->file_length = total_length;
DBG("file id %x record length %d total_length %d",
file_info->file_id, record_length, total_length);
} else {
ofono_warn("%s: %x not found", __func__, file_info->file_id);
ref->pb_files = g_slist_remove(ref->pb_files, file_info);
g_free(file_info);
}
if (ref->pb_next == NULL) {
if (ref->pb_files == NULL) {
ofono_warn("%s: no phonebook on SIM", __func__);
export_and_return(FALSE, cbd);
return;
}
/* Read full contents of the master file */
file_info = ref->pb_files->data;
ofono_sim_read_path(pbd->sim_context, file_info->file_id,
OFONO_SIM_FILE_STRUCTURE_FIXED,
pbd->df_path, pbd->df_size,
pb_adn_cb, cbd);
} else {
file_info = ref->pb_next->data;
ofono_sim_read_info(pbd->sim_context, file_info->file_id,
OFONO_SIM_FILE_STRUCTURE_FIXED,
pbd->df_path, pbd->df_size,
read_info_cb, cbd);
}
}
static void start_sim_app_read(struct cb_data *cbd)
{
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
struct pb_ref_rec *ref_rec;
struct pb_file_info *f_info;
struct pb_file_info *f_ext1;
pbd->df_path = sim_path;
pbd->df_size = sizeof(sim_path);
ref_rec = g_try_malloc0(sizeof(*ref_rec));
if (ref_rec == NULL) {
ofono_error("%s: OOM", __func__);
export_and_return(FALSE, cbd);
return;
}
ref_rec->phonebook = g_tree_new(comp_int);
/* Only EF_ADN and EF_EXT1 read for SIM */
f_info = g_try_malloc0(sizeof(*f_info));
if (f_info == NULL) {
ofono_error("%s: OOM", __func__);
export_and_return(FALSE, cbd);
return;
}
f_info->file_id = SIM_EFADN_FILEID;
f_info->pbr_type = TYPE_1_TAG;
f_info->file_type = TYPE_ADN;
ref_rec->pb_files = g_slist_append(ref_rec->pb_files, f_info);
f_ext1 = g_try_malloc0(sizeof(*f_ext1));
if (f_ext1 == NULL) {
ofono_error("%s: OOM", __func__);
export_and_return(FALSE, cbd);
return;
}
f_ext1->file_id = SIM_EFEXT1_FILEID;
f_ext1->pbr_type = TYPE_3_TAG;
f_ext1->file_type = TYPE_EXT1;
ref_rec->pb_files = g_slist_append(ref_rec->pb_files, f_ext1);
pbd->pb_refs = g_slist_append(pbd->pb_refs, ref_rec);
pbd->pb_ref_next = pbd->pb_refs;
ref_rec->pb_next = ref_rec->pb_files;
/* Start reading process for MF */
ofono_sim_read_info(pbd->sim_context, f_info->file_id,
OFONO_SIM_FILE_STRUCTURE_FIXED,
pbd->df_path, pbd->df_size,
read_info_cb, cbd);
}
static void pb_reference_data_cb(int ok, int total_length, int record,
const unsigned char *sdata,
int record_length, void *userdata)
{
struct cb_data *cbd = userdata;
struct ofono_phonebook *pb = cbd->user;
struct pb_data *pbd = ofono_phonebook_get_data(pb);
const unsigned char *ptr = sdata;
gboolean finished = FALSE;
struct pb_ref_rec *ref_rec;
DBG("total_length %d record %d record_length %d",
total_length, record, record_length);
if (!ok) {
/* We migh have a SIM instead of USIM application: try that */
DBG("%s: error %d, trying SIM files", __func__, ok);
start_sim_app_read(cbd);
return;
}
ref_rec = g_try_malloc0(sizeof(*ref_rec));
if (ref_rec == NULL) {
ofono_error("%s: OOM", __func__);
export_and_return(FALSE, cbd);
return;
}
ref_rec->phonebook = g_tree_new(comp_int);
while (ptr < sdata + record_length && finished == FALSE) {
int typelen, file_id, i;
enum constructed_tag pbr_type = *ptr;
switch (pbr_type) {
case TYPE_1_TAG:
case TYPE_2_TAG:
case TYPE_3_TAG:
typelen = *(ptr + 1);
DBG("File type=%02X, len=%d", *ptr, typelen);
ptr += 2;
i = 0;
while (i < typelen) {
struct pb_file_info *file_info =
g_try_new0(struct pb_file_info, 1);
if (!file_info) {
ofono_error("%s: OOM", __func__);
export_and_return(FALSE, cbd);
return;
}
file_id = (ptr[i + 2] << 8) + ptr[i + 3];
DBG("creating file info for File type=%02X",
ptr[i]);
DBG("File ID=%04X", file_id);
file_info->pbr_type = pbr_type;
file_info->file_type = ptr[i];
file_info->file_id = file_id;
/* Keep order, important for type 2 files */
ref_rec->pb_files =
g_slist_append(ref_rec->pb_files,
file_info);
i += ptr[i + 1] + 2;
}
ptr += typelen;
break;
default:
DBG("All handled %02x", *ptr);
finished = TRUE;
break;
}
}
pbd->pb_refs = g_slist_append(pbd->pb_refs, ref_rec);
if (record*record_length >= total_length) {
struct pb_ref_rec *ref;
struct pb_file_info *file_info;
DBG("All EFpbr records read");
pbd->pb_ref_next = pbd->pb_refs;
ref = pbd->pb_ref_next->data;
if (ref->pb_files == NULL) {
ofono_error("%s: no files to read", __func__);
export_and_return(FALSE, cbd);
return;
}
ref->pb_next = ref->pb_files;
file_info = ref->pb_files->data;
/* Start reading process for first EF_PBR entry */
ofono_sim_read_info(pbd->sim_context, file_info->file_id,
OFONO_SIM_FILE_STRUCTURE_FIXED,
pbd->df_path, pbd->df_size,
read_info_cb, cbd);
}
}
static void ril_export_entries(struct ofono_phonebook *pb,
const char *storage,
ofono_phonebook_cb_t cb, void *data)
{
struct pb_data *pbd = ofono_phonebook_get_data(pb);
struct cb_data *cbd;
DBG("Storage %s", storage);
/* Only for SIM memory */
if (strcmp(storage, "SM") != 0) {
CALLBACK_WITH_FAILURE(cb, data);
return;
}
cbd = cb_data_new(cb, data, pb);
/* Assume USIM, change in case EF_PBR is not present */
pbd->df_path = usim_path;
pbd->df_size = sizeof(usim_path);
ofono_sim_read(pbd->sim_context, SIM_EFPBR_FILEID,
OFONO_SIM_FILE_STRUCTURE_FIXED,
pb_reference_data_cb, cbd);
}
static gboolean ril_delayed_register(gpointer user_data)
{
struct ofono_phonebook *pb = user_data;
ofono_phonebook_register(pb);
return FALSE;
}
static int ril_phonebook_probe(struct ofono_phonebook *pb,
unsigned int vendor, void *user)
{
struct ofono_modem *modem = user;
struct pb_data *pd = g_try_new0(struct pb_data, 1);
if (pd == NULL)
return -ENOMEM;
pd->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
if (pd->sim == NULL)
return -ENOENT;
pd->sim_context = ofono_sim_context_create(pd->sim);
if (pd->sim_context == NULL)
return -ENOENT;
ofono_phonebook_set_data(pb, pd);
g_idle_add(ril_delayed_register, pb);
return 0;
}
static void ril_phonebook_remove(struct ofono_phonebook *pb)
{
struct pb_data *pbd = ofono_phonebook_get_data(pb);
ofono_phonebook_set_data(pb, NULL);
ofono_sim_context_free(pbd->sim_context);
g_free(pbd);
}
static const struct ofono_phonebook_driver driver = {
.name = RILMODEM,
.probe = ril_phonebook_probe,
.remove = ril_phonebook_remove,
.export_entries = ril_export_entries
};
void ril_phonebook_init(void)
{
ofono_phonebook_driver_register(&driver);
}
void ril_phonebook_exit(void)
{
ofono_phonebook_driver_unregister(&driver);
}