Merge remote-tracking branch 'cros/upstream' into 'cros/master'

Change-Id: Id4ea6235247ff73f1378549a3078380332b83682
diff --git a/TODO b/TODO
index 7f089f7..d9b8d27 100644
--- a/TODO
+++ b/TODO
@@ -25,14 +25,6 @@
    detailed sub-types, e.g.: the 'QmiNasRadioTechnologyPreference' flags type,
    where the first two bits define the type of the next ones.
 
- * dms: Support 'Preferred Roaming List' TLV (0x13) in DMS/"Activate Manual".
-   This TLV is based on an array of raw binary data which can be passed to the
-   device over multiple messages, using a sequence number to identify each
-   passed chunk. Probably not worth handling this specific case in qmi-codegen.
-   Also, sending such a message should trigger internally multiple messages
-   being sent automatically, not just one.
-
- * qmicli: Implement `--dms-activate-manual'.
  * qmicli: Implement `--dms-set-time'.
  * qmicli: Implement `--dms-get-alt-net-config'.
  * qmicli: Implement `--dms-set-alt-net-config'.
diff --git a/build-aux/qmi-codegen/VariableArray.py b/build-aux/qmi-codegen/VariableArray.py
index c5aadf9..c402da1 100644
--- a/build-aux/qmi-codegen/VariableArray.py
+++ b/build-aux/qmi-codegen/VariableArray.py
@@ -72,6 +72,13 @@
             default_array_size = { 'format' : 'guint8' }
             self.array_size_element = VariableFactory.create_variable(default_array_size, '', self.container_type)
 
+        # Load variable type for the sequence prefix
+        if 'sequence-prefix-format' in dictionary:
+            sequence = { 'format' : dictionary['sequence-prefix-format'] }
+            self.array_sequence_element = VariableFactory.create_variable(sequence, '', self.container_type)
+        else:
+            self.array_sequence_element = ''
+
 
     """
     Emit the type for the array element
@@ -150,15 +157,33 @@
             f.write(string.Template(template).substitute(translations))
         else:
             translations['array_size_element_format'] = self.array_size_element.public_format
-
             template = (
-                '${lp}    ${array_size_element_format} ${common_var_prefix}_n_items;\n'
+                '${lp}    ${array_size_element_format} ${common_var_prefix}_n_items;\n')
+
+            if self.array_sequence_element != '':
+                translations['array_sequence_element_format'] = self.array_sequence_element.public_format
+                template += (
+                    '${lp}    ${array_sequence_element_format} ${common_var_prefix}_sequence;\n')
+
+            template += (
                 '\n'
                 '${lp}    /* Read number of items in the array */\n')
             f.write(string.Template(template).substitute(translations))
-
             self.array_size_element.emit_buffer_read(f, line_prefix + '    ', common_var_prefix + '_n_items', buffer_name, buffer_len)
 
+            if self.array_sequence_element != '':
+                template = (
+                    '\n'
+                    '${lp}    /* Read sequence in the array */\n')
+                f.write(string.Template(template).substitute(translations))
+                self.array_size_element.emit_buffer_read(f, line_prefix + '    ', common_var_prefix + '_sequence', buffer_name, buffer_len)
+
+                template = (
+                    '\n'
+                    '${lp}    ${variable_name}_sequence = ${common_var_prefix}_sequence;\n')
+                f.write(string.Template(template).substitute(translations))
+
+
         template = (
             '\n'
             '${lp}    ${variable_name} = g_array_sized_new (\n'
@@ -202,15 +227,15 @@
 
         template = (
             '${lp}{\n'
-            '${lp}    guint ${common_var_prefix}_i;\n')
+            '${lp}    guint ${common_var_prefix}_i;\n'
+            '\n')
         f.write(string.Template(template).substitute(translations))
 
         if self.fixed_size:
             translations['fixed_size'] = self.fixed_size
 
             template = (
-                '${lp}    guint16 ${common_var_prefix}_n_items = ${fixed_size};\n'
-                '\n')
+                '${lp}    guint16 ${common_var_prefix}_n_items = ${fixed_size};\n')
             f.write(string.Template(template).substitute(translations))
         else:
             translations['array_size_element_format'] = self.array_size_element.public_format
@@ -228,13 +253,27 @@
                 '${lp}    const guint8 *${common_var_prefix}_aux_buffer = &${buffer_name}[${variable_name}];\n'
                 '${lp}    guint16 ${common_var_prefix}_aux_buffer_len = ${buffer_len} - ${variable_name};\n'
                 '\n'
-                '${lp}    ${variable_name} += ${array_size_element_size};\n'
-                '\n')
+                '${lp}    ${variable_name} += ${array_size_element_size};\n')
+
+            if self.array_sequence_element != '':
+                if self.array_sequence_element.public_format == 'guint8':
+                    translations['array_sequence_element_size'] = '1'
+                elif self.array_sequence_element.public_format == 'guint16':
+                    translations['array_sequence_element_size'] = '2'
+                elif self.array_sequence_element.public_format == 'guint32':
+                    translations['array_sequence_element_size'] = '4'
+                else:
+                    translations['array_sequence_element_size'] = '0'
+                template += (
+                    '\n'
+                    '${lp}    ${variable_name} += ${array_sequence_element_size};\n')
+
             f.write(string.Template(template).substitute(translations))
 
             self.array_size_element.emit_buffer_read(f, line_prefix + '    ', common_var_prefix + '_n_items', common_var_prefix + '_aux_buffer', common_var_prefix + '_aux_buffer_len')
 
         template = (
+            '\n'
             '${lp}    for (${common_var_prefix}_i = 0; ${common_var_prefix}_i < ${common_var_prefix}_n_items; ${common_var_prefix}_i++) {\n'
             '\n')
         f.write(string.Template(template).substitute(translations))
@@ -275,6 +314,10 @@
 
             self.array_size_element.emit_buffer_write(f, line_prefix + '    ', common_var_prefix + '_n_items', buffer_name, buffer_len)
 
+        if self.array_sequence_element != '':
+            self.array_sequence_element.emit_buffer_write(f, line_prefix + '    ', variable_name + '_sequence', buffer_name, buffer_len)
+
+
         template = (
             '\n'
             '${lp}    for (${common_var_prefix}_i = 0; ${common_var_prefix}_i < ${variable_name}->len; ${common_var_prefix}_i++) {\n')
@@ -314,15 +357,31 @@
             f.write(string.Template(template).substitute(translations))
         else:
             translations['array_size_element_format'] = self.array_size_element.public_format
-
             template = (
-                '${lp}    ${array_size_element_format} ${common_var_prefix}_n_items;\n'
+                '${lp}    ${array_size_element_format} ${common_var_prefix}_n_items;\n')
+
+            if self.array_sequence_element != '':
+                translations['array_sequence_element_format'] = self.array_sequence_element.public_format
+                template += (
+                    '${lp}    ${array_sequence_element_format} ${common_var_prefix}_sequence;\n')
+
+            template += (
                 '\n'
                 '${lp}    /* Read number of items in the array */\n')
             f.write(string.Template(template).substitute(translations))
-
             self.array_size_element.emit_buffer_read(f, line_prefix + '    ', common_var_prefix + '_n_items', buffer_name, buffer_len)
 
+            if self.array_sequence_element != '':
+                template = (
+                    '\n'
+                    '${lp}    /* Read sequence */\n')
+                f.write(string.Template(template).substitute(translations))
+                self.array_sequence_element.emit_buffer_read(f, line_prefix + '    ', common_var_prefix + '_sequence', buffer_name, buffer_len)
+                template = (
+                    '\n'
+                    '${lp}    g_string_append_printf (${printable}, "[[Seq:%u]] ", ${common_var_prefix}_sequence);\n')
+                f.write(string.Template(template).substitute(translations))
+
         template = (
             '\n'
             '${lp}    g_string_append (${printable}, "{");\n'
@@ -349,7 +408,13 @@
         translations = { 'lp'   : line_prefix,
                          'name' : variable_name }
 
-        template = (
+        template = ''
+        if self.array_sequence_element != '':
+            translations['array_sequence_element_format'] = self.array_sequence_element.public_format
+            template += (
+                '${lp}${array_sequence_element_format} ${name}_sequence;\n')
+
+        template += (
             '${lp}GArray *${name};\n')
         return string.Template(template).substitute(translations)
 
@@ -361,7 +426,13 @@
         translations = { 'lp'   : line_prefix,
                          'name' : variable_name }
 
-        template = (
+        template = ''
+        if self.array_sequence_element != '':
+            translations['array_sequence_element_format'] = self.array_sequence_element.public_format
+            template += (
+                '${lp}${array_sequence_element_format} *${name}_sequence,\n')
+
+        template += (
             '${lp}GArray **${name},\n')
         return string.Template(template).substitute(translations)
 
@@ -374,7 +445,12 @@
                          'public_array_element_format' : self.array_element.public_format,
                          'name'                        : variable_name }
 
-        template = (
+        template = ''
+        if self.array_sequence_element != '':
+            template += (
+                '${lp}@${name}_sequence: a placeholder for the output sequence number, or %NULL if not required.\n')
+
+        template += (
             '${lp}@${name}: a placeholder for the output #GArray of #${public_array_element_format} elements, or %NULL if not required. Do not free it, it is owned by @self.\n')
         return string.Template(template).substitute(translations)
 
@@ -387,15 +463,21 @@
                          'from' : variable_name_from,
                          'to'   : variable_name_to }
 
+        template = ''
+        if self.array_sequence_element != '':
+            template += (
+                '${lp}if (${to}_sequence)\n'
+                '${lp}    *${to}_sequence = ${from}_sequence;\n')
+
         if to_is_reference:
-            template = (
+            template += (
                 '${lp}if (${to})\n'
                 '${lp}    *${to} = ${from};\n')
-            return string.Template(template).substitute(translations)
         else:
-            template = (
+            template += (
                 '${lp}${to} = ${from};\n')
-            return string.Template(template).substitute(translations)
+
+        return string.Template(template).substitute(translations)
 
 
     """
