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

Change-Id: Ibb43d893d888a424cee14728613dabebd41e82d9
diff --git a/docs/reference/libqmi-glib/libqmi-glib-common.sections b/docs/reference/libqmi-glib/libqmi-glib-common.sections
index 5258c1e..d194120 100644
--- a/docs/reference/libqmi-glib/libqmi-glib-common.sections
+++ b/docs/reference/libqmi-glib/libqmi-glib-common.sections
@@ -48,6 +48,7 @@
 QMI_DEVICE_PROXY_PATH
 QMI_DEVICE_WWAN_IFACE
 QMI_DEVICE_SIGNAL_INDICATION
+QMI_DEVICE_SIGNAL_REMOVED
 QmiDevice
 QmiDeviceOpenFlags
 QmiDeviceReleaseClientFlags
diff --git a/src/libqmi-glib/qmi-device.c b/src/libqmi-glib/qmi-device.c
index 841bcb4..e98f3ca 100644
--- a/src/libqmi-glib/qmi-device.c
+++ b/src/libqmi-glib/qmi-device.c
@@ -76,6 +76,7 @@
 
 enum {
     SIGNAL_INDICATION,
+    SIGNAL_REMOVED,
     SIGNAL_LAST
 };
 
@@ -1555,13 +1556,14 @@
             g_error_free (error);
         /* Close the device */
         qmi_device_close (self, NULL);
-        return FALSE;
+        return G_SOURCE_REMOVE;
     }
 
     if (r == 0) {
         /* HUP! */
         g_warning ("Cannot read from istream: connection broken");
-        return FALSE;
+        g_signal_emit (self, signals[SIGNAL_REMOVED], 0);
+        return G_SOURCE_REMOVE;
     }
 
     /* else, r > 0 */
@@ -1571,7 +1573,7 @@
 
     parse_response (self);
 
-    return TRUE;
+    return G_SOURCE_CONTINUE;
 }
 
 typedef struct {
@@ -1623,7 +1625,6 @@
                            self,
                            NULL);
     g_source_attach (self->priv->input_source, g_main_context_get_thread_default ());
-    g_source_unref (self->priv->input_source);
 
     g_task_return_boolean (task, TRUE);
     g_object_unref (task);
@@ -2407,7 +2408,10 @@
 static void
 destroy_iostream (QmiDevice *self)
 {
-    g_clear_pointer (&self->priv->input_source, g_source_destroy);
+    if (self->priv->input_source) {
+        g_source_destroy (self->priv->input_source);
+        g_clear_pointer (&self->priv->input_source, g_source_unref);
+    }
     g_clear_pointer (&self->priv->buffer, g_byte_array_unref);
     g_clear_object (&self->priv->istream);
     g_clear_object (&self->priv->ostream);
@@ -3174,4 +3178,24 @@
                       G_TYPE_NONE,
                       1,
                       G_TYPE_BYTE_ARRAY);
+
+    /**
+     * QmiDevice::device-removed:
+     * @object: A #QmiDevice.
+     * @output: none
+     *
+     * The ::device-removed signal is emitted when an unexpected port hang-up is received.
+     *
+     * Since: 1.20
+     */
+    signals[SIGNAL_REMOVED] =
+        g_signal_new (QMI_DEVICE_SIGNAL_REMOVED,
+                      G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
+                      G_SIGNAL_RUN_LAST,
+                      0,
+                      NULL,
+                      NULL,
+                      NULL,
+                      G_TYPE_NONE,
+                      0);
 }
