@Override
  public Cursor query(
      @NonNull Uri uri,
      String[] projection,
      String selection,
      String[] selectionArgs,
      String sortOrder) {
    int match = uriMatcher.match(uri);
    String table;
    String useSelection = selection;
    String[] useSelectionArgs = selectionArgs;
    switch (match) {
      case ALL_ROWS:
        table = uri.getLastPathSegment();
        break;
      case ROW_BY_ID:
        List<String> segments = uri.getPathSegments();
        table = segments.get(TABLE_SEGMENT);
        useSelection = WHERE_MATCHES_ID;
        useSelectionArgs = new String[] {segments.get(ID_SEGMENT)};
        break;
      default:
        throw new UnsupportedOperationException("Unknown uri: " + uri);
    }

    boolean distinct = false;
    String limit = null;

    // If have query parameters, see if any exist interested in.
    if (!TextUtils.isEmpty(uri.getQuery())) {
      distinct = uri.getBooleanQueryParameter(DISTINCT_PARAMETER, false);
      limit = uri.getQueryParameter(LIMIT_PARAMETER);
    }

    SQLiteDatabase db = dbHelper.getReadableDatabase();
    db.acquireReference();

    try {
      Cursor cursor =
          db.query(
              distinct,
              table,
              projection,
              useSelection,
              useSelectionArgs,
              null,
              null,
              sortOrder,
              limit);
      // Register the cursor with the requested URI so the caller will receive
      // future database change notifications. Useful for "loaders" which take advantage
      // of this concept.
      cursor.setNotificationUri(getContext().getContentResolver(), uri);
      return cursor;
    } finally {
      db.releaseReference();
    }
  }