Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dfe3d818 authored by Kevin Chyn's avatar Kevin Chyn Committed by Ryan Campbell
Browse files

Fingerprint vts tests.

Fix callback mechanism. Add tests for uncovered interfaces.

Test: make vts && adb push
out/host/linux-x86/vts/android-vts/testcases/DATA/nativetest64/VtsHalBiometricsFingerprintV2_1TargetTest/VtsHalBiometricsFingerprintV2_1TargetTest
/data/local/tmp/ && adb shell
./data/local/tmp/VtsHalBiometricsFingerprintV2_1TargetTest
Bug: 32022721
Change-Id: Ibeb955bfb937d9f23f98a2e073235aa745183e94
parent 8b624737
Loading
Loading
Loading
Loading
+395 −124
Original line number Diff line number Diff line
@@ -23,6 +23,10 @@
#include <hidl/HidlTransportSupport.h>
#include <VtsHalHidlTargetTestBase.h>

#include <cinttypes>
#include <future>
#include <utility>

using android::Condition;
using android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprint;
using android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprintClientCallback;
@@ -34,156 +38,423 @@ using android::hardware::Return;
using android::Mutex;
using android::sp;

class FingerprintHidlTest : public ::testing::VtsHalHidlTargetTestBase {
namespace {

static const uint32_t kTimeout = 3;
static const std::chrono::seconds kTimeoutInSeconds = std::chrono::seconds(kTimeout);
static const uint32_t kGroupId = 99;
static const std::string kTmpDir = "/data/local/tmp/";
static const uint32_t kIterations = 1000;

// Wait for a callback to occur (signaled by the given future) up to the
// provided timeout. If the future is invalid or the callback does not come
// within the given time, returns false.
template<class ReturnType>
bool waitForCallback(
    std::future<ReturnType> future,
    std::chrono::milliseconds timeout = kTimeoutInSeconds) {
  auto expiration = std::chrono::system_clock::now() + timeout;

protected:
    class MyCallback : public IBiometricsFingerprintClientCallback {
  EXPECT_TRUE(future.valid());
  if (future.valid()) {
    std::future_status status = future.wait_until(expiration);
    EXPECT_NE(std::future_status::timeout, status)
        << "Timed out waiting for callback";
    if (status == std::future_status::ready) {
      return true;
    }
  }

  return false;
}

// Base callback implementation that just logs all callbacks by default
class FingerprintCallbackBase : public IBiometricsFingerprintClientCallback {
 public:
  // implement methods of IBiometricsFingerprintClientCallback
        virtual Return<void> onEnrollResult(uint64_t, uint32_t, uint32_t,
                uint32_t) override {
            callBackCalled();
  virtual Return<void> onEnrollResult(uint64_t, uint32_t, uint32_t, uint32_t)
      override {
    ALOGD("Enroll callback called.");
    return Return<void>();
  }

        virtual Return<void> onAcquired(uint64_t, FingerprintAcquiredInfo,
                int32_t) override {
            callBackCalled();
  virtual Return<void> onAcquired(uint64_t, FingerprintAcquiredInfo, int32_t)
      override {
    ALOGD("Acquired callback called.");
    return Return<void>();
  }

  virtual Return<void> onAuthenticated(uint64_t, uint32_t, uint32_t,
      const hidl_vec<uint8_t>&) override {
            callBackCalled();
    ALOGD("Authenticated callback called.");
    return Return<void>();
  }

        virtual Return<void> onError(uint64_t, FingerprintError error, int32_t)
  virtual Return<void> onError(uint64_t, FingerprintError, int32_t)
      override {
            mTestCase->mErr = error;
            callBackCalled();
    ALOGD("Error callback called.");
    EXPECT_TRUE(false);  // fail any test that triggers an error
    return Return<void>();
  }

  virtual Return<void> onRemoved(uint64_t, uint32_t, uint32_t, uint32_t)
      override {
            callBackCalled();
    ALOGD("Removed callback called.");
    return Return<void>();
  }

        virtual Return<void> onEnumerate(uint64_t, uint32_t, uint32_t,
                uint32_t) override {
            callBackCalled();
  virtual Return<void> onEnumerate(uint64_t, uint32_t, uint32_t, uint32_t)
      override {
    ALOGD("Enumerate callback called.");
    return Return<void>();
  }
};

        void callBackCalled () {
            mTestCase->mCallbackCalled = true;
            mTestCase->mCallbackCond.broadcast();
class EnumerateCallback : public FingerprintCallbackBase {
 public:
  virtual Return<void> onEnumerate(uint64_t deviceId, uint32_t fingerId,
      uint32_t groupId, uint32_t remaining) override {
    this->deviceId = deviceId;
    this->fingerId = fingerId;
    this->groupId = groupId;
    this->remaining = remaining;

    if(remaining == 0UL) {
      promise.set_value();
    }
    return Return<void>();
  }

        FingerprintHidlTest* mTestCase;
    public:
        MyCallback(FingerprintHidlTest* aTest) : mTestCase(aTest) {}
  uint64_t deviceId;
  uint32_t fingerId;
  uint32_t groupId;
  uint32_t remaining;
  std::promise<void> promise;
};

    sp<MyCallback> mCallback;
    bool mCallbackCalled;
    Condition mCallbackCond;
    FingerprintError mErr;
    Mutex mLock;
    sp<IBiometricsFingerprint> mService;
    static const unsigned int kThresholdInSeconds = 3;

    void clearErr () {
        mErr = FingerprintError::ERROR_NO_ERROR;
    }

    // Timed callback mechanism.  Will block up to kThresholdInSeconds,
    // returning true if callback was invoked in that time frame.
    bool waitForCallback(const unsigned int seconds = kThresholdInSeconds) {
        nsecs_t reltime = seconds_to_nanoseconds(seconds);
        Mutex::Autolock _l(mLock);
        nsecs_t endTime = systemTime() + reltime;
        while (!mCallbackCalled) {
            if (reltime == 0) {
                mCallbackCond.wait(mLock);
            } else {
                nsecs_t now = systemTime();
                if (now > endTime) {
                    return false;
                }
                mCallbackCond.waitRelative(mLock, endTime - now);
class ErrorCallback : public FingerprintCallbackBase {
 public:
  ErrorCallback(
      bool filterErrors=false,
      FingerprintError errorType=FingerprintError::ERROR_NO_ERROR) {
    this->filterErrors = filterErrors;
    this->errorType = errorType;
  }

  virtual Return<void> onError(uint64_t deviceId, FingerprintError error,
      int32_t vendorCode) override {
    if ((this->filterErrors && this->errorType == error) || !this->filterErrors) {
      this->deviceId = deviceId;
      this->error = error;
      this->vendorCode = vendorCode;
      promise.set_value();
    }
        return true;
    return Return<void>();
  }

  bool filterErrors;
  FingerprintError errorType;
  uint64_t deviceId;
  FingerprintError error;
  int32_t vendorCode;
  std::promise<void> promise;
};

class RemoveCallback : public FingerprintCallbackBase {
 public:
    FingerprintHidlTest (): mCallbackCalled(false) {}
  RemoveCallback(uint32_t groupId) {
    this->removeGroupId = groupId;
  }

  virtual Return<void> onRemoved(uint64_t, uint32_t, uint32_t groupId,
      uint32_t remaining) override {
    EXPECT_EQ(this->removeGroupId, groupId);
    if(remaining == 0UL) {
      promise.set_value();
    }
    return Return<void>();
  }

  uint32_t removeGroupId;
  std::promise<void> promise;
};

class FingerprintHidlTest : public ::testing::VtsHalHidlTargetTestBase {
 public:
  virtual void SetUp() override {
    mService = ::testing::VtsHalHidlTargetTestBase::getService<IBiometricsFingerprint>();
    ASSERT_FALSE(mService == nullptr);

        ASSERT_NE(mService, nullptr);
        clearErr();

        mCallback = new MyCallback(this);
        // TODO: instantly fail any test that receives a death notification
    // Create an active group
    Return<RequestStatus> res = mService->setActiveGroup(kGroupId, kTmpDir);
    ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));
  }

  virtual void TearDown() override {}
};

class FingerprintHidlEnvironment : public ::testing::Environment {
public:
    virtual void SetUp() {}
    virtual void TearDown() {}
  sp<IBiometricsFingerprint> mService;
};


// The service should be reachable.
TEST_F(FingerprintHidlTest, ConnectTest) {
    Return<uint64_t> rc = mService->setNotify(mCallback);
    EXPECT_NE(rc, 0UL);
  sp<FingerprintCallbackBase> cb = new FingerprintCallbackBase();
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0UL, static_cast<uint64_t>(rc));
}

// Starting the service with null callback should succeed.
TEST_F(FingerprintHidlTest, ConnectNullTest) {
  Return<uint64_t> rc = mService->setNotify(NULL);
  ASSERT_NE(0UL, static_cast<uint64_t>(rc));
}

// Pre-enroll should always return unique, cryptographically secure, non-zero number
TEST_F(FingerprintHidlTest, PreEnrollTest) {
  std::map<uint64_t, uint64_t> m;

  for(unsigned int i = 0; i < kIterations; ++i) {
    uint64_t res = static_cast<uint64_t>(mService->preEnroll());
    EXPECT_NE(0UL, res);
    m[res]++;
    EXPECT_EQ(1UL, m[res]);
  }
}

// Enroll with an invalid (all zeroes) HAT should fail.
TEST_F(FingerprintHidlTest, EnrollInvalidHatTest) {
  sp<ErrorCallback> cb = new ErrorCallback();
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0UL, static_cast<uint64_t>(rc));

  uint8_t token[69];
  for(int i=0; i<69; i++) {
    token[i] = 0;
  }

  Return<RequestStatus> res = mService->enroll(token, kGroupId, kTimeout);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // At least one call to onError should occur
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));
  ASSERT_NE(FingerprintError::ERROR_NO_ERROR, cb->error);
}

// Enroll with an invalid (null) HAT should fail.
TEST_F(FingerprintHidlTest, EnrollNullTest) {
  sp<ErrorCallback> cb = new ErrorCallback();
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0UL, static_cast<uint64_t>(rc));

  uint8_t token[69];
  Return<RequestStatus> res = mService->enroll(token, kGroupId, kTimeout);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // At least one call to onError should occur
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));
  ASSERT_NE(FingerprintError::ERROR_NO_ERROR, cb->error);
}

// PostEnroll should always return within 3s
TEST_F(FingerprintHidlTest, PostEnrollTest) {
  sp<FingerprintCallbackBase> cb = new FingerprintCallbackBase();
  Return<uint64_t> rc = mService->setNotify(cb);

  auto start = std::chrono::system_clock::now();
  Return<RequestStatus> res = mService->postEnroll();
  auto elapsed = std::chrono::system_clock::now() - start;
  ASSERT_GE(kTimeoutInSeconds, elapsed);
}

// getAuthenticatorId should always return non-zero numbers
TEST_F(FingerprintHidlTest, GetAuthenticatorIdTest) {
  Return<uint64_t> res = mService->getAuthenticatorId();
  EXPECT_NE(0UL, static_cast<uint64_t>(res));
}

// Enumerate should always trigger onEnumerated(fid=0, rem=0) when there are no fingerprints
TEST_F(FingerprintHidlTest, EnumerateTest) {
  sp<EnumerateCallback> cb = new EnumerateCallback();
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0UL, static_cast<uint64_t>(rc));