@@ -405,7 +487,13 @@
         translations = { 'lp'   : line_prefix,
                          'name' : variable_name }
 
-        template = (
+        template = ''
+        if self.array_sequence_element != '':
+            translations['array_sequence_element_format'] = self.array_sequence_element.public_format
+            template += (
+                '${lp}${array_sequence_element_format} ${name}_sequence,\n')
+
+        template += (
             '${lp}GArray *${name},\n')
         return string.Template(template).substitute(translations)
 
@@ -418,7 +506,12 @@
                          'public_array_element_format' : self.array_element.public_format,
                          'name'                        : variable_name }
 
-        template = (
+        template = ''
+        if self.array_sequence_element != '':
+            template += (
+                '${lp}@${name}_sequence: the sequence number.\n')
+
+        template += (
             '${lp}@${name}: a #GArray of #${public_array_element_format} elements. A new reference to @${name} will be taken.\n')
         return string.Template(template).substitute(translations)
 
@@ -431,7 +524,12 @@
                          'from' : variable_name_from,
                          'to'   : variable_name_to }
 
-        template = (
+        template = ''
+        if self.array_sequence_element != '':
+            template += (
+                '${lp}${to}_sequence = ${from}_sequence;\n')
+
+        template += (
             '${lp}if (${to})\n'
             '${lp}    g_array_unref (${to});\n'
             '${lp}${to} = g_array_ref (${from});\n')
@@ -446,7 +544,12 @@
                          'public_array_element_format' : self.array_element.public_format,
                          'name'                        : variable_name }
 
