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.
This commit is contained in:
Benjamin Berg 2019-12-24 01:01:04 +01:00
parent 8292c449f7
commit 4d5c34e11a
13 changed files with 385 additions and 102 deletions

View file

@ -26,6 +26,7 @@ FpDeviceError
fp_device_retry_quark fp_device_retry_quark
fp_device_error_quark fp_device_error_quark
FpEnrollProgress FpEnrollProgress
FpMatchCb
fp_device_get_driver fp_device_get_driver
fp_device_get_device_id fp_device_get_device_id
fp_device_get_name fp_device_get_name
@ -159,6 +160,8 @@ fpi_device_identify_complete
fpi_device_capture_complete fpi_device_capture_complete
fpi_device_delete_complete fpi_device_delete_complete
fpi_device_enroll_progress fpi_device_enroll_progress
fpi_device_verify_report
fpi_device_identify_report
</SECTION> </SECTION>
<SECTION> <SECTION>

View file

@ -152,6 +152,7 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data)
g_print ("Print loaded. Time to verify!\n"); g_print ("Print loaded. Time to verify!\n");
fp_device_verify (dev, verify_print, NULL, fp_device_verify (dev, verify_print, NULL,
NULL, NULL, NULL,
(GAsyncReadyCallback) on_verify_completed, (GAsyncReadyCallback) on_verify_completed,
verify_data); verify_data);
} }
@ -205,6 +206,7 @@ start_verification (FpDevice *dev, VerifyData *verify_data)
g_print ("Print loaded. Time to verify!\n"); g_print ("Print loaded. Time to verify!\n");
fp_device_verify (dev, verify_print, NULL, fp_device_verify (dev, verify_print, NULL,
NULL, NULL, NULL,
(GAsyncReadyCallback) on_verify_completed, (GAsyncReadyCallback) on_verify_completed,
verify_data); verify_data);
} }

View file

