Add new idevice_events_subscribe/unsubscribe API with context

The older API idevice_event_subscribe/unsubscribe can only be used
by a single instance. With the addition of a context, is is now possible
to register multiple callback functions in different threads.
For backwards compatibility the old API will still be available for a while
before being removed in a future release.
diff --git a/include/libimobiledevice/libimobiledevice.h b/include/libimobiledevice/libimobiledevice.h
index c3b87cd..6851145 100644
--- a/include/libimobiledevice/libimobiledevice.h
+++ b/include/libimobiledevice/libimobiledevice.h
@@ -94,6 +94,9 @@
 /** Callback to notifiy if a device was added or removed. */
 typedef void (*idevice_event_cb_t) (const idevice_event_t *event, void *user_data);
 
+/** Event subscription context type */
+typedef struct idevice_subscription_context* idevice_subscription_context_t;
+
 /* functions */
 
 /**
@@ -104,9 +107,36 @@
 void idevice_set_debug_level(int level);
 
 /**
- * Register a callback function that will be called when device add/remove
+ * Subscribe a callback function that will be called when device add/remove
  * events occur.
  *
+ * @param context A pointer to a idevice_subscription_context_t that will be
+ *    set upon creation of the subscription. The returned context must be
+ *    passed to idevice_events_unsubscribe() to unsubscribe the callback.
+ * @param callback Callback function to call.
+ * @param user_data Application-specific data passed as parameter
+ *   to the registered callback function.
+ *
+ * @return IDEVICE_E_SUCCESS on success or an error value when an error occurred.
+ */
+idevice_error_t idevice_events_subscribe(idevice_subscription_context_t *context, idevice_event_cb_t callback, void *user_data);
+
+/**
+ * Unsubscribe the event callback function that has been registered with
+ * idevice_events_subscribe().
+ *
+ * @param context A valid context as returned from idevice_events_subscribe().
+ *
+ * @return IDEVICE_E_SUCCESS on success or an error value when an error occurred.
+ */
+idevice_error_t idevice_events_unsubscribe(idevice_subscription_context_t context);
+
+/**
+ * (DEPRECATED) Register a callback function that will be called when device add/remove
+ * events occur.
+ *
+ * @deprecated Use idevice_events_subscribe() instead.
+ *
  * @param callback Callback function to call.
  * @param user_data Application-specific data passed as parameter
  *   to the registered callback function.
@@ -116,9 +146,11 @@
 idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data);
 
 /**
- * Release the event callback function that has been registered with
+ * (DEPRECATED) Release the event callback function that has been registered with
  *  idevice_event_subscribe().
  *
+ * @deprecated Use idevice_events_unsubscribe() instead.
+ *
  * @return IDEVICE_E_SUCCESS on success or an error value when an error occurred.
  */
 idevice_error_t idevice_event_unsubscribe(void);
diff --git a/src/idevice.c b/src/idevice.c
index 22d57e3..c8574fc 100644
--- a/src/idevice.c
+++ b/src/idevice.c
@@ -215,10 +215,17 @@
 #warning No compiler support for constructor/destructor attributes, some features might not be available.
 #endif
 
-static idevice_event_cb_t event_cb = NULL;
+struct idevice_subscription_context {
+	idevice_event_cb_t callback;
+	void *user_data;
+	usbmuxd_subscription_context_t ctx;
+};
+
+static idevice_subscription_context_t event_ctx = NULL;
 
 static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data)
 {
+	idevice_subscription_context_t context = (idevice_subscription_context_t)user_data;
 	idevice_event_t ev;
 
 	ev.event = event->event;
@@ -232,34 +239,66 @@
 		debug_info("Unknown connection type %d", event->device.conn_type);
 	}
 
-	if (event_cb) {
-		event_cb(&ev, user_data);
+	if (context->callback) {
+		context->callback(&ev, context->user_data);
 	}
 }
 
