@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;
  }