From 4d5c34e11a4b72b964dbee48e426c982894f46fe Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 24 Dec 2019 01:01:04 +0100 Subject: [PATCH] Introduce an early reporting mechanism for verify and match It is a good idea to report match results early, to e.g. log in a user immediately even if more device interaction is needed. Add new _full variants for the verify/identify functions, with a corresponding callback. Also move driver result reporting into new fpi_device_{identify,verify}_report functions and remove the reporting from the fpi_device_{identify,verify}_complete calls. Basic updates to code is done in places. Only the upekts driver is actually modified from a behaviour point of view. The image driver code should be restructured quite a bit to split the reporting and only report completion after device deactivation. This should simplifiy the code quite a bit again. --- doc/libfprint-sections.txt | 3 + examples/verify.c | 2 + libfprint/drivers/synaptics/synaptics.c | 17 +- libfprint/drivers/upekts.c | 23 +- libfprint/fp-device-private.h | 17 ++ libfprint/fp-device.c | 54 ++++- libfprint/fp-device.h | 30 +++ libfprint/fpi-device.c | 278 +++++++++++++++++++----- libfprint/fpi-device.h | 16 +- libfprint/fpi-image-device.c | 8 +- tests/test-device-fake.c | 23 +- tests/test-fpi-device.c | 4 +- tests/virtual-image.py | 12 +- 13 files changed, 385 insertions(+), 102 deletions(-) diff --git a/doc/libfprint-sections.txt b/doc/libfprint-sections.txt index 30a4e9b..ca92190 100644 --- a/doc/libfprint-sections.txt +++ b/doc/libfprint-sections.txt @@ -26,6 +26,7 @@ FpDeviceError fp_device_retry_quark fp_device_error_quark FpEnrollProgress +FpMatchCb fp_device_get_driver fp_device_get_device_id fp_device_get_name @@ -159,6 +160,8 @@ fpi_device_identify_complete fpi_device_capture_complete fpi_device_delete_complete fpi_device_enroll_progress +fpi_device_verify_report +fpi_device_identify_report
diff --git a/examples/verify.c b/examples/verify.c index 7fcc64c..ffbf113 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -152,6 +152,7 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) g_print ("Print loaded. Time to verify!\n"); fp_device_verify (dev, verify_print, NULL, + NULL, NULL, NULL, (GAsyncReadyCallback) on_verify_completed, verify_data); } @@ -205,6 +206,7 @@ start_verification (FpDevice *dev, VerifyData *verify_data) g_print ("Print loaded. Time to verify!\n"); fp_device_verify (dev, verify_print, NULL, + NULL, NULL, NULL, (GAsyncReadyCallback) on_verify_completed, verify_data); } diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 2aac75e..2470ba9 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -592,16 +592,13 @@ verify_msg_cb (FpiDeviceSynaptics *self, if (error) { - fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); + fpi_device_verify_complete (device, error); return; } if (resp == NULL && self->cmd_complete_on_removal) { - fpi_device_verify_complete (device, - GPOINTER_TO_INT (self->cmd_complete_data), - NULL, - error); + fpi_device_verify_complete (device, NULL); return; } @@ -638,21 +635,21 @@ verify_msg_cb (FpiDeviceSynaptics *self, { fp_info ("Print is not in database"); fpi_device_verify_complete (device, - FPI_MATCH_ERROR, - NULL, fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND)); } else { fp_warn ("Verify has failed: %d", resp->result); - fpi_device_verify_complete (device, FPI_MATCH_FAIL, NULL, NULL); + fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); + fpi_device_verify_complete (device, NULL); } break; case BMKT_RSP_VERIFY_OK: fp_info ("Verify was successful! for user: %s finger: %d score: %f", verify_resp->user_id, verify_resp->finger_id, verify_resp->match_result); - fpi_device_verify_complete (device, FPI_MATCH_SUCCESS, NULL, NULL); + fpi_device_verify_report (device, FPI_MATCH_SUCCESS, NULL, NULL); + fpi_device_verify_complete (device, NULL); break; } } @@ -675,8 +672,6 @@ verify (FpDevice *device) if (!parse_print_data (data, &finger, &user_id, &user_id_len)) { fpi_device_verify_complete (device, - FPI_MATCH_ERROR, - NULL, fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); return; } diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index 965b3b2..47903ef 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -832,7 +832,7 @@ initsm_done (FpiSsm *ssm, FpDevice *dev, GError *error) } static FpiSsm * -deinitsm_new (FpDevice *dev) +deinitsm_new (FpDevice *dev, void *user_data) { return fpi_ssm_new (dev, deinitsm_state_handler, DEINITSM_NUM_STATES); } @@ -988,7 +988,7 @@ static void do_enroll_stop (FpDevice *dev, FpPrint *print, GError *error) { EnrollStopData *data = g_new0 (EnrollStopData, 1); - FpiSsm *ssm = deinitsm_new (dev); + FpiSsm *ssm = deinitsm_new (dev, data); data->print = g_object_ref (print); data->error = error; @@ -1225,8 +1225,7 @@ enroll (FpDevice *dev) typedef struct { - FpiMatchResult res; - GError *error; + GError *error; } VerifyStopData; static void @@ -1244,17 +1243,25 @@ verify_stop_deinit_cb (FpiSsm *ssm, FpDevice *dev, GError *error) if (error) fp_warn ("Error deinitializing: %s", error->message); - fpi_device_verify_complete (dev, data->res, NULL, data->error); + if (data->error) + fpi_device_verify_complete (dev, data->error); + else + fpi_device_verify_complete (dev, g_steal_pointer (&error)); + + g_error_free (error); } static void do_verify_stop (FpDevice *dev, FpiMatchResult res, GError *error) { VerifyStopData *data = g_new0 (VerifyStopData, 1); - FpiSsm *ssm = deinitsm_new (dev); + FpiSsm *ssm = deinitsm_new (dev, data); - data->res = res; - data->error = error; + /* Report the error immediately if possible, otherwise delay it. */ + if (!error && error->domain != FP_DEVICE_RETRY) + fpi_device_verify_report (dev, res, NULL, error); + else + data->error = error; fpi_ssm_start (ssm, verify_stop_deinit_cb); fpi_ssm_set_data (ssm, data, (GDestroyNotify) verify_stop_data_free); diff --git a/libfprint/fp-device-private.h b/libfprint/fp-device-private.h index 1a350fe..5bf5954 100644 --- a/libfprint/fp-device-private.h +++ b/libfprint/fp-device-private.h @@ -63,3 +63,20 @@ typedef struct } FpEnrollData; void enroll_data_free (FpEnrollData *enroll_data); + +typedef struct +{ + FpPrint *enrolled_print; /* verify */ + GPtrArray *gallery; /* identify */ + + gboolean result_reported; + FpPrint *match; + FpPrint *print; + GError *error; + + FpMatchCb match_cb; + gpointer match_data; + GDestroyNotify match_destroy; +} FpMatchData; + +void match_data_free (FpMatchData *match_data); diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 634c2cc..3b36ae6 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -809,10 +809,13 @@ fp_device_enroll_finish (FpDevice *device, * @device: a #FpDevice * @enrolled_print: a #FpPrint to verify * @cancellable: (nullable): a #GCancellable, or %NULL + * @match_cb: (nullable) (scope notified): match reporting callback + * @match_data: (closure match_cb): user data for @match_cb + * @match_destroy: (destroy match_data): Destroy notify for @match_data * @callback: the function to call on completion * @user_data: the data to pass to @callback * - * Start an asynchronous operation to close the device. The callback will + * Start an asynchronous operation to verify a print. The callback will * be called once the operation has finished. Retrieve the result with * fp_device_verify_finish(). */ @@ -820,11 +823,15 @@ void fp_device_verify (FpDevice *device, FpPrint *enrolled_print, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, + GDestroyNotify match_destroy, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GTask) task = NULL; FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; task = g_task_new (device, cancellable, callback, user_data); if (g_task_return_error_if_cancelled (task)) @@ -848,9 +855,14 @@ fp_device_verify (FpDevice *device, priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); - g_task_set_task_data (priv->current_task, - g_object_ref (enrolled_print), - g_object_unref); + data = g_new0 (FpMatchData, 1); + data->enrolled_print = g_object_ref (enrolled_print); + data->match_cb = match_cb; + data->match_data = match_data; + data->match_destroy = match_destroy; + + // Attach the match data as task data so that it is destroyed + g_task_set_task_data (priv->current_task, data, (GDestroyNotify) match_data_free); FP_DEVICE_GET_CLASS (device)->verify (device); } @@ -886,7 +898,11 @@ fp_device_verify_finish (FpDevice *device, if (print) { - *print = g_object_get_data (G_OBJECT (result), "print"); + FpMatchData *data; + + data = g_task_get_task_data (G_TASK (result)); + + *print = data->print; if (*print) g_object_ref (*print); } @@ -902,6 +918,9 @@ fp_device_verify_finish (FpDevice *device, * @device: a #FpDevice * @prints: (element-type FpPrint) (transfer none): #GPtrArray of #FpPrint * @cancellable: (nullable): a #GCancellable, or %NULL + * @match_cb: (nullable) (scope notified): match reporting callback + * @match_data: (closure match_cb): user data for @match_cb + * @match_destroy: (destroy match_data): Destroy notify for @match_data * @callback: the function to call on completion * @user_data: the data to pass to @callback * @@ -913,11 +932,15 @@ void fp_device_identify (FpDevice *device, GPtrArray *prints, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, + GDestroyNotify match_destroy, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GTask) task = NULL; FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; task = g_task_new (device, cancellable, callback, user_data); if (g_task_return_error_if_cancelled (task)) @@ -941,9 +964,14 @@ fp_device_identify (FpDevice *device, priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); - g_task_set_task_data (priv->current_task, - g_ptr_array_ref (prints), - (GDestroyNotify) g_ptr_array_unref); + data = g_new0 (FpMatchData, 1); + data->gallery = g_ptr_array_ref (prints); + data->match_cb = match_cb; + data->match_data = match_data; + data->match_destroy = match_destroy; + + // Attach the match data as task data so that it is destroyed + g_task_set_task_data (priv->current_task, data, (GDestroyNotify) match_data_free); FP_DEVICE_GET_CLASS (device)->identify (device); } @@ -974,15 +1002,19 @@ fp_device_identify_finish (FpDevice *device, FpPrint **print, GError **error) { + FpMatchData *data; + + data = g_task_get_task_data (G_TASK (result)); + if (print) { - *print = g_object_get_data (G_OBJECT (result), "print"); + *print = data->print; if (*print) g_object_ref (*print); } if (match) { - *match = g_object_get_data (G_OBJECT (result), "match"); + *match = data->match; if (*match) g_object_ref (*match); } @@ -1332,6 +1364,7 @@ fp_device_verify_sync (FpDevice *device, fp_device_verify (device, enrolled_print, cancellable, + NULL, NULL, NULL, async_result_ready, &task); while (!task) g_main_context_iteration (NULL, TRUE); @@ -1367,6 +1400,7 @@ fp_device_identify_sync (FpDevice *device, fp_device_identify (device, prints, cancellable, + NULL, NULL, NULL, async_result_ready, &task); while (!task) g_main_context_iteration (NULL, TRUE); diff --git a/libfprint/fp-device.h b/libfprint/fp-device.h index 4f7acac..5b7cf86 100644 --- a/libfprint/fp-device.h +++ b/libfprint/fp-device.h @@ -125,6 +125,30 @@ typedef void (*FpEnrollProgress) (FpDevice *device, gpointer user_data, GError *error); +/** + * FpMatchCb: + * @device: a #FpDevice + * @success: Whether a print was retrieved, %FALSE means @error is set + * @match: (nullable) (transfer none): The matching print + * @print: (nullable) (transfer none): The newly scanned print + * @user_data: (nullable) (transfer none): User provided data + * @error: (nullable) (transfer none): #GError or %NULL + * + * Report the result of a match (identify or verify) operation. This callback + * because it makes sense for drivers to wait e.g. on finger removal before + * finishing the operation. However, the success/failure can often be reported + * at an earlier time, and there is no need to make the user wait. + * + * The passed error is guaranteed to be of type %FP_DEVICE_RETRY if set. Actual + * error conditions will not be reported using this function. Such an error may + * still happen even if this callback has been called. + */ +typedef void (*FpMatchCb) (FpDevice *device, + gboolean success, + FpPrint *match, + FpPrint *print, + gpointer user_data, + GError *error); const gchar *fp_device_get_driver (FpDevice *device); const gchar *fp_device_get_device_id (FpDevice *device); @@ -160,12 +184,18 @@ void fp_device_enroll (FpDevice *device, void fp_device_verify (FpDevice *device, FpPrint *enrolled_print, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, + GDestroyNotify match_destroy, GAsyncReadyCallback callback, gpointer user_data); void fp_device_identify (FpDevice *device, GPtrArray *prints, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, + GDestroyNotify match_destroy, GAsyncReadyCallback callback, gpointer user_data); diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c index 8b2ef9d..629fd2e 100644 --- a/libfprint/fpi-device.c +++ b/libfprint/fpi-device.c @@ -420,6 +420,23 @@ enroll_data_free (FpEnrollData *data) g_free (data); } +void +match_data_free (FpMatchData *data) +{ + g_clear_object (&data->print); + g_clear_object (&data->match); + g_clear_error (&data->error); + + if (data->match_destroy) + data->match_destroy (data->match_data); + data->match_data = NULL; + + g_clear_object (&data->enrolled_print); + g_clear_pointer (&data->gallery, g_ptr_array_unref); + + g_free (data); +} + /** * fpi_device_get_enroll_data: * @device: The #FpDevice @@ -476,12 +493,16 @@ fpi_device_get_verify_data (FpDevice *device, FpPrint **print) { FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); + data = g_task_get_task_data (priv->current_task); + g_assert (data); + if (print) - *print = g_task_get_task_data (priv->current_task); + *print = data->enrolled_print; } /** @@ -496,12 +517,16 @@ fpi_device_get_identify_data (FpDevice *device, GPtrArray **prints) { FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); + data = g_task_get_task_data (priv->current_task); + g_assert (data); + if (prints) - *prints = g_task_get_task_data (priv->current_task); + *prints = data->gallery; } /** @@ -596,11 +621,11 @@ fpi_device_action_error (FpDevice *device, break; case FPI_DEVICE_ACTION_VERIFY: - fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); + fpi_device_verify_complete (device, error); break; case FPI_DEVICE_ACTION_IDENTIFY: - fpi_device_identify_complete (device, NULL, NULL, error); + fpi_device_identify_complete (device, error); break; case FPI_DEVICE_ACTION_CAPTURE: @@ -907,67 +932,65 @@ fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error) /** * fpi_device_verify_complete: * @device: The #FpDevice - * @result: The #FpiMatchResult of the operation - * @print: (transfer floating) The scanned #FpPrint * @error: A #GError if result is %FPI_MATCH_ERROR * * Finish an ongoing verify operation. The returned print should be * representing the new scan and not the one passed for verification. + * + * Note that @error should only be set for actual errors. In the case + * of retry errors, report these using fpi_device_verify_report() + * and then call this function without any error argument. */ void -fpi_device_verify_complete (FpDevice *device, - FpiMatchResult result, - FpPrint *print, - GError *error) +fpi_device_verify_complete (FpDevice *device, + GError *error) { FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); g_debug ("Device reported verify completion"); + data = g_task_get_task_data (priv->current_task); + clear_device_cancel_action (device); - if (print) - g_object_ref_sink (print); - - g_object_set_data_full (G_OBJECT (priv->current_task), - "print", - print, - g_object_unref); - if (!error) { - if (result != FPI_MATCH_ERROR) + if (!data->result_reported) { - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT, - GINT_TO_POINTER (result)); + g_warning ("Driver reported successful verify complete but did not report the result earlier. Reporting error instead"); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + } + else if (data->error) + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, g_steal_pointer (&data->error)); } else { - g_warning ("Driver did not provide an error for a failed verify operation!"); - error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Driver failed to provide an error!"); - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT, + GINT_TO_POINTER (data->match != NULL ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL)); } } else { - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - if (result != FPI_MATCH_ERROR) + /* Replace a retry error with a general error, this is a driver bug. */ + if (error->domain == FP_DEVICE_RETRY) { - g_warning ("Driver passed an error but also provided a match result, returning error!"); - g_object_unref (print); + g_warning ("Driver reported a retry error to fpi_device_verify_complete; reporting operation failure instead!"); + g_clear_error (&error); + error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); } + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); } } /** * fpi_device_identify_complete: * @device: The #FpDevice - * @match: (transfer none): The matching #FpPrint from the passed gallery, or %NULL if none matched - * @print: (transfer floating): The scanned #FpPrint, may be %NULL * @error: The #GError or %NULL on success * * Finish an ongoing identify operation. The match that was identified is @@ -976,46 +999,47 @@ fpi_device_verify_complete (FpDevice *device, */ void fpi_device_identify_complete (FpDevice *device, - FpPrint *match, - FpPrint *print, GError *error) { FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); g_debug ("Device reported identify completion"); + data = g_task_get_task_data (priv->current_task); + clear_device_cancel_action (device); - if (match) - g_object_ref (match); - - if (print) - g_object_ref_sink (print); - - g_object_set_data_full (G_OBJECT (priv->current_task), - "print", - print, - g_object_unref); - g_object_set_data_full (G_OBJECT (priv->current_task), - "match", - match, - g_object_unref); if (!error) { - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, - GUINT_TO_POINTER (TRUE)); + if (!data->result_reported) + { + g_warning ("Driver reported successful identify complete but did not report the result earlier. Reporting error instead"); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + } + else if (data->error) + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, g_steal_pointer (&data->error)); + } + else + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT, GINT_TO_POINTER (TRUE)); + } } else { - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - if (match) + /* Replace a retry error with a general error, this is a driver bug. */ + if (error->domain == FP_DEVICE_RETRY) { - g_warning ("Driver passed an error but also provided a match result, returning error!"); - g_clear_object (&match); + g_warning ("Driver reported a retry error to fpi_device_identify_complete; reporting operation failure instead!"); + g_clear_error (&error); + error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); } + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); } } @@ -1187,3 +1211,151 @@ fpi_device_enroll_progress (FpDevice *device, g_clear_error (&error); g_clear_object (&print); } + +/** + * fpi_device_verify_report: + * @device: The #FpDevice + * @result: The #FpiMatchResult of the operation + * @print: (transfer floating) The scanned #FpPrint + * @error: A #GError if result is %FPI_MATCH_ERROR + * + * Report the result of a verify operation. Note that the passed @error must be + * a retry error with the %FP_DEVICE_RETRY domain. For all other error cases, + * the error should passed to fpi_device_verify_complete(). + */ +void +fpi_device_verify_report (FpDevice *device, + FpiMatchResult result, + FpPrint *print, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data = g_task_get_task_data (priv->current_task); + gboolean call_cb = TRUE; + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); + g_return_if_fail (data->result_reported == FALSE); + + data->result_reported = TRUE; + + g_debug ("Device reported verify result"); + + if (print) + print = g_object_ref_sink (print); + + if (error || result == FPI_MATCH_ERROR) + { + if (result != FPI_MATCH_ERROR) + g_warning ("Driver reported an error code without setting match result to error!"); + + if (error == NULL) + { + g_warning ("Driver reported an error without specifying a retry code, assuming general retry error!"); + error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + } + + if (print) + { + g_warning ("Driver reported a print together with an error!"); + g_clear_object (&print); + } + + data->error = error; + + if (error->domain != FP_DEVICE_RETRY) + { + g_warning ("Driver reported a verify error that was not in the retry domain, delaying report!"); + call_cb = FALSE; + } + } + else + { + if (result == FPI_MATCH_SUCCESS) + { + fpi_device_get_verify_data (device, &data->match); + g_object_ref (data->match); + } + + data->print = g_steal_pointer (&print); + } + + if (call_cb && data->match_cb) + data->match_cb (device, data->error == NULL, data->match, data->print, data->match_data, data->error); +} + +/** + * fpi_device_identify_report: + * @device: The #FpDevice + * @match: (transfer none): The #FpPrint from the gallery that matched + * @print: (transfer floating): The scanned #FpPrint + * @error: A #GError if result is %FPI_MATCH_ERROR + * + * Report the result of a identify operation. Note that the passed @error must be + * a retry error with the %FP_DEVICE_RETRY domain. For all other error cases, + * the error should passed to fpi_device_identify_complete(). + */ +void +fpi_device_identify_report (FpDevice *device, + FpPrint *match, + FpPrint *print, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data = g_task_get_task_data (priv->current_task); + gboolean call_cb = TRUE; + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); + g_return_if_fail (data->result_reported == FALSE); + + data->result_reported = TRUE; + + if (match) + g_object_ref (match); + + if (print) + print = g_object_ref_sink (print); + + if (match && !g_ptr_array_find (data->gallery, match, NULL)) + { + g_warning ("Driver reported a match to a print that was not in the gallery, ignoring match."); + g_clear_object (&match); + } + + g_debug ("Device reported identify result"); + + if (error) + { + if (match != NULL) + { + g_warning ("Driver reported an error code but also provided a match!"); + g_clear_object (&match); + } + + if (print) + { + g_warning ("Driver reported a print together with an error!"); + g_clear_object (&print); + } + + data->error = error; + + if (error->domain != FP_DEVICE_RETRY) + { + g_warning ("Driver reported a verify error that was not in the retry domain, delaying report!"); + call_cb = FALSE; + } + } + else + { + if (match) + data->match = g_steal_pointer (&match); + + if (print) + data->print = g_steal_pointer (&print); + } + + if (call_cb && data->match_cb) + data->match_cb (device, data->error == NULL, data->match, data->print, data->match_data, data->error); +} diff --git a/libfprint/fpi-device.h b/libfprint/fpi-device.h index 3d66ee5..1f53eaf 100644 --- a/libfprint/fpi-device.h +++ b/libfprint/fpi-device.h @@ -229,13 +229,9 @@ void fpi_device_close_complete (FpDevice *device, void fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error); -void fpi_device_verify_complete (FpDevice *device, - FpiMatchResult result, - FpPrint *print, - GError *error); +void fpi_device_verify_complete (FpDevice *device, + GError *error); void fpi_device_identify_complete (FpDevice *device, - FpPrint *match, - FpPrint *print, GError *error); void fpi_device_capture_complete (FpDevice *device, FpImage *image, @@ -250,5 +246,13 @@ void fpi_device_enroll_progress (FpDevice *device, gint completed_stages, FpPrint *print, GError *error); +void fpi_device_verify_report (FpDevice *device, + FpiMatchResult result, + FpPrint *print, + GError *error); +void fpi_device_identify_report (FpDevice *device, + FpPrint *match, + FpPrint *print, + GError *error); G_END_DECLS diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c index f962b8a..0af70ba 100644 --- a/libfprint/fpi-image-device.c +++ b/libfprint/fpi-image-device.c @@ -210,7 +210,9 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g else result = FPI_MATCH_ERROR; - fpi_device_verify_complete (device, result, g_steal_pointer (&print), error); + if (!error || error->domain == FP_DEVICE_RETRY) + fpi_device_verify_report (device, result, g_steal_pointer (&print), g_steal_pointer (&error)); + fpi_device_verify_complete (device, error); fpi_image_device_deactivate (self); } else if (action == FPI_DEVICE_ACTION_IDENTIFY) @@ -231,7 +233,9 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g } } - fpi_device_identify_complete (device, result, g_steal_pointer (&print), error); + if (!error || error->domain == FP_DEVICE_RETRY) + fpi_device_identify_report (device, result, g_steal_pointer (&print), g_steal_pointer (&error)); + fpi_device_identify_complete (device, error); fpi_image_device_deactivate (self); } else diff --git a/tests/test-device-fake.c b/tests/test-device-fake.c index eaa1fa6..84e5e20 100644 --- a/tests/test-device-fake.c +++ b/tests/test-device-fake.c @@ -95,8 +95,16 @@ fpi_device_fake_verify (FpDevice *device) fpi_device_get_verify_data (device, &print); fake_dev->last_called_function = fpi_device_fake_verify; - fpi_device_verify_complete (device, fake_dev->ret_result, print, - fake_dev->ret_error); + + if (!fake_dev->ret_error || fake_dev->ret_error->domain == FP_DEVICE_RETRY) + { + fpi_device_verify_report (device, fake_dev->ret_result, print, fake_dev->ret_error); + fpi_device_verify_complete (device, NULL); + } + else + { + fpi_device_verify_complete (device, fake_dev->ret_error); + } } static void @@ -128,8 +136,15 @@ fpi_device_fake_identify (FpDevice *device) } fake_dev->last_called_function = fpi_device_fake_identify; - fpi_device_identify_complete (device, match, fake_dev->ret_print, - fake_dev->ret_error); + if (!fake_dev->ret_error || fake_dev->ret_error->domain == FP_DEVICE_RETRY) + { + fpi_device_identify_report (device, match, fake_dev->ret_print, fake_dev->ret_error); + fpi_device_identify_complete (device, NULL); + } + else + { + fpi_device_identify_complete (device, fake_dev->ret_error); + } } static void diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 3d1e81c..6af8eb9 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -1166,12 +1166,12 @@ test_driver_complete_actions_errors (void) g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*current_action*failed"); - fpi_device_verify_complete (device, FPI_MATCH_FAIL, NULL, NULL); + fpi_device_verify_complete (device, NULL); g_test_assert_expected_messages (); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*current_action*failed"); - fpi_device_identify_complete (device, NULL, NULL, NULL); + fpi_device_identify_complete (device, NULL); g_test_assert_expected_messages (); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, diff --git a/tests/virtual-image.py b/tests/virtual-image.py index 11ec8ae..1fcb30a 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -220,7 +220,7 @@ class VirtualImage(unittest.TestCase): self._verify_match = None self._verify_fp = None - self.dev.verify(fp_whorl, None, verify_cb) + self.dev.verify(fp_whorl, callback=verify_cb) self.send_image('whorl') while self._verify_match is None: ctx.iteration(True) @@ -228,7 +228,7 @@ class VirtualImage(unittest.TestCase): self._verify_match = None self._verify_fp = None - self.dev.verify(fp_whorl, None, verify_cb) + self.dev.verify(fp_whorl, callback=verify_cb) self.send_image('tented_arch') while self._verify_match is None: ctx.iteration(True) @@ -250,14 +250,14 @@ class VirtualImage(unittest.TestCase): self._identify_match, self._identify_fp = self.dev.identify_finish(res) self._identify_fp = None - self.dev.identify([fp_whorl, fp_tented_arch], None, identify_cb) + self.dev.identify([fp_whorl, fp_tented_arch], callback=identify_cb) self.send_image('tented_arch') while self._identify_fp is None: ctx.iteration(True) assert(self._identify_match is fp_tented_arch) self._identify_fp = None - self.dev.identify([fp_whorl, fp_tented_arch], None, identify_cb) + self.dev.identify([fp_whorl, fp_tented_arch], callback=identify_cb) self.send_image('whorl') while self._identify_fp is None: ctx.iteration(True) @@ -290,7 +290,7 @@ class VirtualImage(unittest.TestCase): self._verify_match = None self._verify_fp = None - self.dev.verify(fp_whorl_new, None, verify_cb) + self.dev.verify(fp_whorl_new, callback=verify_cb) self.send_image('whorl') while self._verify_match is None: ctx.iteration(True) @@ -298,7 +298,7 @@ class VirtualImage(unittest.TestCase): self._verify_match = None self._verify_fp = None - self.dev.verify(fp_whorl_new, None, verify_cb) + self.dev.verify(fp_whorl_new, callback=verify_cb) self.send_image('tented_arch') while self._verify_match is None: ctx.iteration(True)