-LIBIMOBILEDEVICE_API idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data)
+LIBIMOBILEDEVICE_API idevice_error_t idevice_events_subscribe(idevice_subscription_context_t *context, idevice_event_cb_t callback, void *user_data)
 {
-	event_cb = callback;
-	int res = usbmuxd_subscribe(usbmux_event_cb, user_data);
+	if (!context || !callback) {
+		return IDEVICE_E_INVALID_ARG;
+	}
+	*context = malloc(sizeof(struct idevice_subscription_context));
+	if (!*context) {
+		debug_info("ERROR: %s: Failed to allocate subscription context\n", __func__);
+		return IDEVICE_E_UNKNOWN_ERROR;
+	}
+	(*context)->callback = callback;
+	(*context)->user_data = user_data;
+	int res = usbmuxd_events_subscribe(&(*context)->ctx, usbmux_event_cb, *context);
 	if (res != 0) {
-		event_cb = NULL;
+		free(*context);
+		*context = NULL;
 		debug_info("ERROR: usbmuxd_subscribe() returned %d!", res);
 		return IDEVICE_E_UNKNOWN_ERROR;
 	}
 	return IDEVICE_E_SUCCESS;
 }
 
-LIBIMOBILEDEVICE_API idevice_error_t idevice_event_unsubscribe(void)
+LIBIMOBILEDEVICE_API idevice_error_t idevice_events_unsubscribe(idevice_subscription_context_t context)
 {
-	event_cb = NULL;
-	int res = usbmuxd_unsubscribe();
+	if (!context) {
+		return IDEVICE_E_INVALID_ARG;
+	}
+	int res = usbmuxd_events_unsubscribe(context->ctx);
 	if (res != 0) {
 		debug_info("ERROR: usbmuxd_unsubscribe() returned %d!", res);
 		return IDEVICE_E_UNKNOWN_ERROR;
 	}
+	if (context == event_ctx) {
+		event_ctx = NULL;
+	}
+	free(context);
 	return IDEVICE_E_SUCCESS;
 }
 
+LIBIMOBILEDEVICE_API idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data)
+{
+	if (event_ctx) {
+		idevice_events_unsubscribe(event_ctx);
+	}
+	return idevice_events_subscribe(&event_ctx, callback, user_data);
+}
+
+LIBIMOBILEDEVICE_API idevice_error_t idevice_event_unsubscribe(void)
+{
+	if (!event_ctx) {
+		return IDEVICE_E_SUCCESS;
+	}
+	return idevice_events_unsubscribe(event_ctx);
+}
+
 LIBIMOBILEDEVICE_API idevice_error_t idevice_get_device_list_extended(idevice_info_t **devices, int *count)
 {
 	usbmuxd_device_info_t *dev_list;
diff --git a/tools/idevicebtlogger.c b/tools/idevicebtlogger.c
index 421ce98..8de6b22 100644
--- a/tools/idevicebtlogger.c
+++ b/tools/idevicebtlogger.c
@@ -440,13 +440,14 @@
 			assert(0);
 			return -2;
 	}
-	idevice_event_subscribe(device_event_cb, NULL);
+	idevice_subscription_context_t context = NULL;
+	idevice_events_subscribe(&context, device_event_cb, NULL);
 
 	while (!quit_flag) {
 		sleep(1);
 	}
 
-	idevice_event_unsubscribe();
+	idevice_events_unsubscribe(context);
 	stop_logging();
 
 	fclose(packetlogger_file);
diff --git a/tools/idevicesyslog.c b/tools/idevicesyslog.c
index f85c7cc..3084b97 100644
--- a/tools/idevicesyslog.c
+++ b/tools/idevicesyslog.c
@@ -709,12 +709,13 @@
 	line_buffer_size = 1024;
 	line = malloc(line_buffer_size);
 
-	idevice_event_subscribe(device_event_cb, NULL);
+	idevice_subscription_context_t context = NULL;
+	idevice_events_subscribe(&context, device_event_cb, NULL);
 
 	while (!quit_flag) {
 		sleep(1);
 	}
-	idevice_event_unsubscribe();
+	idevice_events_unsubscribe(context);
 	stop_logging();
 
 	if (num_proc_filters > 0) {