  // Callback will return when rem=0 is found
  Return<RequestStatus> res = mService->enumerate();
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));
  EXPECT_EQ(0UL, cb->fingerId);
  EXPECT_EQ(0UL, cb->remaining);

}

// Remove should succeed on any inputs
// At least one callback with "remaining=0" should occur
TEST_F(FingerprintHidlTest, RemoveFingerprintTest) {
  // Register callback
  sp<RemoveCallback> cb = new RemoveCallback(kGroupId);
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0UL, static_cast<uint64_t>(rc));

  // Remove a fingerprint
  Return<RequestStatus> res = mService->remove(kGroupId, 1);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // At least one call to onRemove with remaining=0 should occur
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));
}

// Remove should accept 0 to delete all fingerprints
// At least one callback with "remaining=0" should occur.
TEST_F(FingerprintHidlTest, RemoveAllFingerprintsTest) {
  // Register callback
  sp<RemoveCallback> cb = new RemoveCallback(kGroupId);
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0UL, static_cast<uint64_t>(rc));

  // Remove all fingerprints
  Return<RequestStatus> res = mService->remove(kGroupId, 0);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));
}

// Active group should successfully set to a writable location.
TEST_F(FingerprintHidlTest, SetActiveGroupTest) {
  // Create an active group
  Return<RequestStatus> res = mService->setActiveGroup(2, kTmpDir);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // Reset active group
  res = mService->setActiveGroup(kGroupId, kTmpDir);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));
}