-        template = (
+        template = ''
+        if self.array_sequence_element != '':
+            template += (
+                '${lp}@${name}_sequence: the sequence number.\n')
+
+        template += (
             '${lp}@${name}: a #GArray of #${public_array_element_format} elements.\n')
         return string.Template(template).substitute(translations)
 
diff --git a/build-aux/qmi-codegen/utils.py b/build-aux/qmi-codegen/utils.py
index e9d4ea1..cb12962 100644
--- a/build-aux/qmi-codegen/utils.py
+++ b/build-aux/qmi-codegen/utils.py
@@ -76,8 +76,8 @@
     else:
         template += (
             "#include \"qmi-enums-private.h\"\n")
-    # CTL, WDS, WMS and PDS don't have flags64
-    if service != 'CTL' and service != 'WDS' and service != 'WMS' and service != 'PDS':
+    # CTL, WDS, WMS, PDS and PBM don't have flags64
+    if service != 'CTL' and service != 'WDS' and service != 'WMS' and service != 'PDS' and service != 'PBM':
         template += (
             "#include \"qmi-flags64-${service}.h\"\n")
     template += (
diff --git a/cli/Makefile.am b/cli/Makefile.am
index fddf664..cc6e839 100644
--- a/cli/Makefile.am
+++ b/cli/Makefile.am
@@ -18,7 +18,8 @@
 	qmicli-helpers.h \
 	qmicli-dms.c \
 	qmicli-wds.c \
-	qmicli-nas.c
+	qmicli-nas.c \
+	qmicli-pbm.c
 
 qmicli_LDADD = \
 	$(QMICLI_LIBS) \
diff --git a/cli/qmicli-dms.c b/cli/qmicli-dms.c
index e94405b..0ad64a6 100644
--- a/cli/qmicli-dms.c
+++ b/cli/qmicli-dms.c
@@ -1716,6 +1716,7 @@
     QmiMessageDmsActivateManualInput *input;
     gchar **split;
     GError *error = NULL;
+    gulong split_1_int;
 
     split = g_strsplit (str, ",", -1);
     if (g_strv_length (split) != 4) {
@@ -1724,11 +1725,18 @@
         return NULL;
     }
 
+    split_1_int = strtoul (split[1], NULL, 10);
+    if (split_1_int > G_MAXUINT16) {
+        g_printerr ("error: invalid SID given '%s'\n",
+                    split[1]);
+        return NULL;
+    }
+
     input = qmi_message_dms_activate_manual_input_new ();
     if (!qmi_message_dms_activate_manual_input_set_info (
             input,
             split[0],
-            split[1],
+            (guint16)split_1_int,
             split[2],
             split[3],
             &error)) {
diff --git a/cli/qmicli-pbm.c b/cli/qmicli-pbm.c
new file mode 100644
index 0000000..ca36068
--- /dev/null
+++ b/cli/qmicli-pbm.c
@@ -0,0 +1,312 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * qmicli -- Command line interface to control QMI devices
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libqmi-glib.h>
+
+#include "qmicli.h"
+
+/* Context */
+typedef struct {
+    QmiDevice *device;
+    QmiClientPbm *client;
+    GCancellable *cancellable;
+} Context;
+static Context *ctx;
+
+/* Options */
+static gboolean get_all_capabilities_flag;
+static gboolean noop_flag;
+
+static GOptionEntry entries[] = {
+    { "pbm-get-all-capabilities", 0, 0, G_OPTION_ARG_NONE, &get_all_capabilities_flag,
+      "Get all phonebook capabilities",
+      NULL
+    },
+    { "pbm-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag,
+      "Just allocate or release a PBM client. Use with `--client-no-release-cid' and/or `--client-cid'",
+      NULL
+    },
+    { NULL }
+};
+
+GOptionGroup *
+qmicli_pbm_get_option_group (void)
+{
+	GOptionGroup *group;
+
+	group = g_option_group_new ("pbm",
+	                            "PBM options",
+	                            "Show Phonebook Management options",
+	                            NULL,
+	                            NULL);
+	g_option_group_add_entries (group, entries);
+
+	return group;
+}
+
+gboolean
+qmicli_pbm_options_enabled (void)
+{
+    static guint n_actions = 0;
+    static gboolean checked = FALSE;
+
+    if (checked)
+        return !!n_actions;
+
+    n_actions = (get_all_capabilities_flag +
+                 noop_flag);
+
+    if (n_actions > 1) {
+        g_printerr ("error: too many PBM actions requested\n");
+        exit (EXIT_FAILURE);
+    }
+
+    checked = TRUE;
+    return !!n_actions;
+}
+
+static void
+context_free (Context *context)
+{
+    if (!context)
+        return;
+
+    if (context->client)
+        g_object_unref (context->client);
+    g_object_unref (context->cancellable);
+    g_object_unref (context->device);
+    g_slice_free (Context, context);
+}
+
+static void
+shutdown (gboolean operation_status)
+{
+    /* Cleanup context and finish async operation */
+    context_free (ctx);
+    qmicli_async_operation_done (operation_status);
+}
+
+static void
+get_all_capabilities_ready (QmiClientPbm *client,
+                            GAsyncResult *res)
+{
+    GError *error = NULL;
+    QmiMessagePbmGetAllCapabilitiesOutput *output;
+    GArray *array = NULL;
+    guint i, j;
+
+    output = qmi_client_pbm_get_all_capabilities_finish (client, res, &error);
+    if (!output) {
+        g_printerr ("error: operation failed: %s\n",
+                    error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!qmi_message_pbm_get_all_capabilities_output_get_result (output, &error)) {
+        g_printerr ("error: couldn't get capabilities: %s\n", error->message);
+        g_error_free (error);
+        qmi_message_pbm_get_all_capabilities_output_unref (output);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Phonebook capabilities:\n",
+             qmi_device_get_path_display (ctx->device));
+
+    if (qmi_message_pbm_get_all_capabilities_output_get_capability_basic_information (output, &array, NULL)) {
+        g_print ("Capability basic information:\n");
+        for (i = 0; i < array->len; i++) {
+            QmiMessagePbmGetAllCapabilitiesOutputCapabilityBasicInformationElement *session;
+
+            session = &g_array_index (array,
+                                      QmiMessagePbmGetAllCapabilitiesOutputCapabilityBasicInformationElement,
+                                      i);
+            g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type));
+            for (j = 0; j < session->phonebooks->len; j++) {
+                QmiMessagePbmGetAllCapabilitiesOutputCapabilityBasicInformationElementPhonebooksElement *phonebook;
+                gchar *phonebook_type_str;
+
+                phonebook = &g_array_index (session->phonebooks,
+                                            QmiMessagePbmGetAllCapabilitiesOutputCapabilityBasicInformationElementPhonebooksElement,
+                                            j);
+                phonebook_type_str = qmi_pbm_phonebook_type_build_string_from_mask (phonebook->phonebook_type);
+                g_print ("\t\t[%s]:\n", phonebook_type_str);
+                g_print ("\t\t\tUsed records: %" G_GUINT16_FORMAT "\n", phonebook->used_records);
+                g_print ("\t\t\tMaximum records: %" G_GUINT16_FORMAT "\n", phonebook->maximum_records);
+                g_print ("\t\t\tMaximum number length: %u\n", phonebook->maximum_number_length);
+                g_print ("\t\t\tMaximum name length: %u\n", phonebook->maximum_name_length);
+                g_free (phonebook_type_str);
+            }
+        }
+    }
+
+    if (qmi_message_pbm_get_all_capabilities_output_get_group_capability (output, &array, NULL)) {
+        g_print ("Group capability:\n");
+        for (i = 0; i < array->len; i++) {
+            QmiMessagePbmGetAllCapabilitiesOutputGroupCapabilityElement *session;
+
+            session = &g_array_index (array,
+                                      QmiMessagePbmGetAllCapabilitiesOutputGroupCapabilityElement,
+                                      i);
+            g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type));
+            g_print ("\t\tMaximum groups: %u\n", session->maximum_groups);
+            g_print ("\t\tMaximum group tag length: %u\n", session->maximum_group_tag_length);
+        }
+    }
+
+    if (qmi_message_pbm_get_all_capabilities_output_get_additional_number_capability (output, &array, NULL)) {
+        g_print ("Additional number capability:\n");
+        for (i = 0; i < array->len; i++) {
+            QmiMessagePbmGetAllCapabilitiesOutputAdditionalNumberCapabilityElement *session;
+
+            session = &g_array_index (array,
+                                      QmiMessagePbmGetAllCapabilitiesOutputAdditionalNumberCapabilityElement,
+                                      i);
+            g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type));
+            g_print ("\t\tMaximum additional numbers: %u\n", session->maximum_additional_numbers);
+            g_print ("\t\tMaximum additional number length: %u\n", session->maximum_additional_number_length);
+            g_print ("\t\tMaximum additional number tag length: %u\n", session->maximum_additional_number_tag_length);
+        }
+    }
+
+    if (qmi_message_pbm_get_all_capabilities_output_get_email_capability (output, &array, NULL)) {
+        g_print ("Email capability:\n");
+        for (i = 0; i < array->len; i++) {
+            QmiMessagePbmGetAllCapabilitiesOutputEmailCapabilityElement *session;
+
+            session = &g_array_index (array,
+                                      QmiMessagePbmGetAllCapabilitiesOutputEmailCapabilityElement,
+                                      i);
+            g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type));
+            g_print ("\t\tMaximum emails: %u\n", session->maximum_emails);
+            g_print ("\t\tMaximum email address length: %u\n", session->maximum_email_address_length);
+        }
+    }
+
+    if (qmi_message_pbm_get_all_capabilities_output_get_second_name_capability (output, &array, NULL)) {
+        g_print ("Second name capability:\n");
+        for (i = 0; i < array->len; i++) {
+            QmiMessagePbmGetAllCapabilitiesOutputSecondNameCapabilityElement *session;
+
+            session = &g_array_index (array,
+                                      QmiMessagePbmGetAllCapabilitiesOutputSecondNameCapabilityElement,
+                                      i);
+            g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type));
+            g_print ("\t\tMaximum second name length: %u\n", session->maximum_second_name_length);
+        }
+    }
+
+    if (qmi_message_pbm_get_all_capabilities_output_get_hidden_records_capability (output, &array, NULL)) {
+        g_print ("Hidden records capability:\n");
+        for (i = 0; i < array->len; i++) {
+            QmiMessagePbmGetAllCapabilitiesOutputHiddenRecordsCapabilityElement *session;
+
+            session = &g_array_index (array,
+                                      QmiMessagePbmGetAllCapabilitiesOutputHiddenRecordsCapabilityElement,
+                                      i);
+            g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type));
+            g_print ("\t\tSupported: %s\n", session->supported ? "yes" : "no");
+        }
+    }
+
+    if (qmi_message_pbm_get_all_capabilities_output_get_grouping_information_alpha_string_capability (output, &array, NULL)) {
+        g_print ("Alpha string capability:\n");
+        for (i = 0; i < array->len; i++) {
+            QmiMessagePbmGetAllCapabilitiesOutputGroupingInformationAlphaStringCapabilityElement *session;
+
+            session = &g_array_index (array,
+                                      QmiMessagePbmGetAllCapabilitiesOutputGroupingInformationAlphaStringCapabilityElement,
+                                      i);
+            g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type));
+            g_print ("\t\tMaximum records: %u\n", session->maximum_records);
+            g_print ("\t\tUsed records: %u\n", session->used_records);
+            g_print ("\t\tMaximum string length: %u\n", session->maximum_string_length);
+        }
+    }
+
+    if (qmi_message_pbm_get_all_capabilities_output_get_additional_number_alpha_string_capability (output, &array, NULL)) {
+        g_print ("Additional number alpha string capability:\n");
+        for (i = 0; i < array->len; i++) {
+            QmiMessagePbmGetAllCapabilitiesOutputAdditionalNumberAlphaStringCapabilityElement *session;
+
+            session = &g_array_index (array,
+                                      QmiMessagePbmGetAllCapabilitiesOutputAdditionalNumberAlphaStringCapabilityElement,
+                                      i);
+            g_print ("\t[%s]:\n", qmi_pbm_session_type_get_string (session->session_type));
+            g_print ("\t\tMaximum records: %u\n", session->maximum_records);
+            g_print ("\t\tUsed records: %u\n", session->used_records);
+            g_print ("\t\tMaximum string length: %u\n", session->maximum_string_length);
+        }
+    }
+
+    qmi_message_pbm_get_all_capabilities_output_unref (output);
+    shutdown (TRUE);
+}
+
+static gboolean
+noop_cb (gpointer unused)
+{
+    shutdown (TRUE);
+    return FALSE;
+}
+
+void
+qmicli_pbm_run (QmiDevice *device,
+                QmiClientPbm *client,
+                GCancellable *cancellable)
+{
+    /* Initialize context */
+    ctx = g_slice_new (Context);
+    ctx->device = g_object_ref (device);
+    ctx->client = g_object_ref (client);
+    ctx->cancellable = g_object_ref (cancellable);
+
+    /* Request to get all capabilities? */
+    if (get_all_capabilities_flag) {
+        g_debug ("Asynchronously getting phonebook capabilities...");
+        qmi_client_pbm_get_all_capabilities (ctx->client,
+                                             NULL,
+                                             10,
+                                             ctx->cancellable,
+                                             (GAsyncReadyCallback)get_all_capabilities_ready,
+                                             NULL);
+        return;
+    }
+
+    /* Just client allocate/release? */
+    if (noop_flag) {
+        g_idle_add (noop_cb, NULL);
+        return;
+    }
+
+    g_warn_if_reached ();
+}
diff --git a/cli/qmicli.c b/cli/qmicli.c
index 43401a5..1a19380 100644
--- a/cli/qmicli.c
+++ b/cli/qmicli.c
@@ -46,6 +46,7 @@
 
 /* Main options */
 static gchar *device_str;
+static gboolean get_service_version_info_flag;
 static gchar *device_set_instance_id_str;
 static gboolean device_open_version_info_flag;
 static gboolean device_open_sync_flag;
@@ -60,6 +61,10 @@
       "Specify device path",
       "[PATH]"
     },
+    { "get-service-version-info", 0, 0, G_OPTION_ARG_NONE, &get_service_version_info_flag,
+      "Get service version info",
+      NULL
+    },
     { "device-set-instance-id", 0, 0, G_OPTION_ARG_STRING, &device_set_instance_id_str,
       "Set instance ID",
       "[Instance ID]"
@@ -191,7 +196,8 @@
     if (checked)
         return !!n_actions;
 
-    n_actions = !!device_set_instance_id_str;
+    n_actions = (!!device_set_instance_id_str +
+                 get_service_version_info_flag);
 
     if (n_actions > 1) {
         g_printerr ("error: too many generic actions requested\n");
@@ -283,6 +289,9 @@
     case QMI_SERVICE_WDS:
         qmicli_wds_run (dev, QMI_CLIENT_WDS (client), cancellable);
         return;
+    case QMI_SERVICE_PBM:
+        qmicli_pbm_run (dev, QMI_CLIENT_PBM (client), cancellable);
+        return;
     default:
         g_assert_not_reached ();
     }
@@ -370,6 +379,57 @@
 }
 
 static void
