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

@@ -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);
}