// Active group should fail to set to an unwritable location.
TEST_F(FingerprintHidlTest, SetActiveGroupUnwritableTest) {
  // Create an active group to an unwritable location (device root dir)
  Return<RequestStatus> res = mService->setActiveGroup(3, "/");
  ASSERT_NE(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // Reset active group
  res = mService->setActiveGroup(kGroupId, kTmpDir);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));
}

// Active group should fail to set to a null location.
TEST_F(FingerprintHidlTest, SetActiveGroupNullTest) {
  // Create an active group to a null location.
  Return<RequestStatus> res = mService->setActiveGroup(4, nullptr);
  ASSERT_NE(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // Reset active group
  res = mService->setActiveGroup(kGroupId, kTmpDir);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));
}

// Cancel should always return ERROR_CANCELED from any starting state including
// the IDLE state.
TEST_F(FingerprintHidlTest, CancelTest) {
    Return<uint64_t> rc = mService->setNotify(mCallback);
    EXPECT_NE(rc, 0UL);
  sp<ErrorCallback> cb = new ErrorCallback(true, FingerprintError::ERROR_CANCELED);
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0UL, static_cast<uint64_t>(rc));

  Return<RequestStatus> res = mService->cancel();
    // make sure callback was invoked within kThresholdInSeconds
    EXPECT_EQ(true, waitForCallback());
  // check that we were able to make an IPC request successfully
    EXPECT_EQ(RequestStatus::SYS_OK, res);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // make sure callback was invoked within kTimeoutInSeconds
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));
  // check error should be ERROR_CANCELED
    EXPECT_EQ(FingerprintError::ERROR_CANCELED, mErr);
  ASSERT_EQ(FingerprintError::ERROR_CANCELED, cb->error);
}