+get_service_version_info_ready (QmiDevice *dev,
+                                GAsyncResult *res)
+{
+    GError *error = NULL;
+    GArray *services;
+    guint i;
+
+    services = qmi_device_get_service_version_info_finish (dev, res, &error);
+    if (!services) {
+        g_printerr ("error: couldn't get service version info: %s\n",
+                    error->message);
+        exit (EXIT_FAILURE);
+    }
+
+    g_print ("[%s] Supported versions:\n",
+             qmi_device_get_path_display (dev));
+    for (i = 0; i < services->len; i++) {
+        QmiDeviceServiceVersionInfo *info;
+        const gchar *service_str;
+
+        info = &g_array_index (services, QmiDeviceServiceVersionInfo, i);
+        service_str = qmi_service_get_string (info->service);
+        if (service_str)
+            g_print ("\t%s (%u.%u)\n",
+                     service_str,
+                     info->major_version,
+                     info->minor_version);
+        else
+            g_print ("\tunknown [0x%02x] (%u.%u)\n",
+                     info->service,
+                     info->major_version,
+                     info->minor_version);
+    }
+    g_array_unref (services);
+
+    /* We're done now */
+    qmicli_async_operation_done (TRUE);
+}
+
+static void
+device_get_service_version_info (QmiDevice *dev)
+{
+    g_debug ("Getting service version info...");
+    qmi_device_get_service_version_info (dev,
+                                         10,
+                                         cancellable,
+                                         (GAsyncReadyCallback)get_service_version_info_ready,
+                                         NULL);
+}
+
+static void
 device_open_ready (QmiDevice *dev,
                    GAsyncResult *res)
 {
@@ -386,6 +446,8 @@
 
     if (device_set_instance_id_str)
         device_set_instance_id (dev);
+    else if (get_service_version_info_flag)
+        device_get_service_version_info (dev);
     else
         device_allocate_client (dev);
 }
@@ -450,6 +512,12 @@
         actions_enabled++;
     }
 
+    /* PBM options? */
+    if (qmicli_pbm_options_enabled ()) {
+        service = QMI_SERVICE_PBM;
+        actions_enabled++;
+    }
+
     /* Cannot mix actions from different services */
     if (actions_enabled > 1) {
         g_printerr ("error: cannot execute multiple actions of different services\n");
@@ -483,6 +551,8 @@
 	                            qmicli_nas_get_option_group ());
 	g_option_context_add_group (context,
 	                            qmicli_wds_get_option_group ());
+	g_option_context_add_group (context,
+	                            qmicli_pbm_get_option_group ());
     g_option_context_add_main_entries (context, main_entries, NULL);
     if (!g_option_context_parse (context, &argc, &argv, &error)) {
         g_printerr ("error: %s\n",
diff --git a/cli/qmicli.h b/cli/qmicli.h
index 2517b7f..53a9c9e 100644
--- a/cli/qmicli.h
+++ b/cli/qmicli.h
@@ -47,4 +47,11 @@
                                            QmiClientNas *client,
                                            GCancellable *cancellable);
 
+/* PBM group */
+GOptionGroup *qmicli_pbm_get_option_group (void);
+gboolean      qmicli_pbm_options_enabled  (void);
+void          qmicli_pbm_run              (QmiDevice *device,
+                                           QmiClientPbm *client,
+                                           GCancellable *cancellable);
+
 #endif /* __QMICLI_H__ */
diff --git a/data/Makefile.am b/data/Makefile.am
index 458dda5..d04a0d4 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -7,4 +7,5 @@
 	qmi-service-wds.json \
 	qmi-service-nas.json \
 	qmi-service-wms.json \
-	qmi-service-pds.json
+	qmi-service-pds.json \
+	qmi-service-pbm.json
diff --git a/data/qmi-service-dms.json b/data/qmi-service-dms.json
index ad74755..1e48455 100644
--- a/data/qmi-service-dms.json
+++ b/data/qmi-service-dms.json
@@ -586,27 +586,40 @@
                       "contents"  : [ { "name"       : "Service Programming Code",
                                         "format"     : "string",
                                         "fixed-size" : "6" },
-                                      { "name"       : "System Identification Number",
-                                        "format"     : "string",
-                                        "fixed-size" : "2" },
+                                      { "name"   : "System Identification Number",
+                                        "format" : "guint16" },
                                       { "name"     : "Mobile Directory Number",
                                         "format"   : "string",
                                         "max-size" : "15" },
                                       { "name"     : "Mobile Identification Number",
                                         "format"   : "string",
                                         "max-size" : "15" } ] },
-                    { "name"      : "MN HA key",
-                      "id"        : "0x11",
+                    { "name"               : "MN HA key",
+                      "id"                 : "0x11",
+                      "mandatory"          : "no",
+                      "type"               : "TLV",
+                      "format"             : "string",
+                      "max-size"           : "16",
+                      "size-prefix-format" : "guint8" },
+                    { "name"               : "MN AAA key",
+                      "id"                 : "0x12",
+                      "mandatory"          : "no",
+                      "type"               : "TLV",
+                      "format"             : "string",
+                      "max-size"           : "16",
+                      "size-prefix-format" : "guint8" },
+                    { "name"      : "PRL",
+                      "id"        : "0x13",
                       "mandatory" : "no",
                       "type"      : "TLV",
-                      "format"    : "string",
-                      "max-size"  : "16" },
-                    { "name"      : "MN AAA key",
-                      "id"        : "0x12",
-                      "mandatory" : "no",
-                      "type"      : "TLV",
-                      "format"    : "string",
-                      "max-size"  : "16" } ],
+                      "format"    : "sequence",
+                      "contents"  : [ { "name"   : "PRL total length",
+                                        "format" : "guint16" },
+                                      { "name"   : "PRL segment",
+                                        "format"                 : "array",
+                                        "size-prefix-format"     : "guint16",
+                                        "sequence-prefix-format" : "guint8",
+                                        "array-element"          : { "format" : "guint8" } } ] } ],
      "output"  : [  { "common-ref" : "Operation Result" } ] },
 
   // *********************************************************************************
@@ -684,15 +697,14 @@
      "service" : "DMS",
      "id"      : "0x0038",
      "version" : "1.6",
-     "input"   : [  { "name"               : "User Data",
-                      "id"                 : "0x01",
-                      "mandatory"          : "yes",
-                      "type"               : "TLV",
-                      "format"             : "array",
-                      "size-prefix-format" : "guint16",
-                      "array-element"      : { "format" : "guint8" },
-                      "prerequisites"      : [ { "common-ref" : "Success" } ] } ],
-     "output"  : [  { "common-ref" : "Operation Result" } ] },
+     "input"   : [ { "name"               : "User Data",
+                     "id"                 : "0x01",
+                     "mandatory"          : "yes",
+                     "type"               : "TLV",
+                     "format"             : "array",
+                     "size-prefix-format" : "guint16",
+                     "array-element"      : { "format" : "guint8" } } ],
+     "output"  : [ { "common-ref" : "Operation Result" } ] },
 
   // *********************************************************************************
   {  "name"    : "Read ERI File",
@@ -878,7 +890,7 @@
                       "format"        : "guint64",
                       "public-format" : "QmiDmsBandCapability",
                       "prerequisites": [ { "common-ref" : "Success" } ] },
