@Override
  public void deleteMessage(int aMessageId, int aRequestId) {
    class DeleteMessageRunnable implements Runnable {
      private int mMessageId;
      private int mRequestId;

      DeleteMessageRunnable(int aMessageId, int aRequestId) {
        mMessageId = aMessageId;
        mRequestId = aRequestId;
      }

      @Override
      public void run() {
        try {
          ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
          Uri message = ContentUris.withAppendedId(kSmsContentUri, mMessageId);

          int count = cr.delete(message, null, null);

          if (count > 1) {
            throw new TooManyResultsException();
          }

          notifySmsDeleted(count == 1, mRequestId);
        } catch (TooManyResultsException e) {
          Log.e("GeckoSmsManager", "Delete more than one message?", e);
          notifySmsDeleteFailed(kUnknownError, mRequestId);
        } catch (Exception e) {
          Log.e("GeckoSmsManager", "Error while trying to delete a message", e);
          notifySmsDeleteFailed(kUnknownError, mRequestId);
        }
      }
    }

    if (!SmsIOThread.getInstance().execute(new DeleteMessageRunnable(aMessageId, aRequestId))) {
      Log.e("GeckoSmsManager", "Failed to add GetMessageRunnable to the SmsIOThread");
      notifySmsDeleteFailed(kUnknownError, aRequestId);
    }
  }
 @Override
 public void shutdown() {
   SmsIOThread.getInstance().interrupt();
   MessagesListManager.getInstance().clear();
 }
  @Override
  public void getNextMessageInList(int aListId, int aRequestId) {
    class GetNextMessageInListRunnable implements Runnable {
      private int mListId;
      private int mRequestId;

      GetNextMessageInListRunnable(int aListId, int aRequestId) {
        mListId = aListId;
        mRequestId = aRequestId;
      }

      @Override
      public void run() {
        try {
          Cursor cursor = MessagesListManager.getInstance().get(mListId);

          if (!cursor.moveToNext()) {
            MessagesListManager.getInstance().remove(mListId);
            notifyNoMessageInList(mRequestId);
            return;
          }

          int type = cursor.getInt(cursor.getColumnIndex("type"));
          int deliveryStatus;
          String sender = "";
          String receiver = "";

          if (type == kSmsTypeInbox) {
            deliveryStatus = kDeliveryStatusSuccess;
            sender = cursor.getString(cursor.getColumnIndex("address"));
          } else if (type == kSmsTypeSentbox) {
            deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status")));
            receiver = cursor.getString(cursor.getColumnIndex("address"));
          } else {
            throw new UnexpectedDeliveryStateException();
          }

          int listId = MessagesListManager.getInstance().add(cursor);
          notifyGotNextMessage(
              cursor.getInt(cursor.getColumnIndex("_id")),
              deliveryStatus,
              receiver,
              sender,
              cursor.getString(cursor.getColumnIndex("body")),
              cursor.getLong(cursor.getColumnIndex("date")),
              mRequestId);
        } catch (UnexpectedDeliveryStateException e) {
          Log.e("GeckoSmsManager", "Unexcepted delivery state type", e);
          notifyReadingMessageListFailed(kUnknownError, mRequestId);
        } catch (Exception e) {
          Log.e("GeckoSmsManager", "Error while trying to get the next message of a list", e);
          notifyReadingMessageListFailed(kUnknownError, mRequestId);
        }
      }
    }

    if (!SmsIOThread.getInstance().execute(new GetNextMessageInListRunnable(aListId, aRequestId))) {
      Log.e("GeckoSmsManager", "Failed to add GetNextMessageInListRunnable to the SmsIOThread");
      notifyReadingMessageListFailed(kUnknownError, aRequestId);
    }
  }
  @Override
  public void createMessageList(
      long aStartDate,
      long aEndDate,
      String[] aNumbers,
      int aNumbersCount,
      int aDeliveryState,
      boolean aReverse,
      int aRequestId) {
    class CreateMessageListRunnable implements Runnable {
      private long mStartDate;
      private long mEndDate;
      private String[] mNumbers;
      private int mNumbersCount;
      private int mDeliveryState;
      private boolean mReverse;
      private int mRequestId;

      CreateMessageListRunnable(
          long aStartDate,
          long aEndDate,
          String[] aNumbers,
          int aNumbersCount,
          int aDeliveryState,
          boolean aReverse,
          int aRequestId) {
        mStartDate = aStartDate;
        mEndDate = aEndDate;
        mNumbers = aNumbers;
        mNumbersCount = aNumbersCount;
        mDeliveryState = aDeliveryState;
        mReverse = aReverse;
        mRequestId = aRequestId;
      }

      @Override
      public void run() {
        Cursor cursor = null;
        boolean closeCursor = true;

        try {
          // TODO: should use the |selectionArgs| argument in |ContentResolver.query()|.
          ArrayList<String> restrictions = new ArrayList<String>();

          if (mStartDate != 0) {
            restrictions.add("date >= " + mStartDate);
          }

          if (mEndDate != 0) {
            restrictions.add("date <= " + mEndDate);
          }

          if (mNumbersCount > 0) {
            String numberRestriction = "address IN ('" + mNumbers[0] + "'";

            for (int i = 1; i < mNumbersCount; ++i) {
              numberRestriction += ", '" + mNumbers[i] + "'";
            }
            numberRestriction += ")";

            restrictions.add(numberRestriction);
          }

          if (mDeliveryState == kDeliveryStateUnknown) {
            restrictions.add("type IN ('" + kSmsTypeSentbox + "', '" + kSmsTypeInbox + "')");
          } else if (mDeliveryState == kDeliveryStateSent) {
            restrictions.add("type = " + kSmsTypeSentbox);
          } else if (mDeliveryState == kDeliveryStateReceived) {
            restrictions.add("type = " + kSmsTypeInbox);
          } else {
            throw new UnexpectedDeliveryStateException();
          }

          String restrictionText = restrictions.size() > 0 ? restrictions.get(0) : "";

          for (int i = 1; i < restrictions.size(); ++i) {
            restrictionText += " AND " + restrictions.get(i);
          }

          ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
          cursor =
              cr.query(
                  kSmsContentUri,
                  kRequiredMessageRows,
                  restrictionText,
                  null,
                  mReverse ? "date DESC" : "date ASC");

          if (cursor.getCount() == 0) {
            notifyNoMessageInList(mRequestId);
            return;
          }

          cursor.moveToFirst();

          int type = cursor.getInt(cursor.getColumnIndex("type"));
          int deliveryStatus;
          String sender = "";
          String receiver = "";

          if (type == kSmsTypeInbox) {
            deliveryStatus = kDeliveryStatusSuccess;
            sender = cursor.getString(cursor.getColumnIndex("address"));
          } else if (type == kSmsTypeSentbox) {
            deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status")));
            receiver = cursor.getString(cursor.getColumnIndex("address"));
          } else {
            throw new UnexpectedDeliveryStateException();
          }

          int listId = MessagesListManager.getInstance().add(cursor);
          closeCursor = false;
          notifyListCreated(
              listId,
              cursor.getInt(cursor.getColumnIndex("_id")),
              deliveryStatus,
              receiver,
              sender,
              cursor.getString(cursor.getColumnIndex("body")),
              cursor.getLong(cursor.getColumnIndex("date")),
              mRequestId);
        } catch (UnexpectedDeliveryStateException e) {
          Log.e("GeckoSmsManager", "Unexcepted delivery state type", e);
          notifyReadingMessageListFailed(kUnknownError, mRequestId);
        } catch (Exception e) {
          Log.e("GeckoSmsManager", "Error while trying to create a message list cursor", e);
          notifyReadingMessageListFailed(kUnknownError, mRequestId);
        } finally {
          // Close the cursor if MessagesListManager isn't taking care of it.
          // We could also just check if it is in the MessagesListManager list but
          // that would be less efficient.
          if (cursor != null && closeCursor) {
            cursor.close();
          }
        }
      }
    }

    if (!SmsIOThread.getInstance()
        .execute(
            new CreateMessageListRunnable(
                aStartDate,
                aEndDate,
                aNumbers,
                aNumbersCount,
                aDeliveryState,
                aReverse,
                aRequestId))) {
      Log.e("GeckoSmsManager", "Failed to add CreateMessageListRunnable to the SmsIOThread");
      notifyReadingMessageListFailed(kUnknownError, aRequestId);
    }
  }
  @Override
  public void getMessage(int aMessageId, int aRequestId) {
    class GetMessageRunnable implements Runnable {
      private int mMessageId;
      private int mRequestId;

      GetMessageRunnable(int aMessageId, int aRequestId) {
        mMessageId = aMessageId;
        mRequestId = aRequestId;
      }

      @Override
      public void run() {
        Cursor cursor = null;

        try {
          ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
          Uri message = ContentUris.withAppendedId(kSmsContentUri, mMessageId);

          cursor = cr.query(message, kRequiredMessageRows, null, null, null);
          if (cursor == null || cursor.getCount() == 0) {
            throw new NotFoundException();
          }

          if (cursor.getCount() != 1) {
            throw new TooManyResultsException();
          }

          cursor.moveToFirst();

          if (cursor.getInt(cursor.getColumnIndex("_id")) != mMessageId) {
            throw new UnmatchingIdException();
          }

          int type = cursor.getInt(cursor.getColumnIndex("type"));
          int deliveryStatus;
          String sender = "";
          String receiver = "";

          if (type == kSmsTypeInbox) {
            deliveryStatus = kDeliveryStatusSuccess;
            sender = cursor.getString(cursor.getColumnIndex("address"));
          } else if (type == kSmsTypeSentbox) {
            deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status")));
            receiver = cursor.getString(cursor.getColumnIndex("address"));
          } else {
            throw new InvalidTypeException();
          }

          notifyGetSms(
              cursor.getInt(cursor.getColumnIndex("_id")),
              deliveryStatus,
              receiver,
              sender,
              cursor.getString(cursor.getColumnIndex("body")),
              cursor.getLong(cursor.getColumnIndex("date")),
              mRequestId);
        } catch (NotFoundException e) {
          Log.i("GeckoSmsManager", "Message id " + mMessageId + " not found");
          notifyGetSmsFailed(kNotFoundError, mRequestId);
        } catch (UnmatchingIdException e) {
          Log.e(
              "GeckoSmsManager",
              "Requested message id (" + mMessageId + ") is different from the one we got.");
          notifyGetSmsFailed(kUnknownError, mRequestId);
        } catch (TooManyResultsException e) {
          Log.e("GeckoSmsManager", "Get too many results for id " + mMessageId);
          notifyGetSmsFailed(kUnknownError, mRequestId);
        } catch (InvalidTypeException e) {
          Log.i("GeckoSmsManager", "Message has an invalid type, we ignore it.");
          notifyGetSmsFailed(kNotFoundError, mRequestId);
        } catch (Exception e) {
          Log.e("GeckoSmsManager", "Error while trying to get message", e);
          notifyGetSmsFailed(kUnknownError, mRequestId);
        } finally {
          if (cursor != null) {
            cursor.close();
          }
        }
      }
    }

    if (!SmsIOThread.getInstance().execute(new GetMessageRunnable(aMessageId, aRequestId))) {
      Log.e("GeckoSmsManager", "Failed to add GetMessageRunnable to the SmsIOThread");
      notifyGetSmsFailed(kUnknownError, aRequestId);
    }
  }
 public GeckoSmsManager() {
   SmsIOThread.getInstance().start();
 }