@ -592,16 +592,13 @@ verify_msg_cb (FpiDeviceSynaptics *self,
if (error) if (error)
{ {
fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); fpi_device_verify_complete (device, error);
return; return;
} }
if (resp == NULL && self->cmd_complete_on_removal) if (resp == NULL && self->cmd_complete_on_removal)
{ {
fpi_device_verify_complete (device, fpi_device_verify_complete (device, NULL);
GPOINTER_TO_INT (self->cmd_complete_data),
NULL,
error);
return; return;
} }
@ -638,21 +635,21 @@ verify_msg_cb (FpiDeviceSynaptics *self,
{ {
fp_info ("Print is not in database"); fp_info ("Print is not in database");
fpi_device_verify_complete (device, fpi_device_verify_complete (device,
FPI_MATCH_ERROR,
NULL,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND)); fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
} }
else else
{ {
fp_warn ("Verify has failed: %d", resp->result); 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; break;
case BMKT_RSP_VERIFY_OK: case BMKT_RSP_VERIFY_OK:
fp_info ("Verify was successful! for user: %s finger: %d score: %f", fp_info ("Verify was successful! for user: %s finger: %d score: %f",
verify_resp->user_id, verify_resp->finger_id, verify_resp->match_result); 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; break;
} }
} }
@ -675,8 +672,6 @@ verify (FpDevice *device)
if (!parse_print_data (data, &finger, &user_id, &user_id_len)) if (!parse_print_data (data, &finger, &user_id, &user_id_len))
{ {
fpi_device_verify_complete (device, fpi_device_verify_complete (device,
FPI_MATCH_ERROR,
NULL,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
return; return;
} }

View file

@ -832,7 +832,7 @@ initsm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
} }
static FpiSsm * static FpiSsm *
deinitsm_new (FpDevice *dev) deinitsm_new (FpDevice *dev, void *user_data)
{ {
return fpi_ssm_new (dev, deinitsm_state_handler, DEINITSM_NUM_STATES); 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) do_enroll_stop (FpDevice *dev, FpPrint *print, GError *error)
{ {
EnrollStopData *data = g_new0 (EnrollStopData, 1); EnrollStopData *data = g_new0 (EnrollStopData, 1);
FpiSsm *ssm = deinitsm_new (dev); FpiSsm *ssm = deinitsm_new (dev, data);
data->print = g_object_ref (print); data->print = g_object_ref (print);
data->error = error; data->error = error;
@ -1225,8 +1225,7 @@ enroll (FpDevice *dev)
typedef struct typedef struct
{ {
FpiMatchResult res; GError *error;
GError *error;
} VerifyStopData; } VerifyStopData;
static void static void
@ -1244,17 +1243,25 @@ verify_stop_deinit_cb (FpiSsm *ssm, FpDevice *dev, GError *error)
if (error) if (error)
fp_warn ("Error deinitializing: %s", error->message); 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 static void
do_verify_stop (FpDevice *dev, FpiMatchResult res, GError *error) do_verify_stop (FpDevice *dev, FpiMatchResult res, GError *error)
{ {
VerifyStopData *data = g_new0 (VerifyStopData, 1); VerifyStopData *data = g_new0 (VerifyStopData, 1);
FpiSsm *ssm = deinitsm_new (dev); FpiSsm *ssm = deinitsm_new (dev, data);
data->res = res; /* Report the error immediately if possible, otherwise delay it. */
data->error = error; 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_start (ssm, verify_stop_deinit_cb);
fpi_ssm_set_data (ssm, data, (GDestroyNotify) verify_stop_data_free); fpi_ssm_set_data (ssm, data, (GDestroyNotify) verify_stop_data_free);

View file

@ -63,3 +63,20 @@ typedef struct
} FpEnrollData; } FpEnrollData;
void enroll_data_free (FpEnrollData *enroll_data); 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);

View file

@ -809,10 +809,13 @@ fp_device_enroll_finish (FpDevice *device,
* @device: a #FpDevice * @device: a #FpDevice
* @enrolled_print: a #FpPrint to verify * @enrolled_print: a #FpPrint to verify
* @cancellable: (nullable): a #GCancellable, or %NULL * @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 * @callback: the function to call on completion
* @user_data: the data to pass to @callback * @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 * be called once the operation has finished. Retrieve the result with
* fp_device_verify_finish(). * fp_device_verify_finish().
*/ */
@ -820,11 +823,15 @@ void
fp_device_verify (FpDevice *device, fp_device_verify (FpDevice *device,
FpPrint *enrolled_print, FpPrint *enrolled_print,
GCancellable *cancellable, GCancellable *cancellable,
FpMatchCb match_cb,
gpointer match_data,
GDestroyNotify match_destroy,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data) gpointer user_data)
{ {
g_autoptr(GTask) task = NULL; g_autoptr(GTask) task = NULL;
FpDevicePrivate *priv = fp_device_get_instance_private (device); FpDevicePrivate *priv = fp_device_get_instance_private (device);
FpMatchData *data;
task = g_task_new (device, cancellable, callback, user_data); task = g_task_new (device, cancellable, callback, user_data);
if (g_task_return_error_if_cancelled (task)) if (g_task_return_error_if_cancelled (task))
@ -848,9 +855,14 @@ fp_device_verify (FpDevice *device,
priv->current_task = g_steal_pointer (&task); priv->current_task = g_steal_pointer (&task);
maybe_cancel_on_cancelled (device, cancellable); maybe_cancel_on_cancelled (device, cancellable);
g_task_set_task_data (priv->current_task, data = g_new0 (FpMatchData, 1);
g_object_ref (enrolled_print), data->enrolled_print = g_object_ref (enrolled_print);
g_object_unref); 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); FP_DEVICE_GET_CLASS (device)->verify (device);
} }
@ -886,7 +898,11 @@ fp_device_verify_finish (FpDevice *device,
if (print) 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) if (*print)
g_object_ref (*print); g_object_ref (*print);
} }
@ -902,6 +918,9 @@ fp_device_verify_finish (FpDevice *device,
* @device: a #FpDevice * @device: a #FpDevice
* @prints: (element-type FpPrint) (transfer none): #GPtrArray of #FpPrint * @prints: (element-type FpPrint) (transfer none): #GPtrArray of #FpPrint
* @cancellable: (nullable): a #GCancellable, or %NULL * @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 * @callback: the function to call on completion
* @user_data: the data to pass to @callback * @user_data: the data to pass to @callback
* *
@ -913,11 +932,15 @@ void
fp_device_identify (FpDevice *device, fp_device_identify (FpDevice *device,
GPtrArray *prints, GPtrArray *prints,
GCancellable *cancellable, GCancellable *cancellable,
FpMatchCb match_cb,
gpointer match_data,
GDestroyNotify match_destroy,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data) gpointer user_data)
{ {
g_autoptr(GTask) task = NULL; g_autoptr(GTask) task = NULL;
FpDevicePrivate *priv = fp_device_get_instance_private (device); FpDevicePrivate *priv = fp_device_get_instance_private (device);
FpMatchData *data;
task = g_task_new (device, cancellable, callback, user_data); task = g_task_new (device, cancellable, callback, user_data);
if (g_task_return_error_if_cancelled (task)) if (g_task_return_error_if_cancelled (task))
@ -941,9 +964,14 @@ fp_device_identify (FpDevice *device,
priv->current_task = g_steal_pointer (&task); priv->current_task = g_steal_pointer (&task);
maybe_cancel_on_cancelled (device, cancellable); maybe_cancel_on_cancelled (device, cancellable);
g_task_set_task_data (priv->current_task, data = g_new0 (FpMatchData, 1);
g_ptr_array_ref (prints), data->gallery = g_ptr_array_ref (prints);
(GDestroyNotify) g_ptr_array_unref); 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); FP_DEVICE_GET_CLASS (device)->identify (device);
} }
@ -974,15 +1002,19 @@ fp_device_identify_finish (FpDevice *device,
FpPrint **print, FpPrint **print,
GError **error) GError **error)
{ {
FpMatchData *data;
data = g_task_get_task_data (G_TASK (result));
if (print) if (print)
{ {
*print = g_object_get_data (G_OBJECT (result), "print"); *print = data->print;
if (*print) if (*print)
g_object_ref (*print); g_object_ref (*print);
} }
if (match) if (match)
{ {
*match = g_object_get_data (G_OBJECT (result), "match"); *match = data->match;
if (*match) if (*match)
g_object_ref (*match); g_object_ref (*match);
} }
@ -1332,6 +1364,7 @@ fp_device_verify_sync (FpDevice *device,
fp_device_verify (device, fp_device_verify (device,
enrolled_print, enrolled_print,
cancellable, cancellable,
NULL, NULL, NULL,
async_result_ready, &task); async_result_ready, &task);
while (!task) while (!task)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
@ -1367,6 +1400,7 @@ fp_device_identify_sync (FpDevice *device,
fp_device_identify (device, fp_device_identify (device,
prints, prints,
cancellable, cancellable,
NULL, NULL, NULL,
async_result_ready, &task); async_result_ready, &task);
while (!task) while (!task)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);

View file

@ -125,6 +125,30 @@ typedef void (*FpEnrollProgress) (FpDevice *device,
gpointer user_data, gpointer user_data,
GError *error); 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_driver (FpDevice *device);
const gchar *fp_device_get_device_id (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, void fp_device_verify (FpDevice *device,
FpPrint *enrolled_print, FpPrint *enrolled_print,
GCancellable *cancellable, GCancellable *cancellable,
FpMatchCb match_cb,
gpointer match_data,
GDestroyNotify match_destroy,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
void fp_device_identify (FpDevice *device, void fp_device_identify (FpDevice *device,
GPtrArray *prints, GPtrArray *prints,
GCancellable *cancellable, GCancellable *cancellable,
FpMatchCb match_cb,
gpointer match_data,
GDestroyNotify match_destroy,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);

View file

@ -420,6 +420,23 @@ enroll_data_free (FpEnrollData *data)
g_free (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: * fpi_device_get_enroll_data:
* @device: The #FpDevice * @device: The #FpDevice
@ -476,12 +493,16 @@ fpi_device_get_verify_data (FpDevice *device,
FpPrint **print) FpPrint **print)
{ {
FpDevicePrivate *priv = fp_device_get_instance_private (device); FpDevicePrivate *priv = fp_device_get_instance_private (device);
FpMatchData *data;
g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); 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) 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) GPtrArray **prints)
{ {
FpDevicePrivate *priv = fp_device_get_instance_private (device); FpDevicePrivate *priv = fp_device_get_instance_private (device);
FpMatchData *data;
g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); 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) 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; break;
case FPI_DEVICE_ACTION_VERIFY: case FPI_DEVICE_ACTION_VERIFY:
fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); fpi_device_verify_complete (device, error);
break; break;
case FPI_DEVICE_ACTION_IDENTIFY: case FPI_DEVICE_ACTION_IDENTIFY:
fpi_device_identify_complete (device, NULL, NULL, error); fpi_device_identify_complete (device, error);
break; break;
case FPI_DEVICE_ACTION_CAPTURE: case FPI_DEVICE_ACTION_CAPTURE:
@ -907,67 +932,65 @@ fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error)
/** /**
* fpi_device_verify_complete: * fpi_device_verify_complete:
* @device: The #FpDevice * @device: The #FpDevice
* @result: The #FpiMatchResult of the operation
* @print: (transfer floating) The scanned #FpPrint
* @error: A #GError if result is %FPI_MATCH_ERROR * @error: A #GError if result is %FPI_MATCH_ERROR
* *
* Finish an ongoing verify operation. The returned print should be * Finish an ongoing verify operation. The returned print should be
* representing the new scan and not the one passed for verification. * 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 void
fpi_device_verify_complete (FpDevice *device, fpi_device_verify_complete (FpDevice *device,
FpiMatchResult result, GError *error)
FpPrint *print,
GError *error)
{ {
FpDevicePrivate *priv = fp_device_get_instance_private (device); FpDevicePrivate *priv = fp_device_get_instance_private (device);
FpMatchData *data;
g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY);
g_debug ("Device reported verify completion"); g_debug ("Device reported verify completion");
data = g_task_get_task_data (priv->current_task);
clear_device_cancel_action (device); 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 (!error)
{ {
if (result != FPI_MATCH_ERROR) if (!data->result_reported)
{ {
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT, g_warning ("Driver reported successful verify complete but did not report the result earlier. Reporting error instead");
GINT_TO_POINTER (result)); 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 else
{ {
g_warning ("Driver did not provide an error for a failed verify operation!"); fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT,
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, GINT_TO_POINTER (data->match != NULL ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL));
"Driver failed to provide an error!");
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
} }
} }
else else
{ {
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); /* Replace a retry error with a general error, this is a driver bug. */
if (result != FPI_MATCH_ERROR) if (error->domain == FP_DEVICE_RETRY)
{ {
g_warning ("Driver passed an error but also provided a match result, returning error!"); g_warning ("Driver reported a retry error to fpi_device_verify_complete; reporting operation failure instead!");
g_object_unref (print); 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: * fpi_device_identify_complete:
* @device: The #FpDevice * @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 * @error: The #GError or %NULL on success
* *
* Finish an ongoing identify operation. The match that was identified is * Finish an ongoing identify operation. The match that was identified is
@ -976,46 +999,47 @@ fpi_device_verify_complete (FpDevice *device,
*/ */
void void
fpi_device_identify_complete (FpDevice *device, fpi_device_identify_complete (FpDevice *device,
FpPrint *match,
FpPrint *print,
GError *error) GError *error)
{ {
FpDevicePrivate *priv = fp_device_get_instance_private (device); FpDevicePrivate *priv = fp_device_get_instance_private (device);
FpMatchData *data;
g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY);
g_debug ("Device reported identify completion"); g_debug ("Device reported identify completion");
data = g_task_get_task_data (priv->current_task);
clear_device_cancel_action (device); 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) if (!error)
{ {
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, if (!data->result_reported)
GUINT_TO_POINTER (TRUE)); {
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 else
{ {
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); /* Replace a retry error with a general error, this is a driver bug. */
if (match) if (error->domain == FP_DEVICE_RETRY)
{ {
g_warning ("Driver passed an error but also provided a match result, returning error!"); g_warning ("Driver reported a retry error to fpi_device_identify_complete; reporting operation failure instead!");
g_clear_object (&match); 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_error (&error);
g_clear_object (&print); 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);
}

View file

@ -229,13 +229,9 @@ void fpi_device_close_complete (FpDevice *device,
void fpi_device_enroll_complete (FpDevice *device, void fpi_device_enroll_complete (FpDevice *device,
FpPrint *print, FpPrint *print,
GError *error); GError *error);
void fpi_device_verify_complete (FpDevice *device, void fpi_device_verify_complete (FpDevice *device,
FpiMatchResult result, GError *error);
FpPrint *print,
GError *error);
void fpi_device_identify_complete (FpDevice *device, void fpi_device_identify_complete (FpDevice *device,
FpPrint *match,
FpPrint *print,
GError *error); GError *error);
void fpi_device_capture_complete (FpDevice *device, void fpi_device_capture_complete (FpDevice *device,
FpImage *image, FpImage *image,
@ -250,5 +246,13 @@ void fpi_device_enroll_progress (FpDevice *device,
gint completed_stages, gint completed_stages,
FpPrint *print, FpPrint *print,
GError *error); 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 G_END_DECLS

View file

@ -210,7 +210,9 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g
else else
result = FPI_MATCH_ERROR; 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); fpi_image_device_deactivate (self);
} }
else if (action == FPI_DEVICE_ACTION_IDENTIFY) 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); fpi_image_device_deactivate (self);
} }
else else

View file

@ -95,8 +95,16 @@ fpi_device_fake_verify (FpDevice *device)
fpi_device_get_verify_data (device, &print); fpi_device_get_verify_data (device, &print);
fake_dev->last_called_function = fpi_device_fake_verify; 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 static void
@ -128,8 +136,15 @@ fpi_device_fake_identify (FpDevice *device)
} }
fake_dev->last_called_function = fpi_device_fake_identify; fake_dev->last_called_function = fpi_device_fake_identify;
fpi_device_identify_complete (device, match, fake_dev->ret_print, if (!fake_dev->ret_error || fake_dev->ret_error->domain == FP_DEVICE_RETRY)
fake_dev->ret_error); {
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 static void

View file

@ -1166,12 +1166,12 @@ test_driver_complete_actions_errors (void)
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*assertion*current_action*failed"); "*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_assert_expected_messages ();
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*assertion*current_action*failed"); "*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_assert_expected_messages ();
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,

View file

@ -220,7 +220,7 @@ class VirtualImage(unittest.TestCase):
self._verify_match = None self._verify_match = None
self._verify_fp = 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') self.send_image('whorl')
while self._verify_match is None: while self._verify_match is None:
ctx.iteration(True) ctx.iteration(True)
@ -228,7 +228,7 @@ class VirtualImage(unittest.TestCase):
self._verify_match = None self._verify_match = None
self._verify_fp = 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') self.send_image('tented_arch')
while self._verify_match is None: while self._verify_match is None:
ctx.iteration(True) ctx.iteration(True)
@ -250,14 +250,14 @@ class VirtualImage(unittest.TestCase):
self._identify_match, self._identify_fp = self.dev.identify_finish(res) self._identify_match, self._identify_fp = self.dev.identify_finish(res)
self._identify_fp = None 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') self.send_image('tented_arch')
while self._identify_fp is None: while self._identify_fp is None:
ctx.iteration(True) ctx.iteration(True)
assert(self._identify_match is fp_tented_arch) assert(self._identify_match is fp_tented_arch)
self._identify_fp = None 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') self.send_image('whorl')
while self._identify_fp is None: while self._identify_fp is None:
ctx.iteration(True) ctx.iteration(True)
@ -290,7 +290,7 @@ class VirtualImage(unittest.TestCase):
self._verify_match = None self._verify_match = None
self._verify_fp = 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') self.send_image('whorl')
while self._verify_match is None: while self._verify_match is None:
ctx.iteration(True) ctx.iteration(True)
@ -298,7 +298,7 @@ class VirtualImage(unittest.TestCase):
self._verify_match = None self._verify_match = None
self._verify_fp = 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') self.send_image('tented_arch')
while self._verify_match is None: while self._verify_match is None:
ctx.iteration(True) ctx.iteration(True)