// A call to cancel should after any other method call should set the error
// state to canceled.
TEST_F(FingerprintHidlTest, AuthTest) {
    Return<uint64_t> rc = mService->setNotify(mCallback);
    EXPECT_NE(rc, 0UL);
// A call to cancel should succeed during enroll.
TEST_F(FingerprintHidlTest, CancelEnrollTest) {
  Return<RequestStatus> res = mService->setActiveGroup(kGroupId, kTmpDir);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

    Return<RequestStatus> res = mService->authenticate(0, 0);
  sp<ErrorCallback> cb = new ErrorCallback(true, FingerprintError::ERROR_CANCELED);
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0U, static_cast<uint64_t>(rc));

  uint8_t token[69];
  res = mService->enroll(token, kGroupId, kTimeout);
  // check that we were able to make an IPC request successfully
    EXPECT_EQ(RequestStatus::SYS_OK, res);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  res = mService->cancel();
    // make sure callback was invoked within kThresholdInSeconds
    EXPECT_EQ(true, waitForCallback());
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // make sure callback was invoked within kTimeoutInSeconds
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));

  // check error should be ERROR_CANCELED
  ASSERT_EQ(FingerprintError::ERROR_CANCELED, cb->error);
}

// A call to cancel should succeed during authentication.
TEST_F(FingerprintHidlTest, CancelAuthTest) {
  sp<ErrorCallback> cb = new ErrorCallback(true, FingerprintError::ERROR_CANCELED);
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0U, static_cast<uint64_t>(rc));

  Return<RequestStatus> res = mService->authenticate(0, kGroupId);
  // check that we were able to make an IPC request successfully
    EXPECT_EQ(RequestStatus::SYS_OK, res);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  res = mService->cancel();
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // make sure callback was invoked within kTimeoutInSeconds
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));

  // check error should be ERROR_CANCELED
  ASSERT_EQ(FingerprintError::ERROR_CANCELED, cb->error);
}

// A call to cancel should succeed during authentication.
TEST_F(FingerprintHidlTest, CancelRemoveTest) {
  sp<ErrorCallback> cb = new ErrorCallback(true, FingerprintError::ERROR_CANCELED);
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0U, static_cast<uint64_t>(rc));

  // Remove a fingerprint
  Return<RequestStatus> res = mService->remove(kGroupId, 1);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  res = mService->cancel();
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // make sure callback was invoked within kTimeoutInSeconds
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));

  // check error should be ERROR_CANCELED
  ASSERT_EQ(FingerprintError::ERROR_CANCELED, cb->error);
}

// A call to cancel should succeed during authentication.
TEST_F(FingerprintHidlTest, CancelRemoveAllTest) {
  sp<ErrorCallback> cb = new ErrorCallback(true, FingerprintError::ERROR_CANCELED);
  Return<uint64_t> rc = mService->setNotify(cb);
  ASSERT_NE(0U, static_cast<uint64_t>(rc));

  // Remove a fingerprint
  Return<RequestStatus> res = mService->remove(kGroupId, 0);
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  res = mService->cancel();
  ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));

  // make sure callback was invoked within kTimeoutInSeconds
  ASSERT_TRUE(waitForCallback(cb->promise.get_future()));

  // check error should be ERROR_CANCELED
    EXPECT_EQ(FingerprintError::ERROR_CANCELED, mErr);
  ASSERT_EQ(FingerprintError::ERROR_CANCELED, cb->error);
}
}  // anonymous namespace

int main(int argc, char **argv) {
    ::testing::AddGlobalTestEnvironment(new FingerprintHidlEnvironment);
  ::testing::InitGoogleTest(&argc, argv);
  int status = RUN_ALL_TESTS();
  LOG(INFO) << "Test result = " << status;
  return status;
}