exit method

Attempts to leave kiosk mode.

Implementation

Future<DVKioskExitResult> exit(DVKioskExitRequest request) async {
  // A build with no kiosk policy has no kiosk runtime: the call exists,
  // reports, and changes nothing.
  if (!policy.enabled) {
    return const DVKioskExitResult(
      granted: false,
      message: 'This build declares no kiosk policy, so there is nothing to '
          'exit.',
      degradation: DVKioskDegradation.noPolicy,
    );
  }

  if (isLockedOut) {
    return const DVKioskExitResult(
      granted: false,
      message: 'Too many attempts. Try again later.',
      degradation: DVKioskDegradation.lockedOut,
    );
  }
  // The lockout has run out, so the next attempt starts clean.
  if (_lockedUntil != null) {
    _lockedUntil = null;
    _failedAttempts = 0;
    if (state.value == DVKioskState.locked) {
      _enter(DVKioskState.active);
    }
  }

  // The declared method is the only way out. Accepting a PIN against an
  // adminAuth policy would make the declaration advisory.
  final bool methodMatches = switch (policy.exitMethod) {
    DVKioskExitMethod.pin ||
    DVKioskExitMethod.gesturePin =>
      request.method == DVKioskExitMethod.pin,
    DVKioskExitMethod.adminAuth =>
      request.method == DVKioskExitMethod.adminAuth,
    DVKioskExitMethod.remote => request.method == DVKioskExitMethod.remote,
    DVKioskExitMethod.hardwareCombo => false,
    DVKioskExitMethod.none => false,
  };
  if (!methodMatches) {
    return const DVKioskExitResult(
      granted: false,
      message: 'That is not how this kiosk is left.',
    );
  }

  final bool ok = await _accepts(request);
  if (ok) {
    // Forgotten on success, or two wrong entries today and one tomorrow
    // lock a kiosk out for no reason anyone can see.
    _failedAttempts = 0;
    _lockedUntil = null;
    _enter(DVKioskState.staffMode);
    return const DVKioskExitResult(granted: true, message: 'Staff mode.');
  }

  _failedAttempts++;
  if (_failedAttempts >= policy.maxAttempts) {
    _lockedUntil = _clock().add(policy.lockoutFor);
    _enter(DVKioskState.locked);
    return const DVKioskExitResult(
      granted: false,
      message: 'Too many attempts. Try again later.',
      degradation: DVKioskDegradation.lockedOut,
    );
  }
  // Never says what the right answer was, or how close this one came.
  return const DVKioskExitResult(
    granted: false,
    message: 'That did not work.',
  );
}