diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 6040c1be..002645fc 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -1246,6 +1246,33 @@ match_data_free (FpMatchData *data) g_free (data); } +static void +on_verify_identification (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(FpPrint) match = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) verify_task = G_TASK (g_steal_pointer (&user_data)); + FpDevice *device = FP_DEVICE (source_object); + FpMatchData *match_data; + + if (!fp_device_identify_finish (device, result, &match, &print, &error)) + { + g_task_return_error (verify_task, g_steal_pointer (&error)); + return; + } + + match_data = g_new0 (FpMatchData, 1); + match_data->print = g_steal_pointer (&print); + + g_task_set_task_data (verify_task, g_steal_pointer (&match_data), + (GDestroyNotify) match_data_free); + + g_task_return_int (verify_task, match ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL); +} + /** * fp_device_verify: * @device: a #FpDevice @@ -1294,7 +1321,8 @@ fp_device_verify (FpDevice *device, return; } - if (!cls->verify || !(priv->features & FP_DEVICE_FEATURE_VERIFY)) + if ((!cls->verify && !cls->identify) || + !(priv->features & FP_DEVICE_FEATURE_VERIFY)) { g_task_return_error (task, fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED, @@ -1302,6 +1330,21 @@ fp_device_verify (FpDevice *device, return; } + if (!cls->verify) + { + g_autoptr(GPtrArray) prints = NULL; + + g_assert (cls->identify); + + prints = g_ptr_array_sized_new (1); + g_ptr_array_add (prints, enrolled_print); + + fp_device_identify (device, prints, cancellable, match_cb, match_data, + match_destroy, on_verify_identification, + g_steal_pointer (&task)); + return; + } + fpi_device_update_temp (device, TRUE); if (priv->temp_current == FP_TEMPERATURE_HOT) { diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c index 817c2a0f..fd96d2f3 100644 --- a/libfprint/fpi-device.c +++ b/libfprint/fpi-device.c @@ -127,7 +127,7 @@ fpi_device_class_auto_initialize_features (FpDeviceClass *device_class) device_class->features |= FP_DEVICE_FEATURE_VERIFY; if (device_class->identify) - device_class->features |= FP_DEVICE_FEATURE_IDENTIFY; + device_class->features |= FP_DEVICE_FEATURE_IDENTIFY | FP_DEVICE_FEATURE_VERIFY; if (device_class->list) device_class->features |= FP_DEVICE_FEATURE_STORAGE_LIST; diff --git a/libfprint/fpi-device.h b/libfprint/fpi-device.h index 8d65b691..37cdb2bb 100644 --- a/libfprint/fpi-device.h +++ b/libfprint/fpi-device.h @@ -107,7 +107,8 @@ struct _FpIdEntry * guaranteed to only happen when the device is open (this includes delete). * @close: Close the device again * @enroll: Start an enroll operation - * @verify: Start a verify operation + * @verify: Start a verify operation (it will be implemented via @identify + * if not overriden). * @identify: Start an identify operation * @capture: Start a capture operation * @list: List prints stored on the device diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 33013bc7..27e6705e 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -672,6 +672,7 @@ test_driver_initial_features_no_verify (void) { g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + dev_class->identify = NULL; dev_class->verify = NULL; dev_class->features = FP_DEVICE_FEATURE_NONE; @@ -679,7 +680,7 @@ test_driver_initial_features_no_verify (void) g_assert_cmpuint (dev_class->features, !=, FP_DEVICE_FEATURE_NONE); g_assert_true (dev_class->features & FP_DEVICE_FEATURE_CAPTURE); - g_assert_true (dev_class->features & FP_DEVICE_FEATURE_IDENTIFY); + g_assert_false (dev_class->features & FP_DEVICE_FEATURE_IDENTIFY); g_assert_false (dev_class->features & FP_DEVICE_FEATURE_VERIFY); g_assert_false (dev_class->features & FP_DEVICE_FEATURE_DUPLICATES_CHECK); g_assert_true (dev_class->features & FP_DEVICE_FEATURE_STORAGE); @@ -1719,6 +1720,372 @@ test_driver_verify_complete_retry (void) g_clear_error (&error); } +static void +test_driver_verify_via_identify (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) out_print = NULL; + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + FpiDeviceFake *fake_dev; + gboolean match; + + dev_class->verify = NULL; + + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + enrolled_print = make_fake_print_reffed (device, NULL); + fake_dev->ret_match = enrolled_print; + fake_dev->ret_print = enrolled_print; + + g_assert_true (fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, match_data, + &match, &out_print, &error)); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert_no_error (error); + + g_assert_true (match_data->called); + g_assert_nonnull (match_data->match); + g_assert_true (match_data->print == out_print); + g_assert_true (match_data->match == enrolled_print); + + g_assert (out_print == enrolled_print); + g_assert_true (match); +} + +static void +test_driver_verify_via_identify_fail (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) out_print = NULL; + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + FpiDeviceFake *fake_dev; + gboolean match; + + dev_class->verify = NULL; + + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + + enrolled_print = make_fake_print_reffed (device, g_variant_new_uint64 (3)); + fake_dev->ret_match = NULL; + fake_dev->ret_print = enrolled_print; + g_assert_true (fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, match_data, + &match, &out_print, &error)); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert_no_error (error); + + g_assert_true (match_data->called); + g_assert_no_error (match_data->error); + g_assert_true (match_data->print == out_print); + g_assert_null (match_data->match); + + g_assert (out_print == enrolled_print); + g_assert_false (match); +} + +static void +test_driver_verify_via_identify_retry (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) out_print = NULL; + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + FpiDeviceFake *fake_dev; + gboolean match; + + dev_class->verify = NULL; + + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + enrolled_print = make_fake_print_reffed (device, NULL); + + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, match_data, + &match, &out_print, &error)); + + g_assert_true (match_data->called); + g_assert_null (match_data->match); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_false (match); +} + +static void +test_driver_verify_via_identify_error (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) out_print = NULL; + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + FpiDeviceFake *fake_dev; + gboolean match; + + dev_class->verify = NULL; + + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + enrolled_print = make_fake_print_reffed (device, NULL); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, match_data, + &match, &out_print, &error)); + + g_assert_false (match_data->called); + g_assert_null (match_data->match); + g_assert_no_error (match_data->error); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_false (match); +} + +static void +test_driver_verify_via_identify_not_supported (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) out_print = NULL; + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + FpiDeviceFake *fake_dev; + gboolean match; + + dev_class->verify = NULL; + dev_class->features &= ~FP_DEVICE_FEATURE_VERIFY; + + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + fake_dev->last_called_function = NULL; + + enrolled_print = make_fake_print_reffed (device, g_variant_new_uint64 (3)); + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, match_data, + &match, &out_print, &error)); + + g_assert_null (fake_dev->last_called_function); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED); + + g_assert_false (match_data->called); + g_assert_no_error (match_data->error); + + g_assert_null (out_print); + g_assert_false (match); +} + +static void fake_device_identify_complete_error (FpDevice *device); + +static void +test_driver_verify_via_identify_report_no_callback (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(GError) error = NULL; + FpiDeviceFake *fake_dev; + gboolean match; + + dev_class->verify = NULL; + + dev_class->identify = fake_device_identify_complete_error; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + enrolled_print = make_fake_print_reffed (device, NULL); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a verify error that was not in the retry domain*"); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED); + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, match_data, + &match, &print, &error)); + + g_test_assert_expected_messages (); + + g_assert_false (match_data->called); + g_assert_null (match_data->match); + g_assert_no_error (match_data->error); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_false (match); +} + +static void fake_device_identify_immediate_complete (FpDevice *device); + +static void +test_driver_verify_via_identify_not_reported (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(GError) error = NULL; + + dev_class->verify = NULL; + dev_class->identify = fake_device_identify_immediate_complete; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + enrolled_print = make_fake_print_reffed (device, NULL); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*reported successful identify complete*not report*result*"); + + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, + NULL, NULL, + NULL, NULL, &error)); + + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + + g_test_assert_expected_messages (); +} + +static void +test_driver_verify_via_identify_complete_retry (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(GError) error = NULL; + FpiDeviceFake *fake_dev; + gboolean match; + + dev_class->verify = NULL; + dev_class->identify = fake_device_identify_complete_error; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + enrolled_print = make_fake_print_reffed (device, NULL); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + test_driver_match_data_clear (match_data); + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error)); + + g_assert_true (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_false (match); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_null (print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a retry error to fpi_device_identify_complete" + "*reporting general identification failure*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + fake_dev->user_data = g_error_copy (fake_dev->ret_error); + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error)); + + g_test_assert_expected_messages (); + g_assert_true (error != g_steal_pointer (&fake_dev->ret_error)); + g_steal_pointer (&fake_dev->user_data); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_false (match); + g_assert_null (print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a retry error to fpi_device_identify_complete" + "*reporting general identification failure*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + fake_dev->user_data = g_error_copy (fake_dev->ret_error); + + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error)); + g_test_assert_expected_messages (); + + g_assert_true (error != g_steal_pointer (&fake_dev->ret_error)); + g_steal_pointer (&fake_dev->user_data); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_false (match); + g_assert_null (print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a match to a print that was not in the gallery*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + fake_dev->user_data = fpi_device_error_new (FP_DEVICE_ERROR_PROTO); + fake_dev->ret_match = make_fake_print (device, NULL); + g_object_add_weak_pointer (G_OBJECT (fake_dev->ret_match), + (gpointer) (&fake_dev->ret_match)); + + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error)); + g_test_assert_expected_messages (); + + g_assert_true (error != g_steal_pointer (&fake_dev->ret_error)); + g_steal_pointer (&fake_dev->user_data); + g_object_unref (fake_dev->ret_match); + g_assert_null (fake_dev->ret_match); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + g_assert_false (match); + g_assert_null (print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a print together with an error*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + fake_dev->ret_print = make_fake_print (device, NULL); + g_object_add_weak_pointer (G_OBJECT (fake_dev->ret_print), + (gpointer) (&fake_dev->ret_print)); + + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error)); + g_test_assert_expected_messages (); + + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_true (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_null (fake_dev->ret_print); + g_assert_false (match); + g_assert_null (print); + g_clear_error (&error); +} + static void fake_device_stub_identify (FpDevice *device) { @@ -3453,6 +3820,14 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/verify/report_no_cb", test_driver_verify_report_no_callback); g_test_add_func ("/driver/verify/not_reported", test_driver_verify_not_reported); g_test_add_func ("/driver/verify/complete_retry", test_driver_verify_complete_retry); + g_test_add_func ("/driver/verify/via-identify", test_driver_verify_via_identify); + g_test_add_func ("/driver/verify/via-identify/fail", test_driver_verify_via_identify_fail); + g_test_add_func ("/driver/verify/via-identify/retry", test_driver_verify_via_identify_retry); + g_test_add_func ("/driver/verify/via-identify/error", test_driver_verify_via_identify_error); + g_test_add_func ("/driver/verify/via-identify/not_supported", test_driver_verify_via_identify_not_supported); + g_test_add_func ("/driver/verify/via-identify/report_no_cb", test_driver_verify_via_identify_report_no_callback); + g_test_add_func ("/driver/verify/via-identify/not_reported", test_driver_verify_via_identify_not_reported); + g_test_add_func ("/driver/verify/via-identify/complete_retry", test_driver_verify_via_identify_complete_retry); g_test_add_func ("/driver/identify", test_driver_identify); g_test_add_func ("/driver/identify/fail", test_driver_identify_fail); g_test_add_func ("/driver/identify/retry", test_driver_identify_retry);