flatpak_dir_list_unused_refs (FlatpakDir         *self,
                              const char         *arch,
                              GHashTable         *metadata_injection,
                              GHashTable         *eol_injection,
                              const char * const *refs_to_exclude,
                              gboolean            filter_by_eol,
                              GCancellable       *cancellable,
                              GError            **error)
{
  g_autoptr(GHashTable) used_refs = NULL;
  g_autoptr(GHashTable) excluded_refs_ht = NULL;
  g_autoptr(GPtrArray) refs =  NULL;
  g_autoptr(GPtrArray) runtime_refs = NULL;

  /* Convert refs_to_exclude to hashtable for fast repeated lookups */
  if (refs_to_exclude)
    {
      excluded_refs_ht = g_hash_table_new_full ((GHashFunc)flatpak_decomposed_hash, (GEqualFunc)flatpak_decomposed_equal, (GDestroyNotify)flatpak_decomposed_unref, NULL);
      for (int i = 0; refs_to_exclude[i] != NULL; i++)
        {
          const char *ref_to_exclude = refs_to_exclude[i];
          g_autoptr(FlatpakDecomposed) d = flatpak_decomposed_new_from_ref (ref_to_exclude, NULL);
          if (d)
            g_hash_table_add (excluded_refs_ht, flatpak_decomposed_ref (d));
        }
    }

  used_refs = g_hash_table_new_full ((GHashFunc)flatpak_decomposed_hash, (GEqualFunc)flatpak_decomposed_equal, (GDestroyNotify)flatpak_decomposed_unref, NULL);

  if (!find_used_refs (self, NULL, arch, metadata_injection, excluded_refs_ht,
                       used_refs, cancellable, error))
    return NULL;

  /* If @self is a system installation, also check the per-user installation
   * for any apps there using runtimes in the system installation or runtimes
   * there with sdks or extensions in the system installation. Only do so if
   * the per-user installation exists; it wouldn't make sense to create it here
   * if not.
   */
  if (!flatpak_dir_is_user (self))
    {
      g_autoptr(FlatpakDir) user_dir = flatpak_dir_get_user ();
      g_autoptr(GError) local_error = NULL;

      if (!find_used_refs (self, user_dir, arch, metadata_injection, excluded_refs_ht,
                           used_refs, cancellable, &local_error))
        {
          /* We may get permission denied if the process is sandboxed with
           * systemd's ProtectHome=
           */
          if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
              !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
            {
              g_propagate_error (error, g_steal_pointer (&local_error));
              return NULL;
            }
        }
    }

  runtime_refs = flatpak_dir_list_refs (self, FLATPAK_KINDS_RUNTIME, cancellable, error);
  if (runtime_refs == NULL)
    return NULL;

  refs = g_ptr_array_new_with_free_func (g_free);

  for (int i = 0; i < runtime_refs->len; i++)
    {
      FlatpakDecomposed *ref = g_ptr_array_index (runtime_refs, i);

      if (g_hash_table_contains (used_refs, ref))
        continue;

      if (arch != NULL && !flatpak_decomposed_is_arch (ref, arch))
        continue;

      if (filter_by_eol)
        {
          gboolean is_eol = FALSE;

          if (eol_injection && g_hash_table_contains (eol_injection, flatpak_decomposed_get_ref (ref)))
            {
              is_eol = GPOINTER_TO_INT (g_hash_table_lookup (eol_injection, ref));
            }
          else
            {
              g_autoptr(GBytes) deploy_data = NULL;

              /* deploy v4 guarantees eol/eolr info */
              deploy_data = flatpak_dir_get_deploy_data (self, ref, 4,
                                                         cancellable, NULL);
              is_eol = deploy_data != NULL &&
                (flatpak_deploy_data_get_eol (deploy_data) != NULL ||
                 flatpak_deploy_data_get_eol_rebase (deploy_data));
            }

          if (!is_eol)
            continue;
        }

      g_ptr_array_add (refs, flatpak_decomposed_dup_ref (ref));
    }

  g_ptr_array_add (refs, NULL);
  return (char **)g_ptr_array_free (g_steal_pointer (&refs), FALSE);
}