diff --git a/src/libqmi-glib/qmi-device.h b/src/libqmi-glib/qmi-device.h
index 8ff2318..4ece41b 100644
--- a/src/libqmi-glib/qmi-device.h
+++ b/src/libqmi-glib/qmi-device.h
@@ -105,6 +105,15 @@
 #define QMI_DEVICE_SIGNAL_INDICATION "indication"
 
 /**
+ * QMI_DEVICE_REMOVED:
+ *
+ * Symbol defining the #QmiDevice::device-removed signal.
+ *
+ * Since: 1.20
+ */
+#define QMI_DEVICE_SIGNAL_REMOVED "device-removed"
+
+/**
  * QmiDevice:
  *
  * The #QmiDevice structure contains private data and should only be accessed
diff --git a/src/libqmi-glib/qmi-proxy.c b/src/libqmi-glib/qmi-proxy.c
index 0e19cd8..bfb362e 100644
--- a/src/libqmi-glib/qmi-proxy.c
+++ b/src/libqmi-glib/qmi-proxy.c
@@ -98,6 +98,7 @@
     QmiMessage *internal_proxy_open_request;
     GArray *qmi_client_info_array;
     guint indication_id;
+    guint device_removed_id;
 } Client;
 
 static gboolean connection_readable_cb (GSocket *socket, GIOCondition condition, Client *client);
@@ -131,6 +132,8 @@
         if (client->device) {
             if (g_signal_handler_is_connected (client->device, client->indication_id))
                 g_signal_handler_disconnect (client->device, client->indication_id);
+            if (g_signal_handler_is_connected (client->device, client->device_removed_id))
+                g_signal_handler_disconnect (client->device, client->device_removed_id);
             g_object_unref (client->device);
         }
 
@@ -322,6 +325,13 @@
 }
 
 static void
+device_removed_cb (QmiDevice *device,
+                   Client *client)
+{
+    untrack_client (client->proxy, client);
+}
+
+static void
 device_open_ready (QmiDevice *device,
                    GAsyncResult *res,
                    Client *client)
@@ -355,6 +365,10 @@
                                               "indication",
                                               G_CALLBACK (indication_cb),
                                               client);
+    client->device_removed_id = g_signal_connect (client->device,
+                                                  "device-removed",
+                                                  G_CALLBACK (device_removed_cb),
+                                                  client);
 
     complete_internal_proxy_open (self, client);
 
diff --git a/src/qmicli/qmicli-uim.c b/src/qmicli/qmicli-uim.c
index 84df534..ce39e2d 100644
--- a/src/qmicli/qmicli-uim.c
+++ b/src/qmicli/qmicli-uim.c
@@ -43,6 +43,7 @@
 
 /* Options */
 static gchar *read_transparent_str;
+static gchar *read_record_str;
 static gchar *set_pin_protection_str;
 static gchar *verify_pin_str;
 static gchar *unblock_pin_str;
@@ -80,6 +81,10 @@
       "Get the attributes of a given file",
       "[0xNNNN,0xNNNN,...]"
     },
+    { "uim-read-record", 0, 0, G_OPTION_ARG_STRING, &read_record_str,
+      "Read a record from given file (allowed keys: record-number, record-length, file ([0xNNNN-0xNNNN,...])",
+      "[\"key=value,...\"]"
+    },
     { "uim-get-card-status", 0, 0, G_OPTION_ARG_NONE, &get_card_status_flag,
       "Get card status",
       NULL
@@ -136,6 +141,7 @@
                  !!unblock_pin_str +
                  !!change_pin_str +
                  !!read_transparent_str +
+                 !!read_record_str +
                  !!get_file_attributes_str +
                  !!sim_power_on_str +
                  !!sim_power_off_str +
@@ -865,14 +871,15 @@
 }
 
 static gboolean
-get_sim_file_id_and_path (const gchar *file_path_str,
-                          guint16 *file_id,
-                          GArray **file_path)
+get_sim_file_id_and_path_with_separator (const gchar *file_path_str,
+                                         guint16 *file_id,
+                                         GArray **file_path,
+                                         const gchar *separator)
 {
     guint i;
     gchar **split;
 
-    split = g_strsplit (file_path_str, ",", -1);
+    split = g_strsplit (file_path_str, separator, -1);
     if (!split) {
         g_printerr ("error: invalid file path given: '%s'\n", file_path_str);
         return FALSE;
@@ -914,6 +921,14 @@
     return TRUE;
 }
 
+static gboolean
+get_sim_file_id_and_path (const gchar *file_path_str,
+                          guint16 *file_id,
+                          GArray **file_path)
+{
+    return get_sim_file_id_and_path_with_separator (file_path_str, file_id, file_path, ",");
+}
+
 static void
 read_transparent_ready (QmiClientUim *client,
                         GAsyncResult *res)
@@ -1013,6 +1028,175 @@
 }
 
 static void
