Program: 32187
Project: 32187
Buggy source:
@Override public TokensChunkResponse<Event> getEarlierMessages(final String roomId,final String fromToken,final int limit){
  if (null != roomId) {
    LinkedHashMap<String,Event> events=mRoomEvents.get(roomId);
    if ((events == null) || (events.size() == 0)) {
      return null;
    }
    ArrayList<Event> eventsList=new ArrayList<>(events.values());
    ArrayList<Event> subEventsList=new ArrayList<>();
    Collections.reverse(eventsList);
    TokensChunkResponse<Event> response=new TokensChunkResponse<Event>();
    if ((null == fromToken) && (eventsList.size() <= limit)) {
      subEventsList=eventsList;
    }
 else {
      int index=0;
      if (null != fromToken) {
        for (; (index < eventsList.size()) && (!TextUtils.equals(fromToken,eventsList.get(index).mToken)); index++)         ;
        index++;
      }
      if (index < eventsList.size()) {
        for (; index < eventsList.size(); index++) {
          Event event=eventsList.get(index);
          subEventsList.add(event);
          if ((subEventsList.size() >= limit) && (event.mToken != null)) {
            break;
          }
        }
      }
    }
    if (subEventsList.size() == 0) {
      return null;
    }
    response.chunk=subEventsList;
    Event firstEvent=subEventsList.get(0);
    Event lastEvent=subEventsList.get(subEventsList.size() - 1);
    response.start=firstEvent.mToken;
    response.end=lastEvent.mToken;
    if (response.end == null) {
      response.end=mRoomTokens.get(roomId);
    }
    return response;
  }
  return null;
}

Fixed version (ground truth):
@Override public TokensChunkResponse<Event> getEarlierMessages(final String roomId,final String fromToken,final int limit){
  if (null != roomId) {
    LinkedHashMap<String,Event> events=mRoomEvents.get(roomId);
    if ((events == null) || (events.size() == 0)) {
      return null;
    }
    if (TextUtils.equals(mRoomTokens.get(roomId),fromToken)) {
      return null;
    }
    ArrayList<Event> eventsList=new ArrayList<>(events.values());
    ArrayList<Event> subEventsList=new ArrayList<>();
    Collections.reverse(eventsList);
    TokensChunkResponse<Event> response=new TokensChunkResponse<Event>();
    if ((null == fromToken) && (eventsList.size() <= limit)) {
      subEventsList=eventsList;
    }
 else {
      int index=0;
      if (null != fromToken) {
        for (; (index < eventsList.size()) && (!TextUtils.equals(fromToken,eventsList.get(index).mToken)); index++)         ;
        index++;
      }
      if (index < eventsList.size()) {
        for (; index < eventsList.size(); index++) {
          Event event=eventsList.get(index);
          subEventsList.add(event);
          if ((subEventsList.size() >= limit) && (event.mToken != null)) {
            break;
          }
        }
      }
    }
    if (subEventsList.size() == 0) {
      return null;
    }
    response.chunk=subEventsList;
    Event firstEvent=subEventsList.get(0);
    Event lastEvent=subEventsList.get(subEventsList.size() - 1);
    response.start=firstEvent.mToken;
    if (null == lastEvent.mToken) {
      lastEvent.mToken=mRoomTokens.get(roomId);
    }
    response.end=lastEvent.mToken;
    return response;
  }
  return null;
}