org.sandrob.bouncycastle.jce.spec.ECParameterSpec engineGetSpec() {
    if (ecSpec != null) {
      return EC5Util.convertSpec(ecSpec, withCompression);
    }

    return ProviderUtil.getEcImplicitlyCa();
  }
  @Override
  public Endpoint loadEndpoint(RepositoryInformation repoInfo) throws FedXException {

    File store = FileUtil.getFileLocation(repoInfo.getLocation());

    if (!store.exists()) {
      throw new FedXRuntimeException(
          "Store does not exist at '"
              + repoInfo.getLocation()
              + ": "
              + store.getAbsolutePath()
              + "'.");
    }

    try {
      NativeStore ns = new NativeStoreExt(store);
      SailRepository repo = new SailRepository(ns);
      repo.initialize();

      ProviderUtil.checkConnectionIfConfigured(repo);

      Endpoint res =
          new Endpoint(
              repoInfo.getId(),
              repoInfo.getName(),
              repoInfo.getLocation(),
              repoInfo.getType(),
              EndpointClassification.Local);
      res.setEndpointConfiguration(repoInfo.getEndpointConfiguration());
      res.setRepo(repo);

      // register a federated service manager to deal with this endpoint
      SAILFederatedService federatedService = new SAILFederatedService(res);
      federatedService.initialize();
      FederatedServiceManager.getInstance().registerService(repoInfo.getName(), federatedService);

      return res;
    } catch (RepositoryException e) {
      throw new FedXException(
          "Repository " + repoInfo.getId() + " could not be initialized: " + e.getMessage(), e);
    }
  }
 private void notifyChange(final Uri uri) {
   final Context context = getContext();
   context.getContentResolver().notifyChange(MmsSms.CONTENT_URI, null, true, UserHandle.USER_ALL);
   ProviderUtil.notifyIfNotDefaultSmsApp(uri, getCallingPackage(), context);
 }
  @Override
  public Cursor query(
      Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    // First check if a restricted view of the "pdu" table should be used based on the
    // caller's identity. Only system, phone or the default sms app can have full access
    // of mms data. For other apps, we present a restricted view which only contains sent
    // or received messages, without wap pushes.
    final boolean accessRestricted =
        ProviderUtil.isAccessRestricted(getContext(), getCallingPackage(), Binder.getCallingUid());
    final String pduTable = getPduTable(accessRestricted);

    SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

    // Generate the body of the query.
    int match = sURLMatcher.match(uri);
    if (LOCAL_LOGV) {
      Log.v(TAG, "Query uri=" + uri + ", match=" + match);
    }

    switch (match) {
      case MMS_ALL:
        constructQueryForBox(qb, Mms.MESSAGE_BOX_ALL, pduTable);
        break;
      case MMS_INBOX:
        constructQueryForBox(qb, Mms.MESSAGE_BOX_INBOX, pduTable);
        break;
      case MMS_SENT:
        constructQueryForBox(qb, Mms.MESSAGE_BOX_SENT, pduTable);
        break;
      case MMS_DRAFTS:
        constructQueryForBox(qb, Mms.MESSAGE_BOX_DRAFTS, pduTable);
        break;
      case MMS_OUTBOX:
        constructQueryForBox(qb, Mms.MESSAGE_BOX_OUTBOX, pduTable);
        break;
      case MMS_ALL_ID:
        qb.setTables(pduTable);
        qb.appendWhere(Mms._ID + "=" + uri.getPathSegments().get(0));
        break;
      case MMS_INBOX_ID:
      case MMS_SENT_ID:
      case MMS_DRAFTS_ID:
      case MMS_OUTBOX_ID:
        qb.setTables(pduTable);
        qb.appendWhere(Mms._ID + "=" + uri.getPathSegments().get(1));
        qb.appendWhere(" AND " + Mms.MESSAGE_BOX + "=" + getMessageBoxByMatch(match));
        break;
      case MMS_ALL_PART:
        qb.setTables(TABLE_PART);
        break;
      case MMS_MSG_PART:
        qb.setTables(TABLE_PART);
        qb.appendWhere(Part.MSG_ID + "=" + uri.getPathSegments().get(0));
        break;
      case MMS_PART_ID:
        qb.setTables(TABLE_PART);
        qb.appendWhere(Part._ID + "=" + uri.getPathSegments().get(1));
        break;
      case MMS_MSG_ADDR:
        qb.setTables(TABLE_ADDR);
        qb.appendWhere(Addr.MSG_ID + "=" + uri.getPathSegments().get(0));
        break;
      case MMS_REPORT_STATUS:
        /*
          SELECT DISTINCT address,
                          T.delivery_status AS delivery_status,
                          T.read_status AS read_status
          FROM addr
          INNER JOIN (SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3,
                             ifnull(P2.st, 0) AS delivery_status,
                             ifnull(P3.read_status, 0) AS read_status
                      FROM pdu P1
                      INNER JOIN pdu P2
                      ON P1.m_id = P2.m_id AND P2.m_type = 134
                      LEFT JOIN pdu P3
                      ON P1.m_id = P3.m_id AND P3.m_type = 136
                      UNION
                      SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3,
                             ifnull(P2.st, 0) AS delivery_status,
                             ifnull(P3.read_status, 0) AS read_status
                      FROM pdu P1
                      INNER JOIN pdu P3
                      ON P1.m_id = P3.m_id AND P3.m_type = 136
                      LEFT JOIN pdu P2
                      ON P1.m_id = P2.m_id AND P2.m_type = 134) T
          ON (msg_id = id2 AND type = 151)
          OR (msg_id = id3 AND type = 137)
          WHERE T.id1 = ?;
        */
        qb.setTables(
            TABLE_ADDR
                + " INNER JOIN "
                + "(SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, "
                + "ifnull(P2.st, 0) AS delivery_status, "
                + "ifnull(P3.read_status, 0) AS read_status "
                + "FROM "
                + pduTable
                + " P1 INNER JOIN "
                + pduTable
                + " P2 "
                + "ON P1.m_id=P2.m_id AND P2.m_type=134 "
                + "LEFT JOIN "
                + pduTable
                + " P3 "
                + "ON P1.m_id=P3.m_id AND P3.m_type=136 "
                + "UNION "
                + "SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, "
                + "ifnull(P2.st, 0) AS delivery_status, "
                + "ifnull(P3.read_status, 0) AS read_status "
                + "FROM "
                + pduTable
                + " P1 INNER JOIN "
                + pduTable
                + " P3 "
                + "ON P1.m_id=P3.m_id AND P3.m_type=136 "
                + "LEFT JOIN "
                + pduTable
                + " P2 "
                + "ON P1.m_id=P2.m_id AND P2.m_type=134) T "
                + "ON (msg_id=id2 AND type=151) OR (msg_id=id3 AND type=137)");
        qb.appendWhere("T.id1 = " + uri.getLastPathSegment());
        qb.setDistinct(true);
        break;
      case MMS_REPORT_REQUEST:
        /*
          SELECT address, d_rpt, rr
          FROM addr join pdu on pdu._id = addr.msg_id
          WHERE pdu._id = messageId AND addr.type = 151
        */
        qb.setTables(TABLE_ADDR + " join " + pduTable + " on " + pduTable + "._id = addr.msg_id");
        qb.appendWhere(pduTable + "._id = " + uri.getLastPathSegment());
        qb.appendWhere(" AND " + TABLE_ADDR + ".type = " + PduHeaders.TO);
        break;
      case MMS_SENDING_RATE:
        qb.setTables(TABLE_RATE);
        break;
      case MMS_DRM_STORAGE_ID:
        qb.setTables(TABLE_DRM);
        qb.appendWhere(BaseColumns._ID + "=" + uri.getLastPathSegment());
        break;
      case MMS_THREADS:
        qb.setTables(pduTable + " group by thread_id");
        break;
      default:
        Log.e(TAG, "query: invalid request: " + uri);
        return null;
    }

    String finalSortOrder = null;
    if (TextUtils.isEmpty(sortOrder)) {
      if (qb.getTables().equals(pduTable)) {
        finalSortOrder = Mms.DATE + " DESC";
      } else if (qb.getTables().equals(TABLE_PART)) {
        finalSortOrder = Part.SEQ;
      }
    } else {
      finalSortOrder = sortOrder;
    }

    Cursor ret;
    try {
      SQLiteDatabase db = mOpenHelper.getReadableDatabase();
      ret = qb.query(db, projection, selection, selectionArgs, null, null, finalSortOrder);
    } catch (SQLiteException e) {
      Log.e(TAG, "returning NULL cursor, query: " + uri, e);
      return null;
    }

    // TODO: Does this need to be a URI for this provider.
    ret.setNotificationUri(getContext().getContentResolver(), uri);
    return ret;
  }
  @Override
  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    // The _data column is filled internally in MmsProvider, so this check is just to avoid
    // it from being inadvertently set. This is not supposed to be a protection against
    // malicious attack, since sql injection could still be attempted to bypass the check. On
    // the other hand, the MmsProvider does verify that the _data column has an allowed value
    // before opening any uri/files.
    if (values != null && values.containsKey(Part._DATA)) {
      return 0;
    }
    final int callerUid = Binder.getCallingUid();
    final String callerPkg = getCallingPackage();
    int match = sURLMatcher.match(uri);
    if (LOCAL_LOGV) {
      Log.v(TAG, "Update uri=" + uri + ", match=" + match);
    }

    boolean notify = false;
    String msgId = null;
    String table;

    switch (match) {
      case MMS_ALL_ID:
      case MMS_INBOX_ID:
      case MMS_SENT_ID:
      case MMS_DRAFTS_ID:
      case MMS_OUTBOX_ID:
        msgId = uri.getLastPathSegment();
        // fall-through
      case MMS_ALL:
      case MMS_INBOX:
      case MMS_SENT:
      case MMS_DRAFTS:
      case MMS_OUTBOX:
        notify = true;
        table = TABLE_PDU;
        break;

      case MMS_MSG_PART:
      case MMS_PART_ID:
        table = TABLE_PART;
        break;

      case MMS_PART_RESET_FILE_PERMISSION:
        String path =
            getContext().getDir(PARTS_DIR_NAME, 0).getPath() + '/' + uri.getPathSegments().get(1);
        // Reset the file permission back to read for everyone but me.
        int result = FileUtils.setPermissions(path, 0644, -1, -1);
        if (LOCAL_LOGV) {
          Log.d(TAG, "MmsProvider.update setPermissions result: " + result + " for path: " + path);
        }
        return 0;

      default:
        Log.w(TAG, "Update operation for '" + uri + "' not implemented.");
        return 0;
    }

    String extraSelection = null;
    ContentValues finalValues;
    if (table.equals(TABLE_PDU)) {
      // Filter keys that we don't support yet.
      filterUnsupportedKeys(values);
      if (ProviderUtil.shouldRemoveCreator(values, callerUid)) {
        // CREATOR should not be changed by non-SYSTEM/PHONE apps
        Log.w(TAG, callerPkg + " tries to update CREATOR");
        values.remove(Mms.CREATOR);
      }
      finalValues = new ContentValues(values);

      if (msgId != null) {
        extraSelection = Mms._ID + "=" + msgId;
      }
    } else if (table.equals(TABLE_PART)) {
      finalValues = new ContentValues(values);

      switch (match) {
        case MMS_MSG_PART:
          extraSelection = Part.MSG_ID + "=" + uri.getPathSegments().get(0);
          break;
        case MMS_PART_ID:
          extraSelection = Part._ID + "=" + uri.getPathSegments().get(1);
          break;
        default:
          break;
      }
    } else {
      return 0;
    }

    String finalSelection = concatSelections(selection, extraSelection);
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    int count = db.update(table, finalValues, finalSelection, selectionArgs);
    if (notify && (count > 0)) {
      notifyChange(uri);
    }
    return count;
  }
  @Override
  public Uri insert(Uri uri, ContentValues values) {
    // The _data column is filled internally in MmsProvider, so this check is just to avoid
    // it from being inadvertently set. This is not supposed to be a protection against
    // malicious attack, since sql injection could still be attempted to bypass the check. On
    // the other hand, the MmsProvider does verify that the _data column has an allowed value
    // before opening any uri/files.
    if (values != null && values.containsKey(Part._DATA)) {
      return null;
    }
    final int callerUid = Binder.getCallingUid();
    final String callerPkg = getCallingPackage();
    int msgBox = Mms.MESSAGE_BOX_ALL;
    boolean notify = true;

    int match = sURLMatcher.match(uri);
    if (LOCAL_LOGV) {
      Log.v(TAG, "Insert uri=" + uri + ", match=" + match);
    }

    String table = TABLE_PDU;
    switch (match) {
      case MMS_ALL:
        Object msgBoxObj = values.getAsInteger(Mms.MESSAGE_BOX);
        if (msgBoxObj != null) {
          msgBox = (Integer) msgBoxObj;
        } else {
          // default to inbox
          msgBox = Mms.MESSAGE_BOX_INBOX;
        }
        break;
      case MMS_INBOX:
        msgBox = Mms.MESSAGE_BOX_INBOX;
        break;
      case MMS_SENT:
        msgBox = Mms.MESSAGE_BOX_SENT;
        break;
      case MMS_DRAFTS:
        msgBox = Mms.MESSAGE_BOX_DRAFTS;
        break;
      case MMS_OUTBOX:
        msgBox = Mms.MESSAGE_BOX_OUTBOX;
        break;
      case MMS_MSG_PART:
        notify = false;
        table = TABLE_PART;
        break;
      case MMS_MSG_ADDR:
        notify = false;
        table = TABLE_ADDR;
        break;
      case MMS_SENDING_RATE:
        notify = false;
        table = TABLE_RATE;
        break;
      case MMS_DRM_STORAGE:
        notify = false;
        table = TABLE_DRM;
        break;
      default:
        Log.e(TAG, "insert: invalid request: " + uri);
        return null;
    }

    SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    ContentValues finalValues;
    Uri res = Mms.CONTENT_URI;
    long rowId;

    if (table.equals(TABLE_PDU)) {
      boolean addDate = !values.containsKey(Mms.DATE);
      boolean addMsgBox = !values.containsKey(Mms.MESSAGE_BOX);

      // Filter keys we don't support yet.
      filterUnsupportedKeys(values);

      // TODO: Should initialValues be validated, e.g. if it
      // missed some significant keys?
      finalValues = new ContentValues(values);

      long timeInMillis = System.currentTimeMillis();

      if (addDate) {
        finalValues.put(Mms.DATE, timeInMillis / 1000L);
      }

      if (addMsgBox && (msgBox != Mms.MESSAGE_BOX_ALL)) {
        finalValues.put(Mms.MESSAGE_BOX, msgBox);
      }

      if (msgBox != Mms.MESSAGE_BOX_INBOX) {
        // Mark all non-inbox messages read.
        finalValues.put(Mms.READ, 1);
      }

      // thread_id
      Long threadId = values.getAsLong(Mms.THREAD_ID);
      String address = values.getAsString(CanonicalAddressesColumns.ADDRESS);

      if (((threadId == null) || (threadId == 0)) && (!TextUtils.isEmpty(address))) {
        finalValues.put(Mms.THREAD_ID, Threads.getOrCreateThreadId(getContext(), address));
      }

      if (ProviderUtil.shouldSetCreator(finalValues, callerUid)) {
        // Only SYSTEM or PHONE can set CREATOR
        // If caller is not SYSTEM or PHONE, or SYSTEM or PHONE does not set CREATOR
        // set CREATOR using the truth on caller.
        // Note: Inferring package name from UID may include unrelated package names
        finalValues.put(Telephony.Mms.CREATOR, callerPkg);
      }

      if ((rowId = db.insert(table, null, finalValues)) <= 0) {
        Log.e(TAG, "MmsProvider.insert: failed!");
        return null;
      }

      res = Uri.parse(res + "/" + rowId);
    } else if (table.equals(TABLE_ADDR)) {
      finalValues = new ContentValues(values);
      finalValues.put(Addr.MSG_ID, uri.getPathSegments().get(0));

      if ((rowId = db.insert(table, null, finalValues)) <= 0) {
        Log.e(TAG, "Failed to insert address");
        return null;
      }

      res = Uri.parse(res + "/addr/" + rowId);
    } else if (table.equals(TABLE_PART)) {
      finalValues = new ContentValues(values);

      if (match == MMS_MSG_PART) {
        finalValues.put(Part.MSG_ID, uri.getPathSegments().get(0));
      }

      String contentType = values.getAsString("ct");

      // text/plain and app application/smil store their "data" inline in the
      // table so there's no need to create the file
      boolean plainText = false;
      boolean smilText = false;
      if ("text/plain".equals(contentType)) {
        plainText = true;
      } else if ("application/smil".equals(contentType)) {
        smilText = true;
      }
      if (!plainText && !smilText) {
        // Use the filename if possible, otherwise use the current time as the name.
        String contentLocation = values.getAsString("cl");
        if (!TextUtils.isEmpty(contentLocation)) {
          File f = new File(contentLocation);
          contentLocation = "_" + f.getName();
        } else {
          contentLocation = "";
        }

        // Generate the '_data' field of the part with default
        // permission settings.
        String path =
            getContext().getDir(PARTS_DIR_NAME, 0).getPath()
                + "/PART_"
                + System.currentTimeMillis()
                + contentLocation;

        if (DownloadDrmHelper.isDrmConvertNeeded(contentType)) {
          // Adds the .fl extension to the filename if contentType is
          // "application/vnd.oma.drm.message"
          path = DownloadDrmHelper.modifyDrmFwLockFileExtension(path);
        }

        finalValues.put(Part._DATA, path);

        File partFile = new File(path);
        if (!partFile.exists()) {
          try {
            if (!partFile.createNewFile()) {
              throw new IllegalStateException("Unable to create new partFile: " + path);
            }
            // Give everyone rw permission until we encrypt the file
            // (in PduPersister.persistData). Once the file is encrypted, the
            // permissions will be set to 0644.
            int result = FileUtils.setPermissions(path, 0666, -1, -1);
            if (LOCAL_LOGV) {
              Log.d(TAG, "MmsProvider.insert setPermissions result: " + result);
            }
          } catch (IOException e) {
            Log.e(TAG, "createNewFile", e);
            throw new IllegalStateException("Unable to create new partFile: " + path);
          }
        }
      }

      if ((rowId = db.insert(table, null, finalValues)) <= 0) {
        Log.e(TAG, "MmsProvider.insert: failed!");
        return null;
      }

      res = Uri.parse(res + "/part/" + rowId);

      // Don't use a trigger for updating the words table because of a bug
      // in FTS3.  The bug is such that the call to get the last inserted
      // row is incorrect.
      if (plainText) {
        // Update the words table with a corresponding row.  The words table
        // allows us to search for words quickly, without scanning the whole
        // table;
        ContentValues cv = new ContentValues();

        // we're using the row id of the part table row but we're also using ids
        // from the sms table so this divides the space into two large chunks.
        // The row ids from the part table start at 2 << 32.
        cv.put(Telephony.MmsSms.WordsTable.ID, (2L << 32) + rowId);
        cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, values.getAsString("text"));
        cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, rowId);
        cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 2);
        db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
      }

    } else if (table.equals(TABLE_RATE)) {
      long now = values.getAsLong(Rate.SENT_TIME);
      long oneHourAgo = now - 1000 * 60 * 60;
      // Delete all unused rows (time earlier than one hour ago).
      db.delete(table, Rate.SENT_TIME + "<=" + oneHourAgo, null);
      db.insert(table, null, values);
    } else if (table.equals(TABLE_DRM)) {
      String path =
          getContext().getDir(PARTS_DIR_NAME, 0).getPath() + "/PART_" + System.currentTimeMillis();
      finalValues = new ContentValues(1);
      finalValues.put("_data", path);

      File partFile = new File(path);
      if (!partFile.exists()) {
        try {
          if (!partFile.createNewFile()) {
            throw new IllegalStateException("Unable to create new file: " + path);
          }
        } catch (IOException e) {
          Log.e(TAG, "createNewFile", e);
          throw new IllegalStateException("Unable to create new file: " + path);
        }
      }

      if ((rowId = db.insert(table, null, finalValues)) <= 0) {
        Log.e(TAG, "MmsProvider.insert: failed!");
        return null;
      }
      res = Uri.parse(res + "/drm/" + rowId);
    } else {
      throw new AssertionError("Unknown table type: " + table);
    }

    if (notify) {
      notifyChange(res);
    }
    return res;
  }
  @Override
  public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
    final int callerUid = Binder.getCallingUid();
    int count = 0;
    String table = TABLE_SMS;
    String extraWhere = null;
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();

    switch (sURLMatcher.match(url)) {
      case SMS_RAW_MESSAGE:
        table = TABLE_RAW;
        break;

      case SMS_STATUS_PENDING:
        table = TABLE_SR_PENDING;
        break;

      case SMS_ALL:
      case SMS_FAILED:
      case SMS_QUEUED:
      case SMS_INBOX:
      case SMS_SENT:
      case SMS_DRAFT:
      case SMS_OUTBOX:
      case SMS_CONVERSATIONS:
        break;

      case SMS_ALL_ID:
        extraWhere = "_id=" + url.getPathSegments().get(0);
        break;

      case SMS_INBOX_ID:
      case SMS_FAILED_ID:
      case SMS_SENT_ID:
      case SMS_DRAFT_ID:
      case SMS_OUTBOX_ID:
        extraWhere = "_id=" + url.getPathSegments().get(1);
        break;

      case SMS_CONVERSATIONS_ID:
        {
          String threadId = url.getPathSegments().get(1);

          try {
            Integer.parseInt(threadId);
          } catch (Exception ex) {
            Log.e(TAG, "Bad conversation thread id: " + threadId);
            break;
          }

          extraWhere = "thread_id=" + threadId;
          break;
        }

      case SMS_STATUS_ID:
        extraWhere = "_id=" + url.getPathSegments().get(1);
        break;

      default:
        throw new UnsupportedOperationException("URI " + url + " not supported");
    }

    if (table.equals(TABLE_SMS) && ProviderUtil.shouldRemoveCreator(values, callerUid)) {
      // CREATOR should not be changed by non-SYSTEM/PHONE apps
      Log.w(
          TAG,
          ProviderUtil.getPackageNamesByUid(getContext(), callerUid) + " tries to update CREATOR");
      values.remove(Sms.CREATOR);
    }

    where = DatabaseUtils.concatenateWhere(where, extraWhere);
    count = db.update(table, values, where, whereArgs);

    if (count > 0) {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.d(TAG, "update " + url + " succeeded");
      }
      notifyChange(url);
    }
    return count;
  }
  private Uri insertInner(Uri url, ContentValues initialValues, int callerUid) {
    ContentValues values;
    long rowID;
    int type = Sms.MESSAGE_TYPE_ALL;

    int match = sURLMatcher.match(url);
    String table = TABLE_SMS;

    switch (match) {
      case SMS_ALL:
        Integer typeObj = initialValues.getAsInteger(Sms.TYPE);
        if (typeObj != null) {
          type = typeObj.intValue();
        } else {
          // default to inbox
          type = Sms.MESSAGE_TYPE_INBOX;
        }
        break;

      case SMS_INBOX:
        type = Sms.MESSAGE_TYPE_INBOX;
        break;

      case SMS_FAILED:
        type = Sms.MESSAGE_TYPE_FAILED;
        break;

      case SMS_QUEUED:
        type = Sms.MESSAGE_TYPE_QUEUED;
        break;

      case SMS_SENT:
        type = Sms.MESSAGE_TYPE_SENT;
        break;

      case SMS_DRAFT:
        type = Sms.MESSAGE_TYPE_DRAFT;
        break;

      case SMS_OUTBOX:
        type = Sms.MESSAGE_TYPE_OUTBOX;
        break;

      case SMS_RAW_MESSAGE:
        table = "raw";
        break;

      case SMS_STATUS_PENDING:
        table = "sr_pending";
        break;

      case SMS_ATTACHMENT:
        table = "attachments";
        break;

      case SMS_NEW_THREAD_ID:
        table = "canonical_addresses";
        break;

      default:
        Log.e(TAG, "Invalid request: " + url);
        return null;
    }

    SQLiteDatabase db = mOpenHelper.getWritableDatabase();

    if (table.equals(TABLE_SMS)) {
      boolean addDate = false;
      boolean addType = false;

      // Make sure that the date and type are set
      if (initialValues == null) {
        values = new ContentValues(1);
        addDate = true;
        addType = true;
      } else {
        values = new ContentValues(initialValues);

        if (!initialValues.containsKey(Sms.DATE)) {
          addDate = true;
        }

        if (!initialValues.containsKey(Sms.TYPE)) {
          addType = true;
        }
      }

      if (addDate) {
        values.put(Sms.DATE, new Long(System.currentTimeMillis()));
      }

      if (addType && (type != Sms.MESSAGE_TYPE_ALL)) {
        values.put(Sms.TYPE, Integer.valueOf(type));
      }

      // thread_id
      Long threadId = values.getAsLong(Sms.THREAD_ID);
      String address = values.getAsString(Sms.ADDRESS);

      if (((threadId == null) || (threadId == 0)) && (!TextUtils.isEmpty(address))) {
        values.put(Sms.THREAD_ID, Threads.getOrCreateThreadId(getContext(), address));
      }

      // If this message is going in as a draft, it should replace any
      // other draft messages in the thread.  Just delete all draft
      // messages with this thread ID.  We could add an OR REPLACE to
      // the insert below, but we'd have to query to find the old _id
      // to produce a conflict anyway.
      if (values.getAsInteger(Sms.TYPE) == Sms.MESSAGE_TYPE_DRAFT) {
        db.delete(
            TABLE_SMS,
            "thread_id=? AND type=?",
            new String[] {
              values.getAsString(Sms.THREAD_ID), Integer.toString(Sms.MESSAGE_TYPE_DRAFT)
            });
      }

      if (type == Sms.MESSAGE_TYPE_INBOX) {
        // Look up the person if not already filled in.
        if ((values.getAsLong(Sms.PERSON) == null) && (!TextUtils.isEmpty(address))) {
          Cursor cursor = null;
          Uri uri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, Uri.encode(address));
          try {
            cursor =
                getContext()
                    .getContentResolver()
                    .query(uri, CONTACT_QUERY_PROJECTION, null, null, null);

            if (cursor.moveToFirst()) {
              Long id = Long.valueOf(cursor.getLong(PERSON_ID_COLUMN));
              values.put(Sms.PERSON, id);
            }
          } catch (Exception ex) {
            Log.e(TAG, "insert: query contact uri " + uri + " caught ", ex);
          } finally {
            if (cursor != null) {
              cursor.close();
            }
          }
        }
      } else {
        // Mark all non-inbox messages read.
        values.put(Sms.READ, ONE);
      }
      if (ProviderUtil.shouldSetCreator(values, callerUid)) {
        // Only SYSTEM or PHONE can set CREATOR
        // If caller is not SYSTEM or PHONE, or SYSTEM or PHONE does not set CREATOR
        // set CREATOR using the truth on caller.
        // Note: Inferring package name from UID may include unrelated package names
        values.put(Sms.CREATOR, ProviderUtil.getPackageNamesByUid(getContext(), callerUid));
      }
    } else {
      if (initialValues == null) {
        values = new ContentValues(1);
      } else {
        values = initialValues;
      }
    }

    rowID = db.insert(table, "body", values);

    // Don't use a trigger for updating the words table because of a bug
    // in FTS3.  The bug is such that the call to get the last inserted
    // row is incorrect.
    if (table == TABLE_SMS) {
      // Update the words table with a corresponding row.  The words table
      // allows us to search for words quickly, without scanning the whole
      // table;
      ContentValues cv = new ContentValues();
      cv.put(Telephony.MmsSms.WordsTable.ID, rowID);
      cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, values.getAsString("body"));
      cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, rowID);
      cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 1);
      db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
    }
    if (rowID > 0) {
      Uri uri = Uri.parse("content://" + table + "/" + rowID);

      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.d(TAG, "insert " + uri + " succeeded");
      }
      notifyChange(uri);
      return uri;
    } else {
      Log.e(TAG, "insert: failed!");
    }

    return null;
  }