/**
   * Retrieves an album from the provider, and put it in the cache
   *
   * @param ref The reference to the album
   * @param provider The provider from which retrieve the album
   * @return The album, or null if the provider says so
   */
  public Playlist retrievePlaylist(final String ref, final ProviderIdentifier provider) {
    if (ref == null) {
      // Force get stack trace
      try {
        throw new RuntimeException();
      } catch (RuntimeException e) {
        Log.e(TAG, "retrievePlaylist called with a null reference", e);
      }
      return null;
    }

    // Try from cache
    Playlist output = mCache.getPlaylist(ref);

    if (output == null && provider != null) {
      ProviderConnection pc = PluginsLookup.getDefault().getProvider(provider);
      if (pc != null) {
        IMusicProvider binder = pc.getBinder();

        if (binder != null) {
          try {
            output = binder.getPlaylist(ref);
            onPlaylistAddedOrUpdated(provider, output);
          } catch (RemoteException e) {
            Log.e(TAG, "Unable to retrieve the playlist", e);
          }
        }
      }
    }

    return output;
  }
  /**
   * Called by the providers when a Playlist has been added or updated. The app's providers
   * syndicator will automatically update the local cache of playlists based on the playlist name.
   */
  @Override
  public void onPlaylistAddedOrUpdated(final ProviderIdentifier provider, final Playlist p)
      throws RemoteException {
    if (p == null || p.getRef() == null) {
      Log.w(TAG, "Provider returned a null playlist or a null-ref playlist");
      return;
    }

    try {
      // We compare the provided copy with the one we have in cache. We only notify the callbacks
      // if it indeed changed.
      Playlist cached = mCache.getPlaylist(p.getRef());

      boolean notify;
      if (cached == null) {
        mCache.putPlaylist(provider, p);
        cached = p;
        notify = true;
      } else {
        notify = !cached.isIdentical(p);

        // If the playlist isn't identical, update it
        if (notify) {
          // Update the name
          cached.setName(p.getName());

          if (p.getName() == null) {
            Log.w(TAG, "Playlist " + p.getRef() + " updated, but name is null!");
          }

          cached.setIsLoaded(p.isLoaded());

          // Empty the playlist
          while (cached.getSongsCount() > 0) {
            cached.removeSong(0);
          }

          // Re-add the songs to it
          Iterator<String> songIt = p.songs();
          while (songIt.hasNext()) {
            cached.addSong(songIt.next());
          }

          // Set offline information
          cached.setOfflineCapable(p.isOfflineCapable());
          cached.setOfflineStatus(p.getOfflineStatus());
        }
      }

      final Playlist finalCachedPlaylist = cached;

      // If something has actually changed
      if (notify) {
        mExecutor.execute(
            new Runnable() {
              @Override
              public void run() {
                // First, we try to check if we need information for some of the songs
                // TODO(xplodwild): Is this really needed in a properly designed provider?
                Iterator<String> it = finalCachedPlaylist.songs();
                while (it.hasNext()) {
                  String ref = it.next();
                  retrieveSong(ref, provider);
                }

                // Then we notify the callbacks
                postPlaylistForUpdate(finalCachedPlaylist);
              }
            });
      }
    } catch (Exception e) {
      Log.e(TAG, "FUUUU", e);
    }
  }