-		            { "name"          : "LTE Band Capability",
+                    { "name"          : "LTE Band Capability",
                       "id"            : "0x10",
                       "mandatory"     : "no",
                       "type"          : "TLV",
diff --git a/data/qmi-service-pbm.json b/data/qmi-service-pbm.json
new file mode 100644
index 0000000..9296aea
--- /dev/null
+++ b/data/qmi-service-pbm.json
@@ -0,0 +1,298 @@
+
+[
+  // *********************************************************************************
+  {  "name"    : "PBM",
+     "type"    : "Service" },
+
+  // *********************************************************************************
+  {  "name"    : "QMI Client PBM",
+     "type"    : "Client" },
+
+  // *********************************************************************************
+  {  "name"    : "QMI Message PBM",
+     "type"    : "Message-ID-Enum" },
+
+  // *********************************************************************************
+  {  "name"    : "Indication Register",
+     "type"    : "Message",
+     "service" : "PBM",
+     "id"      : "0x0001",
+     "version" : "1.0",
+     "input"   : [ { "name"          : "Event Registration Mask",
+                     "id"            : "0x01",
+                     "mandatory"     : "yes",
+                     "type"          : "TLV",
+                     "format"        : "guint32",
+                     "public-format" : "QmiPbmEventRegistrationFlag" } ],
+     "output"  : [ { "common-ref" : "Operation Result" },
+                   { "name"          : "Event Registration Mask",
+                     "id"            : "0x10",
+                     "mandatory"     : "no",
+                     "type"          : "TLV",
+                     "format"        : "guint32",
+                     "public-format" : "QmiPbmEventRegistrationFlag",
+                     "prerequisites" : [ { "common-ref" : "Success" } ] } ] },
+
+  // *********************************************************************************
+  {  "name"    : "Get Capabilities",
+     "type"    : "Message",
+     "service" : "PBM",
+     "id"      : "0x0002",
+     "version" : "1.0",
+     "input"   : [ { "name"      : "Phonebook Information",
+                     "id"        : "0x01",
+                     "mandatory" : "yes",
+                     "type"      : "TLV",
+                     "format"    : "sequence",
+                     "contents"  : [ { "name"          : "Session Type",
+                                       "format"        : "guint8",
+                                       "public-format" : "QmiPbmSessionType" },
+                                     { "name"          : "Phonebook Type",
+                                       "format"        : "guint16",
+                                       "public-format" : "QmiPbmPhonebookType" } ] } ],
+     "output"  : [ { "common-ref" : "Operation Result" },
+                   { "name"          : "Capability Basic Information",
+                     "id"            : "0x10",
+                     "mandatory"     : "no",
+                     "type"          : "TLV",
+                     "format"        : "sequence",
+                     "contents"      : [ { "name"          : "Session Type",
+                                           "format"        : "guint8",
+                                           "public-format" : "QmiPbmSessionType" },
+                                         { "name"          : "Phonebook Type",
+                                           "format"        : "guint16",
+                                           "public-format" : "QmiPbmPhonebookType" },
+                                         { "name"   : "Used Records",
+                                           "format" : "guint16" },
+                                         { "name"   : "Maximum Records",
+                                           "format" : "guint16" },
+                                         { "name"   : "Maximum Number Length",
+                                           "format" : "guint8" },
+                                         { "name"   : "Maximum Name Length",
+                                           "format" : "guint8" } ],
+                     "prerequisites" : [ { "common-ref" : "Success" } ] },
+                   { "name"          : "Group Capability",
+                     "id"            : "0x11",
+                     "mandatory"     : "no",
+                     "type"          : "TLV",
+                     "format"        : "sequence",
+                     "contents"      : [ { "name"   : "Maximum Groups",
+                                           "format" : "guint8" },
+                                         { "name"   : "Maximum Group Tag Length",
+                                           "format" : "guint8" } ],
+                     "prerequisites" : [ { "common-ref" : "Success" } ] },
+                   { "name"          : "Additional Number Capability",
+                     "id"            : "0x12",
+                     "mandatory"     : "no",
+                     "type"          : "TLV",
+                     "format"        : "sequence",
+                     "contents"      : [ { "name"   : "Maximum Additional Numbers",
+                                           "format" : "guint8" },
+                                         { "name"   : "Maximum Additional Number Length",
+                                           "format" : "guint8" },
+                                         { "name"   : "Maximum Additional Number Tag Length",
+                                           "format" : "guint8" } ],
+                     "prerequisites" : [ { "common-ref" : "Success" } ] },
+                   { "name"          : "Email Capability",
+                     "id"            : "0x13",
+                     "mandatory"     : "no",
+                     "type"          : "TLV",
+                     "format"        : "sequence",
+                     "contents"      : [ { "name"   : "Maximum Emails",
+                                           "format" : "guint8" },
+                                         { "name"   : "Maximum Email Address Length",
+                                           "format" : "guint8" } ],
+                     "prerequisites" : [ { "common-ref" : "Success" } ] },
+                   { "name"          : "Second Name Capability",
+                     "id"            : "0x14",
+                     "mandatory"     : "no",
+                     "type"          : "TLV",
+                     "format"        : "sequence",
+                     "contents"      : [ { "name"   : "Maximum Second Name Length",
+                                           "format" : "guint8" } ],
+                     "prerequisites" : [ { "common-ref" : "Success" } ] },
+                   { "name"          : "Hidden Records Capability",
+                     "id"            : "0x15",
+                     "mandatory"     : "no",
+                     "type"          : "TLV",
+                     "format"        : "sequence",
+                     "contents"      : [ { "name"          : "Supported",
+                                           "format"        : "guint8",
+                                           "public-format" : "gboolean" } ],
+                     "prerequisites" : [ { "common-ref" : "Success" } ] },
+                   { "name"          : "Grouping Information Alpha String Capability",
+                     "id"            : "0x16",
+                     "mandatory"     : "no",
+                     "type"          : "TLV",
+                     "format"        : "sequence",
+                     "contents"      : [ { "name"   : "Maximum Records",
+                                           "format" : "guint8" },
+                                         { "name"   : "Used Records",
+                                           "format" : "guint8" },
+                                         { "name"   : "Maximum String Length",
+                                           "format" : "guint8" } ],
+                     "prerequisites" : [ { "common-ref" : "Success" } ] },
+                   { "name"          : "Additional Number Alpha String Capability",
+                     "id"            : "0x17",
+                     "mandatory"     : "no",
+                     "type"          : "TLV",
+                     "format"        : "sequence",
+                     "contents"      : [ { "name"   : "Maximum Records",
+                                           "format" : "guint8" },
+                                         { "name"   : "Used Records",
+                                           "format" : "guint8" },
+                                         { "name"   : "Maximum String Length",
+                                           "format" : "guint8" } ],
+                     "prerequisites" : [ { "common-ref" : "Success" } ] } ] },
+
+  // *********************************************************************************
+  {  "name"    : "Get All Capabilities",
+     "type"    : "Message",
+     "service" : "PBM",
+     "id"      : "0x0003",
+     "version" : "1.0",
+     "output"  : [ { "common-ref" : "Operation Result" },
+                   { "name"               : "Capability Basic Information",
+                     "id"                 : "0x10",
+                     "mandatory"          : "no",
+                     "type"               : "TLV",
+                     "format"             : "array",
+                     "size-prefix-format" : "guint8",
+                     "array-element"      : { "name"     : "Element",
+                                              "format"   : "struct",
+                                              "contents" : [ { "name"          : "Session Type",
+                                                               "format"        : "guint8",
+                                                               "public-format" : "QmiPbmSessionType" },
+                                                             { "name"               : "Phonebooks",
+                                                               "format"             : "array",
+                                                               "size-prefix-format" : "guint8",
+                                                               "array-element"      : { "name"     : "Element",
+                                                                                        "format"   : "struct",
+                                                                                        "contents" : [ { "name"          : "Phonebook Type",
+                                                                                                         "format"        : "guint16",
+                                                                                                         "public-format" : "QmiPbmPhonebookType" },
+                                                                                                       { "name"   : "Used Records",
+                                                                                                         "format" : "guint16" },
+                                                                                                       { "name"   : "Maximum Records",
+                                                                                                         "format" : "guint16" },
+                                                                                                       { "name"   : "Maximum Number Length",
+                                                                                                         "format" : "guint8" },
+                                                                                                       { "name"   : "Maximum Name Length",
+                                                                                                         "format" : "guint8" } ] } } ] },
+                     "prerequisites"      : [ { "common-ref" : "Success" } ] },
+                   { "name"               : "Group Capability",
+                     "id"                 : "0x11",
+                     "mandatory"          : "no",
+                     "type"               : "TLV",
+                     "format"             : "array",
+                     "size-prefix-format" : "guint8",
+                     "array-element"      : { "name"     : "Element",
+                                              "format"   : "struct",
+                                              "contents" : [ { "name"          : "Session Type",
+                                                               "format"        : "guint8",
+                                                               "public-format" : "QmiPbmSessionType" },
+                                                             { "name"   : "Maximum Groups",
+                                                               "format" : "guint8" },
+                                                             { "name"   : "Maximum Group Tag Length",
+                                                               "format" : "guint8" } ] },
+                     "prerequisites"      : [ { "common-ref" : "Success" } ] },
+                   { "name"               : "Additional Number Capability",
+                     "id"                 : "0x12",
+                     "mandatory"          : "no",
+                     "type"               : "TLV",
+                     "format"             : "array",
+                     "size-prefix-format" : "guint8",
+                     "array-element"      : { "name"     : "Element",
+                                              "format"   : "struct",
+                                              "contents" : [ { "name"          : "Session Type",
+                                                               "format"        : "guint8",
+                                                               "public-format" : "QmiPbmSessionType" },
+                                                             { "name"   : "Maximum Additional Numbers",
+                                                               "format" : "guint8" },
+                                                             { "name"   : "Maximum Additional Number Length",
+                                                               "format" : "guint8" },
+                                                             { "name"   : "Maximum Additional Number Tag Length",
+                                                               "format" : "guint8" } ] },
+                     "prerequisites"      : [ { "common-ref" : "Success" } ] },
+                   { "name"               : "Email Capability",
+                     "id"                 : "0x13",
+                     "mandatory"          : "no",
+                     "type"               : "TLV",
+                     "format"             : "array",
+                     "size-prefix-format" : "guint8",
+                     "array-element"      : { "name"     : "Element",
+                                              "format"   : "struct",
+                                              "contents" : [ { "name"          : "Session Type",
+                                                               "format"        : "guint8",
+                                                               "public-format" : "QmiPbmSessionType" },
+                                                             { "name"   : "Maximum Emails",
+                                                               "format" : "guint8" },
+                                                             { "name"   : "Maximum Email Address Length",
+                                                               "format" : "guint8" } ] },
+                     "prerequisites"      : [ { "common-ref" : "Success" } ] },
+                   { "name"               : "Second Name Capability",
+                     "id"                 : "0x14",
+                     "mandatory"          : "no",
+                     "type"               : "TLV",
+                     "format"             : "array",
+                     "size-prefix-format" : "guint8",
+                     "array-element"      : { "name"     : "Element",
+                                              "format"   : "struct",
+                                              "contents" : [ { "name"          : "Session Type",
+                                                               "format"        : "guint8",
+                                                               "public-format" : "QmiPbmSessionType" },
+                                                             { "name"   : "Maximum Second Name Length",
+                                                               "format" : "guint8" } ] },
+                     "prerequisites"      : [ { "common-ref" : "Success" } ] },
+                   { "name"               : "Hidden Records Capability",
+                     "id"                 : "0x15",
+                     "mandatory"          : "no",
+                     "type"               : "TLV",
+                     "format"             : "array",
+                     "size-prefix-format" : "guint8",
+                     "array-element"      : { "name"     : "Element",
+                                              "format"   : "struct",
+                                              "contents" : [ { "name"          : "Session Type",
+                                                               "format"        : "guint8",
+                                                               "public-format" : "QmiPbmSessionType" },
+                                                             { "name"          : "Supported",
+                                                               "format"        : "guint8",
+                                                               "public-format" : "gboolean" } ] },
+                     "prerequisites"      : [ { "common-ref" : "Success" } ] },
+                   { "name"               : "Grouping Information Alpha String Capability",
+                     "id"                 : "0x16",
+                     "mandatory"          : "no",
+                     "type"               : "TLV",
+                     "format"             : "array",
+                     "size-prefix-format" : "guint8",
+                     "array-element"      : { "name"     : "Element",
+                                              "format"   : "struct",
+                                              "contents" : [ { "name"          : "Session Type",
+                                                               "format"        : "guint8",
+                                                               "public-format" : "QmiPbmSessionType" },
+                                                             { "name"   : "Maximum Records",
+                                                               "format" : "guint8" },
+                                                             { "name"   : "Used Records",
+                                                               "format" : "guint8" },
+                                                             { "name"   : "Maximum String Length",
+                                                               "format" : "guint8" } ] },
+                     "prerequisites"      : [ { "common-ref" : "Success" } ] },
+                   { "name"               : "Additional Number Alpha String Capability",
+                     "id"                 : "0x17",
+                     "mandatory"          : "no",
+                     "type"               : "TLV",
+                     "format"             : "array",
+                     "size-prefix-format" : "guint8",
+                     "array-element"      : { "name"     : "Element",
+                                              "format"   : "struct",
+                                              "contents" : [ { "name"          : "Session Type",
+                                                               "format"        : "guint8",
+                                                               "public-format" : "QmiPbmSessionType" },
+                                                             { "name"   : "Maximum Records",
+                                                               "format" : "guint8" },
+                                                             { "name"   : "Used Records",
+                                                               "format" : "guint8" },
+                                                             { "name"   : "Maximum String Length",
+                                                               "format" : "guint8" } ] },
+                     "prerequisites"      : [ { "common-ref" : "Success" } ] } ] }
+]
diff --git a/docs/reference/libqmi-glib/Makefile.am b/docs/reference/libqmi-glib/Makefile.am
index dc60345..8bc6b2a 100644
--- a/docs/reference/libqmi-glib/Makefile.am
+++ b/docs/reference/libqmi-glib/Makefile.am
@@ -12,7 +12,8 @@
 	$(top_builddir)/libqmi-glib/generated/qmi-nas.sections \
 	$(top_builddir)/libqmi-glib/generated/qmi-wds.sections \
 	$(top_builddir)/libqmi-glib/generated/qmi-wms.sections \
-	$(top_builddir)/libqmi-glib/generated/qmi-pds.sections
+	$(top_builddir)/libqmi-glib/generated/qmi-pds.sections \
+	$(top_builddir)/libqmi-glib/generated/qmi-pbm.sections
 
 $(DOC_MODULE)-sections.mstamp: $(ALL_SECTIONS)
 	$(AM_V_GEN) \
diff --git a/docs/reference/libqmi-glib/libqmi-glib-common.sections b/docs/reference/libqmi-glib/libqmi-glib-common.sections
index dfc19b9..ea8f759 100644
--- a/docs/reference/libqmi-glib/libqmi-glib-common.sections
+++ b/docs/reference/libqmi-glib/libqmi-glib-common.sections
@@ -63,6 +63,9 @@
 qmi_device_set_instance_id_finish
 qmi_device_command
 qmi_device_command_finish
+QmiDeviceServiceVersionInfo
+qmi_device_get_service_version_info
+qmi_device_get_service_version_info_finish
 <SUBSECTION Standard>
 QmiDeviceClass
 QMI_DEVICE
@@ -622,6 +625,28 @@
 </SECTION>
 
 <SECTION>
+<FILE>qmi-enums-pbm</FILE>
+QmiPbmEventRegistrationFlag
+QmiPbmPhonebookType
+QmiPbmSessionType
+<SUBSECTION Methods>
+qmi_pbm_event_registration_flag_build_string_from_mask
+qmi_pbm_phonebook_type_build_string_from_mask
+qmi_pbm_session_type_get_string
+<SUBSECTION Private>
+qmi_pbm_event_registration_flag_get_string
+qmi_pbm_phonebook_type_get_string
+qmi_pbm_session_type_build_string_from_mask
+<SUBSECTION Standard>
+QMI_TYPE_PBM_EVENT_REGISTRATION_FLAG
+QMI_TYPE_PBM_PHONEBOOK_TYPE
+QMI_TYPE_PBM_SESSION_TYPE
+qmi_pbm_event_registration_flag_get_type
+qmi_pbm_phonebook_type_get_type
+qmi_pbm_session_type_get_type
+</SECTION>
+
+<SECTION>
 <FILE>qmi-errors</FILE>
 QmiCoreError
 QmiProtocolError
diff --git a/docs/reference/libqmi-glib/libqmi-glib-docs.xml b/docs/reference/libqmi-glib/libqmi-glib-docs.xml
index 316f970..829a34d 100644
--- a/docs/reference/libqmi-glib/libqmi-glib-docs.xml
+++ b/docs/reference/libqmi-glib/libqmi-glib-docs.xml
@@ -206,6 +206,18 @@
     </section>
   </chapter>
 
+  <chapter>
+    <title>Phonebook Management Service (PBM)</title>
+    <xi:include href="xml/qmi-client-pbm.xml"/>
+    <xi:include href="xml/qmi-enums-pbm.xml"/>
+    <section>
+      <title>PDS Requests</title>
+      <xi:include href="xml/qmi-message-pbm-indication-register.xml"/>
+      <xi:include href="xml/qmi-message-pbm-get-capabilities.xml"/>
+      <xi:include href="xml/qmi-message-pbm-get-all-capabilities.xml"/>
+    </section>
+  </chapter>
+
   <chapter id="object-tree">
     <title>Object Hierarchy</title>
      <xi:include href="xml/tree_index.sgml"/>
diff --git a/libqmi-glib/Makefile.am b/libqmi-glib/Makefile.am
index fdac069..20cb304 100644
--- a/libqmi-glib/Makefile.am
+++ b/libqmi-glib/Makefile.am
@@ -23,6 +23,7 @@
 	qmi-enums-nas.h qmi-flags64-nas.h\
 	qmi-enums-wms.h \
 	qmi-enums-pds.h \
+	qmi-enums-pbm.h \
 	qmi-enums.h qmi-enums-private.h \
 	qmi-utils.h qmi-utils.c \
 	qmi-message.h qmi-message.c \
@@ -48,6 +49,7 @@
 	qmi-enums-nas.h qmi-flags64-nas.h \
 	qmi-enums-wms.h \
 	qmi-enums-pds.h \
+	qmi-enums-pbm.h \
 	qmi-utils.h \
 	qmi-message.h \
 	qmi-device.h \
diff --git a/libqmi-glib/generated/Makefile.am b/libqmi-glib/generated/Makefile.am
index 25f4c48..a376ab2 100644
--- a/libqmi-glib/generated/Makefile.am
+++ b/libqmi-glib/generated/Makefile.am
@@ -11,7 +11,8 @@
 	qmi-nas.h \
 	qmi-wds.h \
 	qmi-wms.h \
-	qmi-pds.h
+	qmi-pds.h \
+	qmi-pbm.h
 
 GENERATED_C = \
 	qmi-error-types.c \
@@ -24,7 +25,8 @@
 	qmi-nas.c \
 	qmi-wds.c \
 	qmi-wms.c \
-	qmi-pds.c
+	qmi-pds.c \
+	qmi-pbm.c
 
 GENERATED_SECTIONS = \
 	qmi-ctl.sections \
@@ -32,7 +34,8 @@
 	qmi-nas.sections \
 	qmi-wds.sections \
 	qmi-wms.sections \
-	qmi-pds.sections
+	qmi-pds.sections \
+	qmi-pbm.sections
 
 # Error types
 qmi-error-types.h: $(top_srcdir)/libqmi-glib/qmi-errors.h $(top_srcdir)/build-aux/templates/qmi-error-types-template.h
@@ -61,10 +64,11 @@
 	$(top_srcdir)/libqmi-glib/qmi-enums-dms.h \
 	$(top_srcdir)/libqmi-glib/qmi-enums-nas.h \
 	$(top_srcdir)/libqmi-glib/qmi-enums-wms.h \
-	$(top_srcdir)/libqmi-glib/qmi-enums-pds.h
+	$(top_srcdir)/libqmi-glib/qmi-enums-pds.h \
+	$(top_srcdir)/libqmi-glib/qmi-enums-pbm.h
 qmi-enum-types.h:  $(ENUMS) $(top_srcdir)/build-aux/templates/qmi-enum-types-template.h
 	$(AM_V_GEN) $(GLIB_MKENUMS) \
-		--fhead "#ifndef __LIBQMI_GLIB_ENUM_TYPES_H__\n#define __LIBQMI_GLIB_ENUM_TYPES_H__\n#include \"qmi-enums.h\"\n#include \"qmi-enums-wds.h\"\n#include \"qmi-enums-dms.h\"\n#include \"qmi-enums-nas.h\"\n#include \"qmi-enums-wms.h\"\n#include \"qmi-enums-pds.h\"\n" \
+		--fhead "#ifndef __LIBQMI_GLIB_ENUM_TYPES_H__\n#define __LIBQMI_GLIB_ENUM_TYPES_H__\n#include \"qmi-enums.h\"\n#include \"qmi-enums-wds.h\"\n#include \"qmi-enums-dms.h\"\n#include \"qmi-enums-nas.h\"\n#include \"qmi-enums-wms.h\"\n#include \"qmi-enums-pds.h\"\n#include \"qmi-enums-pbm.h\"\n" \
 		--template $(top_srcdir)/build-aux/templates/qmi-enum-types-template.h \
 		--ftail "#endif /* __LIBQMI_GLIB_ENUM_TYPES_H__ */\n" \
 		$(ENUMS) > $@
@@ -166,6 +170,16 @@
 			--include $(top_srcdir)/data/qmi-common.json \
 			--output qmi-pds
 
+# PBM service
+qmi-pbm.h qmi-pbm.c qmi-pbm.sections: $(top_srcdir)/data/qmi-service-pbm.json $(top_srcdir)/build-aux/qmi-codegen/*.py $(top_srcdir)/build-aux/qmi-codegen/qmi-codegen
+	$(AM_V_GEN) \
+		rm -f qmi-pbm.h && \
+		rm -f qmi-pbm.c && \
+		$(top_srcdir)/build-aux/qmi-codegen/qmi-codegen \
+			--input $(top_srcdir)/data/qmi-service-pbm.json \
+			--include $(top_srcdir)/data/qmi-common.json \
+			--output qmi-pbm
+
 BUILT_SOURCES = $(GENERATED_H) $(GENERATED_C)
 
 nodist_libqmi_glib_generated_la_SOURCES = \
@@ -193,6 +207,7 @@
 	qmi-nas.h \
 	qmi-wds.h \
 	qmi-wms.h \
-	qmi-pds.h
+	qmi-pds.h \
+	qmi-pbm.h
 
 CLEANFILES = $(GENERATED_H) $(GENERATED_C) $(GENERATED_SECTIONS)
diff --git a/libqmi-glib/libqmi-glib.h b/libqmi-glib/libqmi-glib.h
index 3d60111..c17685d 100644
--- a/libqmi-glib/libqmi-glib.h
+++ b/libqmi-glib/libqmi-glib.h
@@ -52,6 +52,9 @@
 #include "qmi-enums-pds.h"
 #include "qmi-pds.h"
 
+#include "qmi-enums-pbm.h"
+#include "qmi-pbm.h"
+
 /* generated */
 #include "qmi-error-types.h"
 #include "qmi-enum-types.h"
diff --git a/libqmi-glib/qmi-device.c b/libqmi-glib/qmi-device.c
index 87e1c00..e96c4f7 100644
--- a/libqmi-glib/qmi-device.c
+++ b/libqmi-glib/qmi-device.c
@@ -35,6 +35,7 @@
 #include "qmi-nas.h"
 #include "qmi-wms.h"
 #include "qmi-pds.h"
+#include "qmi-pbm.h"
 #include "qmi-utils.h"
 #include "qmi-error-types.h"
 #include "qmi-enum-types.h"
@@ -284,6 +285,114 @@
 }
 
 /*****************************************************************************/
+/* Version info request */
+
+/**
+ * qmi_device_get_service_version_info_finish:
+ * @self: a #QmiDevice.
+ * @res: a #GAsyncResult.
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with qmi_device_get_service_version_info().
+ *
+ * Returns: a #GArray of #QmiDeviceServiceVersionInfo elements, or #NULL if @error is set. The returned value should be freed with g_array_unref().
+ */
+GArray *
+qmi_device_get_service_version_info_finish (QmiDevice *self,
+                                            GAsyncResult *res,
+                                            GError **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return NULL;
+
+    return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+version_info_ready (QmiClientCtl *client_ctl,
+                    GAsyncResult *res,
+                    GSimpleAsyncResult *simple)
+{
+    GArray *service_list = NULL;
+    GArray *out;
+    QmiMessageCtlGetVersionInfoOutput *output;
+    GError *error = NULL;
+    guint i;
+
+    /* Check result of the async operation */
+    output = qmi_client_ctl_get_version_info_finish (client_ctl, res, &error);
+    if (!output) {
+        g_simple_async_result_take_error (simple, error);
+        g_simple_async_result_complete (simple);
+        g_object_unref (simple);
+        return;
+    }
+
+    /* Check result of the QMI operation */
+    if (!qmi_message_ctl_get_version_info_output_get_result (output, &error)) {
+        qmi_message_ctl_get_version_info_output_unref (output);
+        g_simple_async_result_take_error (simple, error);
+        g_simple_async_result_complete (simple);
+        g_object_unref (simple);
+        return;
+    }
+
+    /* QMI operation succeeded, we can now get the outputs */
+    qmi_message_ctl_get_version_info_output_get_service_list (output, &service_list, NULL);
+    out = g_array_sized_new (FALSE, FALSE, sizeof (QmiDeviceServiceVersionInfo), service_list->len);
+    for (i = 0; i < service_list->len; i++) {
+        QmiMessageCtlGetVersionInfoOutputServiceListService *info;
+        QmiDeviceServiceVersionInfo outinfo;
+
+        info = &g_array_index (service_list,
+                               QmiMessageCtlGetVersionInfoOutputServiceListService,
+                               i);
+        outinfo.service = info->service;
+        outinfo.major_version = info->major_version;
+        outinfo.minor_version = info->minor_version;
+        g_array_append_val (out, outinfo);
+    }
+
+    qmi_message_ctl_get_version_info_output_unref (output);
+    g_simple_async_result_set_op_res_gpointer (simple, out, (GDestroyNotify)g_array_unref);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+}
+
+/**
+ * qmi_device_get_service_version_info:
+ * @self: a #QmiClientCtl.
+ * @timeout: maximum time to wait for the method to complete, in seconds.
+ * @cancellable: a #GCancellable or %NULL.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied.
+ * @user_data: user data to pass to @callback.
+ *
+ * Asynchronously requests the service version information of the device.
+ *
+ * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from.
+ *
+ * You can then call qmi_device_get_service_version_info_finish() to get the result of the operation.
+ */
+void
+qmi_device_get_service_version_info (QmiDevice *self,
+                                     guint timeout,
+                                     GCancellable *cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data)
+{
+    qmi_client_ctl_get_version_info (
+        self->priv->client_ctl,
+        NULL,
+        timeout,
+        cancellable,
+        (GAsyncReadyCallback)version_info_ready,
+        g_simple_async_result_new (G_OBJECT (self),
+                                   callback,
+                                   user_data,
+                                   qmi_device_get_service_version_info));
+}
+
+/*****************************************************************************/
 /* Version info checks (private) */
 
 static const QmiMessageCtlGetVersionInfoOutputServiceListService *
@@ -739,6 +848,10 @@
         ctx->client_type = QMI_TYPE_CLIENT_PDS;
         break;
 
+    case QMI_SERVICE_PBM:
+        ctx->client_type = QMI_TYPE_CLIENT_PBM;
+        break;
+
     default:
         g_simple_async_result_set_error (ctx->result,
                                          QMI_CORE_ERROR,
@@ -1428,9 +1541,9 @@
 }
 
 static void
-version_info_ready (QmiClientCtl *client_ctl,
-                    GAsyncResult *res,
-                    DeviceOpenContext *ctx)
+open_version_info_ready (QmiClientCtl *client_ctl,
+                         GAsyncResult *res,
+                         DeviceOpenContext *ctx)
 {
     GArray *service_list;
     QmiMessageCtlGetVersionInfoOutput *output;
@@ -1450,7 +1563,7 @@
                                                  NULL,
                                                  1,
                                                  ctx->cancellable,
-                                                 (GAsyncReadyCallback)version_info_ready,
+                                                 (GAsyncReadyCallback)open_version_info_ready,
                                                  ctx);
                 return;
             }
@@ -1528,7 +1641,7 @@
                                          NULL,
                                          1,
                                          ctx->cancellable,
-                                         (GAsyncReadyCallback)version_info_ready,
+                                         (GAsyncReadyCallback)open_version_info_ready,
                                          ctx);
         return;
     }