+read_record_ready (QmiClientUim *client,
+                   GAsyncResult *res)
+{
+    QmiMessageUimReadRecordOutput *output;
+    GError *error = NULL;
+    guint8 sw1 = 0;
+    guint8 sw2 = 0;
+    GArray *read_result = NULL;
+
+    output = qmi_client_uim_read_record_finish (client, res, &error);
+    if (!output) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        operation_shutdown (FALSE);
+        return;
+    }
+
+    if (!qmi_message_uim_read_record_output_get_result (output, &error)) {
+        g_printerr ("error: couldn't read record file from the UIM: %s\n", error->message);
+        g_error_free (error);
+
+        /* Card result */
+        if (qmi_message_uim_read_record_output_get_card_result (
+                output,
+                &sw1,
+                &sw2,
+                NULL)) {
+            g_print ("Card result:\n"
+                     "\tSW1: '0x%02x'\n"
+                     "\tSW2: '0x%02x'\n",
+                     sw1, sw2);
+        }
+
+        qmi_message_uim_read_record_output_unref (output);
+        operation_shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Successfully read information from the UIM:\n",
+             qmi_device_get_path_display (ctx->device));
+
+    /* Card result */
+    if (qmi_message_uim_read_record_output_get_card_result (
+            output,
+            &sw1,
+            &sw2,
+            NULL)) {
+        g_print ("Card result:\n"
+                 "\tSW1: '0x%02x'\n"
+                 "\tSW2: '0x%02x'\n",
+                 sw1, sw2);
+    }
+
+    /* Read result */
+    if (qmi_message_uim_read_record_output_get_read_result (
+            output,
+            &read_result,
+            NULL)) {
+        gchar *str;
+
+        str = qmicli_get_raw_data_printable (read_result, 80, "\t");
+        g_print ("Read result:\n"
+                 "%s\n",
+                 str);
+        g_free (str);
+    }
+
+    qmi_message_uim_read_record_output_unref (output);
+    operation_shutdown (TRUE);
+}
+
+typedef struct {
+    char *file;
+    guint16 record_number;
+    guint16 record_length;
+} SetReadRecordProperties;
+
+static gboolean
+set_read_record_properties_handle (const gchar *key,
+                                   const gchar *value,
+                                   GError     **error,
+                                   gpointer     user_data)
+{
+    SetReadRecordProperties *props = (SetReadRecordProperties *) user_data;
+
+    if (!value || !value[0]) {
+        g_set_error (error,
+                     QMI_CORE_ERROR,
+                     QMI_CORE_ERROR_FAILED,
+                     "key '%s' requires a value",
+                     key);
+        return FALSE;
+    }
+
+    if (g_ascii_strcasecmp (key, "file") == 0) {
+        props->file = strdup(value);
+        return TRUE;
+    }
+
+    if (g_ascii_strcasecmp (key, "record-number") == 0) {
+        props->record_number = (guint16) atoi(value);
+        return TRUE;
+    }
+
+    if (g_ascii_strcasecmp (key, "record-length") == 0) {
+        props->record_length = (guint16) atoi(value);
+        return TRUE;
+    }
+
+    g_set_error (error,
+                 QMI_CORE_ERROR,
+                 QMI_CORE_ERROR_FAILED,
+                 "Unrecognized option '%s'",
+                 key);
+    return FALSE;
+}
+
+static QmiMessageUimReadRecordInput *
+read_record_input_create (const gchar *str)
+{
+    GError *error = NULL;
+    QmiMessageUimReadRecordInput *input = NULL;
+    SetReadRecordProperties props = {
+        .file = NULL,
+        .record_number = 0,
+        .record_length = 0,
+    };
+    guint16 file_id = 0;
+    GArray *file_path = NULL;
+
+    if (!qmicli_parse_key_value_string (str,
+                                        &error,
+                                        set_read_record_properties_handle,
+                                        &props)) {
+        g_printerr ("error: could not parse input string '%s': %s\n",
+                    str,
+                    error->message);
+        g_error_free (error);
+        goto out;
+    }
+
+    if (!get_sim_file_id_and_path_with_separator (props.file, &file_id, &file_path, "-"))
+        goto out;
+
+    input = qmi_message_uim_read_record_input_new ();
+
+    qmi_message_uim_read_record_input_set_session_information (
+        input,
+        QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING,
+        "",
+        NULL);
+    qmi_message_uim_read_record_input_set_file (
+        input,
+        file_id,
+        file_path,
+        NULL);
+    qmi_message_uim_read_record_input_set_record (
+        input,
+        props.record_number,
+        props.record_length,
+        NULL);
+
+out:
+    free (props.file);
+    g_array_unref (file_path);
+    return input;
+}
+
+static void
 get_file_attributes_ready (QmiClientUim *client,
                            GAsyncResult *res,
                            gchar *file_name)
@@ -1293,6 +1477,28 @@
         return;
     }
 
+    /* Request to read a transparent file? */
+    if (read_record_str) {
+        QmiMessageUimReadRecordInput *input;
+
+        input = read_record_input_create (read_record_str);
+        if (!input) {
+            operation_shutdown (FALSE);
+            return;
+        }
+
+        g_debug ("Asynchronously reading record file at '%s'...",
+                 read_record_str);
+        qmi_client_uim_read_record (ctx->client,
+                                    input,
+                                    10,
+                                    ctx->cancellable,
+                                    (GAsyncReadyCallback)read_record_ready,
+                                    NULL);
+        qmi_message_uim_read_record_input_unref (input);
+        return;
+    }
+
     /* Request to get file attributes? */
     if (get_file_attributes_str) {
         QmiMessageUimGetFileAttributesInput *input;