diff --git a/libqmi-glib/qmi-device.h b/libqmi-glib/qmi-device.h
index f00bd03..2912552 100644
--- a/libqmi-glib/qmi-device.h
+++ b/libqmi-glib/qmi-device.h
@@ -171,6 +171,29 @@
                                         GAsyncResult *res,
                                         GError **error);
 
+/**
+ * QmiDeviceServiceVersionInfo:
+ * @service: a #QmiService.
+ * @major_version: major version of the service.
+ * @minor_version: minor version of the service.
+ *
+ * Version information for a service.
+ */
+typedef struct {
+    QmiService service;
+    guint16 major_version;
+    guint16 minor_version;
+} QmiDeviceServiceVersionInfo;
+
+void    qmi_device_get_service_version_info        (QmiDevice *self,
+                                                    guint timeout,
+                                                    GCancellable *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer user_data);
+GArray *qmi_device_get_service_version_info_finish (QmiDevice *self,
+                                                    GAsyncResult *res,
+                                                    GError **error);
+
 G_END_DECLS
 
 #endif /* _LIBQMI_GLIB_QMI_DEVICE_H_ */
diff --git a/libqmi-glib/qmi-enums-pbm.h b/libqmi-glib/qmi-enums-pbm.h
new file mode 100644
index 0000000..2b10872
--- /dev/null
+++ b/libqmi-glib/qmi-enums-pbm.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libqmi-glib -- GLib/GIO based library to control QMI devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012 Google Inc.
+ */
+
+#ifndef _LIBQMI_GLIB_QMI_ENUMS_PBM_H_
+#define _LIBQMI_GLIB_QMI_ENUMS_PBM_H_
+
+#if !defined (__LIBQMI_GLIB_H_INSIDE__) && !defined (LIBQMI_GLIB_COMPILATION)
+#error "Only <libqmi-glib.h> can be included directly."
+#endif
+
+/**
+ * SECTION: qmi-enums-pbm
+ * @title: PBM enumerations and flags
+ *
+ * This section defines enumerations and flags used in the PBM service
+ * interface.
+ */
+
+/*****************************************************************************/
+/* Helper enums for the 'QMI PBM Indication Register' indication */
+
+/**
+ * QmiPbmEventRegistrationFlag:
+ * @QMI_PBM_EVENT_REGISTRATION_FLAG_RECORD_UPDATE: Request indications when records are added/edited/deleted.
+ * @QMI_PBM_EVENT_REGISTRATION_FLAG_PHONEBOOK_READY: Request indications when phonebooks are ready.
+ * @QMI_PBM_EVENT_REGISTRATION_FLAG_EMERGENCY_NUMBER_LIST: Request indications when emergency numbers are changed.
+ * @QMI_PBM_EVENT_REGISTRATION_FLAG_HIDDEN_RECORD_STATUS: Request indications when hidden record status is changed.
+ * @QMI_PBM_EVENT_REGISTRATION_FLAG_AAS_UPDATE: Request indications when Additional number Alpha String records are added/edited/deleted.
+ * @QMI_PBM_EVENT_REGISTRATION_FLAG_GAS_UPDATE: Request indications when Grouping information Alpha String records are added/edited/deleted.
+ *
+ * Flags to use to register to phonebook indications.
+ */
+typedef enum {
+    QMI_PBM_EVENT_REGISTRATION_FLAG_RECORD_UPDATE         = 1 << 0,
+    QMI_PBM_EVENT_REGISTRATION_FLAG_PHONEBOOK_READY       = 1 << 1,
+    QMI_PBM_EVENT_REGISTRATION_FLAG_EMERGENCY_NUMBER_LIST = 1 << 2,
+    QMI_PBM_EVENT_REGISTRATION_FLAG_HIDDEN_RECORD_STATUS  = 1 << 3,
+    QMI_PBM_EVENT_REGISTRATION_FLAG_AAS_UPDATE            = 1 << 4,
+    QMI_PBM_EVENT_REGISTRATION_FLAG_GAS_UPDATE            = 1 << 5,
+} QmiPbmEventRegistrationFlag;
+
+/*****************************************************************************/
+/* Helper enums for the 'Get Capabilities' request */
+
+/**
+ * QmiPbmPhonebookType:
+ * @QMI_PBM_PHONEBOOK_TYPE_ADN: Abbreviated Dialing Number.
+ * @QMI_PBM_PHONEBOOK_TYPE_FDN: Fixed Dialing Number.
+ * @QMI_PBM_PHONEBOOK_TYPE_MSISDN: Mobile Subscriber Integrated Services Digital Network.
+ * @QMI_PBM_PHONEBOOK_TYPE_MBDN: Mail Box Dialing Number.
+ * @QMI_PBM_PHONEBOOK_TYPE_SDN: Service Dialing Number.
+ * @QMI_PBM_PHONEBOOK_TYPE_BDN: Barred Dialing Number.
+ * @QMI_PBM_PHONEBOOK_TYPE_LND: Last Number Dialed.
+ * @QMI_PBM_PHONEBOOK_TYPE_MBN: Mail Box Number.
+ *
+ * Phonebook type.
+ */
+typedef enum {
+    QMI_PBM_PHONEBOOK_TYPE_ADN    = 1 << 0,
+    QMI_PBM_PHONEBOOK_TYPE_FDN    = 1 << 1,
+    QMI_PBM_PHONEBOOK_TYPE_MSISDN = 1 << 2,
+    QMI_PBM_PHONEBOOK_TYPE_MBDN   = 1 << 3,
+    QMI_PBM_PHONEBOOK_TYPE_SDN    = 1 << 4,
+    QMI_PBM_PHONEBOOK_TYPE_BDN    = 1 << 5,
+    QMI_PBM_PHONEBOOK_TYPE_LND    = 1 << 6,
+    QMI_PBM_PHONEBOOK_TYPE_MBN    = 1 << 7,
+} QmiPbmPhonebookType;
+
+/**
+ * QmiPbmSessionType:
+ * @QMI_PBM_SESSION_TYPE_GW_PRIMARY: Access phonebooks under GSM DF (ICC) or USIM application (UICC).
+ * @QMI_PBM_SESSION_TYPE_1X_PRIMARY: Access phonebooks under CDMA DF (ICC) or CSIM application (UICC).
+ * @QMI_PBM_SESSION_TYPE_GW_SECONDARY: Access phonebooks under GSM DF (ICC) or USIM application (UICC). Dual standby.
+ * @QMI_PBM_SESSION_TYPE_1X_SECONDARY: Access phonebooks under CDMA DF (ICC) or CSIM application (UICC). Dual standby.
+ * @QMI_PBM_SESSION_TYPE_NONPROVISIONING_SLOT_1: Access phonebooks under a nonprovisioning application in slot 1.
+ * @QMI_PBM_SESSION_TYPE_NONPROVISIONING_SLOT_2: Access phonebooks under a nonprovisioning application in slot 2.
+ * @QMI_PBM_SESSION_TYPE_GLOBAL_PHONEBOOK_SLOT_1: Access phonebooks that are not in any application of the card in slot 1.
+ * @QMI_PBM_SESSION_TYPE_GLOBAL_PHONEBOOK_SLOT_2: Access phonebooks that are not in any application of the card in slot 2.
+ *
+ * Type of phonebook management session.
+ */
+typedef enum {
+    QMI_PBM_SESSION_TYPE_GW_PRIMARY              = 0,
+    QMI_PBM_SESSION_TYPE_1X_PRIMARY              = 1,
+    QMI_PBM_SESSION_TYPE_GW_SECONDARY            = 2,
+    QMI_PBM_SESSION_TYPE_1X_SECONDARY            = 3,
+    QMI_PBM_SESSION_TYPE_NONPROVISIONING_SLOT_1  = 4,
+    QMI_PBM_SESSION_TYPE_NONPROVISIONING_SLOT_2  = 5,
+    QMI_PBM_SESSION_TYPE_GLOBAL_PHONEBOOK_SLOT_1 = 6,
+    QMI_PBM_SESSION_TYPE_GLOBAL_PHONEBOOK_SLOT_2 = 7,
+} QmiPbmSessionType;
+
+#endif /* _LIBQMI_GLIB_QMI_ENUMS_PBM_H_ */