1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.providers.telephony; 17 18 import static android.provider.Telephony.RcsColumns.Rcs1To1ThreadColumns.FALLBACK_THREAD_ID_COLUMN; 19 import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.CONFERENCE_URI_COLUMN; 20 import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_ICON_COLUMN; 21 import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_NAME_COLUMN; 22 import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.OWNER_PARTICIPANT_COLUMN; 23 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_TEXT_COLUMN; 24 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.ORIGINATION_TIMESTAMP_COLUMN; 25 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.STATUS_COLUMN; 26 import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN; 27 import static android.provider.Telephony.RcsColumns.RcsThreadColumns.RCS_THREAD_ID_COLUMN; 28 import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_COLUMN; 29 import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED; 30 import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN; 31 import static android.telephony.ims.RcsQueryContinuationToken.THREAD_QUERY_CONTINUATION_TOKEN_TYPE; 32 import static android.telephony.ims.RcsThreadQueryParams.THREAD_QUERY_PARAMETERS_KEY; 33 34 import static com.android.providers.telephony.RcsProvider.RCS_1_TO_1_THREAD_TABLE; 35 import static com.android.providers.telephony.RcsProvider.RCS_GROUP_THREAD_TABLE; 36 import static com.android.providers.telephony.RcsProvider.RCS_MESSAGE_TABLE; 37 import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_THREAD_JUNCTION_TABLE; 38 import static com.android.providers.telephony.RcsProvider.RCS_THREAD_TABLE; 39 import static com.android.providers.telephony.RcsProvider.TAG; 40 import static com.android.providers.telephony.RcsProvider.UNIFIED_RCS_THREAD_VIEW; 41 import static com.android.providers.telephony.RcsProviderUtil.INSERTION_FAILED; 42 43 import android.content.ContentValues; 44 import android.database.Cursor; 45 import android.database.sqlite.SQLiteDatabase; 46 import android.database.sqlite.SQLiteOpenHelper; 47 import android.net.Uri; 48 import android.os.Bundle; 49 import android.provider.BaseColumns; 50 import android.telephony.ims.RcsQueryContinuationToken; 51 import android.telephony.ims.RcsThreadQueryParams; 52 import android.util.Log; 53 54 import com.android.internal.annotations.VisibleForTesting; 55 56 /** 57 * Constants and helpers related to threads for {@link RcsProvider} to keep the code clean. 58 * 59 * @hide 60 */ 61 class RcsProviderThreadHelper { 62 private static final int THREAD_ID_INDEX_IN_URI = 1; 63 64 @VisibleForTesting createThreadTables(SQLiteDatabase db)65 public static void createThreadTables(SQLiteDatabase db) { 66 Log.d(TAG, "Creating thread tables"); 67 68 // Add the thread tables 69 db.execSQL("CREATE TABLE " + RCS_THREAD_TABLE + " (" + 70 RCS_THREAD_ID_COLUMN + " INTEGER PRIMARY KEY AUTOINCREMENT);"); 71 72 db.execSQL("CREATE TABLE " + RCS_1_TO_1_THREAD_TABLE + " (" + 73 RCS_THREAD_ID_COLUMN + " INTEGER PRIMARY KEY, " + 74 FALLBACK_THREAD_ID_COLUMN + " INTEGER, " + 75 "FOREIGN KEY(" + RCS_THREAD_ID_COLUMN 76 + ") REFERENCES " + RCS_THREAD_TABLE + "(" + RCS_THREAD_ID_COLUMN + ")," + 77 "FOREIGN KEY(" + FALLBACK_THREAD_ID_COLUMN 78 + ") REFERENCES threads( " + BaseColumns._ID + "))"); 79 80 db.execSQL("CREATE TABLE " + RCS_GROUP_THREAD_TABLE + " (" + 81 RCS_THREAD_ID_COLUMN + " INTEGER PRIMARY KEY, " + 82 OWNER_PARTICIPANT_COLUMN + " INTEGER, " + 83 GROUP_NAME_COLUMN + " TEXT, " + 84 GROUP_ICON_COLUMN + " TEXT, " + 85 CONFERENCE_URI_COLUMN + " TEXT, " + 86 "FOREIGN KEY(" + RCS_THREAD_ID_COLUMN 87 + ") REFERENCES " + RCS_THREAD_TABLE + "(" + RCS_THREAD_ID_COLUMN + "))"); 88 89 // Add the views 90 91 // The following is a unified thread view. Since SQLite does not support right or full 92 // joins, we are using a union with null values for unused variables for each thread type. 93 // The thread_type column is an easy way to figure out whether the entry came from a 1 to 1 94 // thread or a group thread. The last message in each thread is appended to the table 95 // entries to figure out the latest threads and snippet text. We use COALESCE so that MAX() 96 // can take null values into account in order to have threads with no messages still 97 // represented here 98 // 99 // SELECT <1 to 1 thread and first message> 100 // FROM ( 101 // SELECT * 102 // FROM rcs_1_to_1_thread LEFT JOIN rcs_message 103 // ON rcs_1_to_1_thread.rcs_thread_id=rcs_message.rcs_thread_id) 104 // GROUP BY rcs_thread_id 105 // HAVING MAX(COALESCE(origination_timestamp,1)) 106 // 107 // UNION 108 // SELECT <group thread and first message> 109 // FROM ( 110 // SELECT * 111 // FROM rcs_group_thread LEFT JOIN rcs_message 112 // ON rcs_group_thread.rcs_thread_id=rcs_message.rcs_thread_id) 113 // GROUP BY rcs_thread_id 114 // HAVING MAX(COALESCE(origination_timestamp,1)) 115 116 db.execSQL("CREATE VIEW " + UNIFIED_RCS_THREAD_VIEW + " AS " 117 + "SELECT " 118 + RCS_THREAD_ID_COLUMN + ", " 119 + FALLBACK_THREAD_ID_COLUMN + ", " 120 + "null AS " + OWNER_PARTICIPANT_COLUMN + ", " 121 + "null AS " + GROUP_NAME_COLUMN + ", " 122 + "null AS " + GROUP_ICON_COLUMN + ", " 123 + "null AS " + CONFERENCE_URI_COLUMN + ", " 124 + "0 AS " + THREAD_TYPE_COLUMN + ", " 125 + ORIGINATION_TIMESTAMP_COLUMN + ", " 126 + MESSAGE_TEXT_COLUMN + ", " 127 + STATUS_COLUMN 128 + " FROM (SELECT * FROM " 129 + RCS_1_TO_1_THREAD_TABLE + " LEFT JOIN " + RCS_MESSAGE_TABLE 130 + " ON " 131 + RCS_1_TO_1_THREAD_TABLE + "." + RCS_THREAD_ID_COLUMN + "=" 132 + RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ")" 133 + " GROUP BY " + RCS_THREAD_ID_COLUMN 134 + " HAVING MAX(COALESCE(" 135 + ORIGINATION_TIMESTAMP_COLUMN + ", 1))" 136 + " UNION SELECT " 137 + RCS_THREAD_ID_COLUMN + ", " 138 + "null AS " + FALLBACK_THREAD_ID_COLUMN + ", " 139 + OWNER_PARTICIPANT_COLUMN + ", " 140 + GROUP_NAME_COLUMN + ", " 141 + GROUP_ICON_COLUMN + ", " 142 + CONFERENCE_URI_COLUMN + ", " 143 + "1 AS " + THREAD_TYPE_COLUMN + ", " 144 + ORIGINATION_TIMESTAMP_COLUMN + ", " 145 + MESSAGE_TEXT_COLUMN + ", " 146 + STATUS_COLUMN 147 + " FROM (SELECT * FROM " 148 + RCS_GROUP_THREAD_TABLE + " LEFT JOIN " + RCS_MESSAGE_TABLE 149 + " ON " 150 + RCS_GROUP_THREAD_TABLE + "." + RCS_THREAD_ID_COLUMN + "=" 151 + RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ")" 152 + " GROUP BY " + RCS_THREAD_ID_COLUMN 153 + " HAVING MAX(COALESCE(" 154 + ORIGINATION_TIMESTAMP_COLUMN + ", 1))"); 155 156 // Add the triggers 157 158 // Delete the corresponding rcs_thread row upon deleting a row in rcs_1_to_1_thread 159 // 160 // CREATE TRIGGER deleteRcsThreadAfter1to1 161 // AFTER DELETE ON rcs_1_to_1_thread 162 // BEGIN 163 // DELETE FROM rcs_thread WHERE rcs_thread._id=OLD.rcs_thread_id; 164 // END 165 db.execSQL("CREATE TRIGGER deleteRcsThreadAfter1to1 AFTER DELETE ON " 166 + RCS_1_TO_1_THREAD_TABLE + " BEGIN DELETE FROM " + RCS_THREAD_TABLE + " WHERE " 167 + RCS_THREAD_TABLE + "." + RCS_THREAD_ID_COLUMN + "=OLD." + RCS_THREAD_ID_COLUMN 168 + "; END"); 169 170 // Delete the corresponding rcs_thread row upon deleting a row in rcs_group_thread 171 // 172 // CREATE TRIGGER deleteRcsThreadAfter1to1 173 // AFTER DELETE ON rcs_1_to_1_thread 174 // BEGIN 175 // DELETE FROM rcs_thread WHERE rcs_thread._id=OLD.rcs_thread_id; 176 // END 177 db.execSQL("CREATE TRIGGER deleteRcsThreadAfterGroup AFTER DELETE ON " 178 + RCS_GROUP_THREAD_TABLE + " BEGIN DELETE FROM " + RCS_THREAD_TABLE + " WHERE " 179 + RCS_THREAD_TABLE + "." + RCS_THREAD_ID_COLUMN + "=OLD." + RCS_THREAD_ID_COLUMN 180 + "; END"); 181 182 // Delete the junction table entries upon deleting a 1 to 1 thread 183 // 184 // CREATE TRIGGER delete1To1JunctionEntries 185 // AFTER 186 // DELETE ON rcs_1_to_1_thread 187 // BEGIN 188 // DELETE FROM 189 // rcs_thread_participant 190 // WHERE 191 // rcs_thread_participant.rcs_thread_id = OLD.rcs_thread_id; 192 // END 193 db.execSQL("CREATE TRIGGER delete1To1JunctionEntries AFTER DELETE ON " 194 + RCS_1_TO_1_THREAD_TABLE + " BEGIN DELETE FROM " 195 + RCS_PARTICIPANT_THREAD_JUNCTION_TABLE + " WHERE " 196 + RCS_PARTICIPANT_THREAD_JUNCTION_TABLE + "." + RCS_THREAD_ID_COLUMN + "=OLD." 197 + RCS_THREAD_ID_COLUMN + "; END"); 198 199 // Delete the junction table entries upon deleting a group thread 200 // 201 // CREATE TRIGGER delete1To1JunctionEntries 202 // AFTER 203 // DELETE ON rcs_1_to_1_thread 204 // BEGIN 205 // DELETE FROM 206 // rcs_thread_participant 207 // WHERE 208 // rcs_thread_participant.rcs_thread_id = OLD.rcs_thread_id; 209 // END 210 db.execSQL("CREATE TRIGGER deleteGroupJunctionEntries AFTER DELETE ON " 211 + RCS_GROUP_THREAD_TABLE + " BEGIN DELETE FROM " 212 + RCS_PARTICIPANT_THREAD_JUNCTION_TABLE + " WHERE " 213 + RCS_PARTICIPANT_THREAD_JUNCTION_TABLE + "." + RCS_THREAD_ID_COLUMN + "=OLD." 214 +RCS_THREAD_ID_COLUMN + "; END"); 215 216 // TODO - delete all messages in a thread after deleting a thread 217 218 // TODO - create indexes for faster querying 219 } 220 221 private final SQLiteOpenHelper mSqLiteOpenHelper; 222 RcsProviderThreadHelper(SQLiteOpenHelper sqLiteOpenHelper)223 RcsProviderThreadHelper(SQLiteOpenHelper sqLiteOpenHelper) { 224 mSqLiteOpenHelper = sqLiteOpenHelper; 225 } 226 queryUnifiedThread(Bundle bundle)227 Cursor queryUnifiedThread(Bundle bundle) { 228 RcsThreadQueryParams queryParameters = null; 229 RcsQueryContinuationToken continuationToken = null; 230 if (bundle != null) { 231 queryParameters = bundle.getParcelable( 232 THREAD_QUERY_PARAMETERS_KEY); 233 continuationToken = bundle.getParcelable(QUERY_CONTINUATION_TOKEN); 234 } 235 236 if (continuationToken != null) { 237 return RcsProviderUtil.performContinuationQuery(mSqLiteOpenHelper.getReadableDatabase(), 238 continuationToken); 239 } 240 241 if (queryParameters == null) { 242 queryParameters = new RcsThreadQueryParams.Builder().build(); 243 } 244 245 return performInitialQuery(queryParameters); 246 } 247 performInitialQuery(RcsThreadQueryParams queryParameters)248 private Cursor performInitialQuery(RcsThreadQueryParams queryParameters) { 249 if (queryParameters == null) { 250 // return everything for test purposes 251 queryParameters = new RcsThreadQueryParams.Builder().build(); 252 } 253 254 SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase(); 255 StringBuilder rawQuery = new StringBuilder("SELECT * FROM ").append( 256 UNIFIED_RCS_THREAD_VIEW); 257 258 if (queryParameters.getThreadType() == RcsThreadQueryParams.THREAD_TYPE_1_TO_1) { 259 rawQuery.append(" WHERE ").append(THREAD_TYPE_COLUMN).append("=0"); 260 } else if (queryParameters.getThreadType() == RcsThreadQueryParams.THREAD_TYPE_GROUP) { 261 rawQuery.append(" WHERE ").append(THREAD_TYPE_COLUMN).append("=1"); 262 } 263 264 rawQuery.append(" ORDER BY "); 265 266 if (queryParameters.getSortingProperty() == RcsThreadQueryParams.SORT_BY_TIMESTAMP) { 267 rawQuery.append(ORIGINATION_TIMESTAMP_COLUMN); 268 } else { 269 rawQuery.append(RCS_THREAD_ID_COLUMN); 270 } 271 272 rawQuery.append(queryParameters.getSortDirection() ? " ASC " : " DESC "); 273 RcsProviderUtil.appendLimit(rawQuery, queryParameters.getLimit()); 274 275 String rawQueryAsString = rawQuery.toString(); 276 Cursor cursor = db.rawQuery(rawQueryAsString, null); 277 278 // If this is a paginated query, build the next query and return as a Cursor extra. Only do 279 // this if the current query returned a result. 280 int limit = queryParameters.getLimit(); 281 if (limit > 0) { 282 RcsProviderUtil.createContinuationTokenBundle(cursor, 283 new RcsQueryContinuationToken(THREAD_QUERY_CONTINUATION_TOKEN_TYPE, 284 rawQueryAsString, limit, limit), QUERY_CONTINUATION_TOKEN); 285 } 286 287 return cursor; 288 } 289 queryUnifiedThreadUsingId(Uri uri, String[] projection)290 Cursor queryUnifiedThreadUsingId(Uri uri, String[] projection) { 291 SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase(); 292 String threadId = getThreadIdFromUri(uri); 293 294 return db.query(UNIFIED_RCS_THREAD_VIEW, projection, RCS_THREAD_ID_COLUMN + "=?", 295 new String[]{threadId}, 296 null, null, null); 297 } 298 query1to1Thread(String[] projection, String selection, String[] selectionArgs, String sortOrder)299 Cursor query1to1Thread(String[] projection, String selection, String[] selectionArgs, 300 String sortOrder) { 301 SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase(); 302 return db.query(RCS_1_TO_1_THREAD_TABLE, projection, selection, selectionArgs, null, 303 null, sortOrder); 304 } 305 query1To1ThreadUsingId(Uri uri, String[] projection)306 Cursor query1To1ThreadUsingId(Uri uri, String[] projection) { 307 return query1to1Thread(projection, getThreadIdSelection(uri), null, null); 308 } 309 queryGroupThread(String[] projection, String selection, String[] selectionArgs, String sortOrder)310 Cursor queryGroupThread(String[] projection, String selection, String[] selectionArgs, 311 String sortOrder) { 312 SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase(); 313 return db.query(RCS_GROUP_THREAD_TABLE, projection, selection, selectionArgs, null, 314 null, sortOrder); 315 } 316 queryGroupThreadUsingId(Uri uri, String[] projection)317 Cursor queryGroupThreadUsingId(Uri uri, String[] projection) { 318 return queryGroupThread(projection, getThreadIdSelection(uri), null, null); 319 } 320 321 /** 322 * @param contentValues should contain the participant ID of the other participant under key 323 * {@link RCS_PARTICIPANT_ID_COLUMN} 324 */ insert1To1Thread(ContentValues contentValues)325 long insert1To1Thread(ContentValues contentValues) { 326 if (contentValues.containsKey(RCS_THREAD_ID_COLUMN)) { 327 Log.e(RcsProvider.TAG, 328 "RcsProviderThreadHelper: inserting threads with IDs is not supported"); 329 return TRANSACTION_FAILED; 330 } 331 332 Long participantId = contentValues.getAsLong(RCS_PARTICIPANT_ID_COLUMN); 333 if (participantId == null) { 334 Log.e(RcsProvider.TAG, 335 "inserting threads without participant IDs is not supported"); 336 return TRANSACTION_FAILED; 337 } 338 339 SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(); 340 try { 341 db.beginTransaction(); 342 343 if (hasExisting1To1ThreadForParticipant(db, participantId)) { 344 return TRANSACTION_FAILED; 345 } 346 347 long threadId = insertIntoCommonRcsThreads(db); 348 if (threadId == -1) { 349 return TRANSACTION_FAILED; 350 } 351 352 if (insertP2pThread(db, threadId) == -1) { 353 return TRANSACTION_FAILED; 354 } 355 356 if (insertParticipantIntoP2pThread(db, threadId, participantId) == -1) { 357 return TRANSACTION_FAILED; 358 } 359 360 db.setTransactionSuccessful(); 361 362 return threadId; 363 } finally { 364 db.endTransaction(); 365 } 366 } 367 insertP2pThread(SQLiteDatabase db, long threadId)368 private long insertP2pThread(SQLiteDatabase db, long threadId) { 369 ContentValues contentValues = new ContentValues(1); 370 contentValues.put(RCS_THREAD_ID_COLUMN, threadId); 371 372 return db.insert(RCS_1_TO_1_THREAD_TABLE, RCS_THREAD_ID_COLUMN, contentValues); 373 } 374 insertParticipantIntoP2pThread( SQLiteDatabase db, long threadId, long participantId)375 private long insertParticipantIntoP2pThread( 376 SQLiteDatabase db, long threadId, long participantId) { 377 ContentValues contentValues = new ContentValues(2); 378 contentValues.put(RCS_THREAD_ID_COLUMN, threadId); 379 contentValues.put(RCS_PARTICIPANT_ID_COLUMN, participantId); 380 381 return db.insert( 382 RCS_PARTICIPANT_THREAD_JUNCTION_TABLE, RCS_PARTICIPANT_ID_COLUMN, contentValues); 383 } 384 hasExisting1To1ThreadForParticipant(SQLiteDatabase db, long participantId)385 private boolean hasExisting1To1ThreadForParticipant(SQLiteDatabase db, long participantId) { 386 String table = joinOnColumn( 387 RCS_PARTICIPANT_THREAD_JUNCTION_TABLE, 388 RCS_1_TO_1_THREAD_TABLE, 389 RCS_THREAD_ID_COLUMN); 390 391 try (Cursor cursor = db.query( 392 table, 393 null, 394 RCS_PARTICIPANT_ID_COLUMN + "=?", 395 new String[]{Long.toString(participantId)}, 396 null, 397 null, 398 null)) { 399 if (cursor == null || cursor.getCount() == 0) { 400 return false; 401 } 402 } 403 404 return true; 405 } 406 joinOnColumn(String t1, String t2, String col)407 private String joinOnColumn(String t1, String t2, String col) { 408 return t1 + " JOIN " + t2 + " ON (" + t1 + "." + col + "=" + t2 + "." + col + ")"; 409 } 410 insertGroupThread(ContentValues contentValues)411 long insertGroupThread(ContentValues contentValues) { 412 long returnValue = TRANSACTION_FAILED; 413 if (contentValues.containsKey(RCS_THREAD_ID_COLUMN)) { 414 Log.e(RcsProvider.TAG, 415 "RcsProviderThreadHelper: inserting threads with IDs is not supported"); 416 return returnValue; 417 } 418 419 SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(); 420 try { 421 db.beginTransaction(); 422 423 // Insert into the common rcs_threads table 424 long rowId = insertIntoCommonRcsThreads(db); 425 if (rowId == INSERTION_FAILED) { 426 return returnValue; 427 } 428 429 // Add the rowId in rcs_threads table as a foreign key in rcs_group_table 430 contentValues.put(RCS_THREAD_ID_COLUMN, rowId); 431 db.insert(RCS_GROUP_THREAD_TABLE, RCS_THREAD_ID_COLUMN, contentValues); 432 contentValues.remove(RCS_THREAD_ID_COLUMN); 433 434 db.setTransactionSuccessful(); 435 returnValue = rowId; 436 } finally { 437 db.endTransaction(); 438 } 439 return returnValue; 440 } 441 insertIntoCommonRcsThreads(SQLiteDatabase db)442 private long insertIntoCommonRcsThreads(SQLiteDatabase db) { 443 return db.insert(RCS_THREAD_TABLE, RCS_THREAD_ID_COLUMN, new ContentValues()); 444 } 445 delete1To1Thread(String selection, String[] selectionArgs)446 int delete1To1Thread(String selection, String[] selectionArgs) { 447 SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(); 448 return db.delete(RCS_1_TO_1_THREAD_TABLE, selection, selectionArgs); 449 } 450 delete1To1ThreadWithId(Uri uri)451 int delete1To1ThreadWithId(Uri uri) { 452 return delete1To1Thread(getThreadIdSelection(uri), null); 453 } 454 deleteGroupThread(String selection, String[] selectionArgs)455 int deleteGroupThread(String selection, String[] selectionArgs) { 456 SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(); 457 return db.delete(RCS_GROUP_THREAD_TABLE, selection, selectionArgs); 458 } 459 deleteGroupThreadWithId(Uri uri)460 int deleteGroupThreadWithId(Uri uri) { 461 return deleteGroupThread(getThreadIdSelection(uri), null); 462 } 463 update1To1Thread(ContentValues values, String selection, String[] selectionArgs)464 int update1To1Thread(ContentValues values, String selection, String[] selectionArgs) { 465 if (values.containsKey(RCS_THREAD_ID_COLUMN)) { 466 Log.e(TAG, 467 "RcsProviderThreadHelper: updating thread id for 1 to 1 threads is not " 468 + "allowed"); 469 return 0; 470 } 471 472 SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(); 473 return db.update(RCS_1_TO_1_THREAD_TABLE, values, selection, selectionArgs); 474 } 475 update1To1ThreadWithId(ContentValues values, Uri uri)476 int update1To1ThreadWithId(ContentValues values, Uri uri) { 477 return update1To1Thread(values, getThreadIdSelection(uri), null); 478 } 479 updateGroupThread(ContentValues values, String selection, String[] selectionArgs)480 int updateGroupThread(ContentValues values, String selection, String[] selectionArgs) { 481 if (values.containsKey(RCS_THREAD_ID_COLUMN)) { 482 Log.e(TAG, 483 "RcsProviderThreadHelper: updating thread id for group threads is not " 484 + "allowed"); 485 return 0; 486 } 487 488 SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(); 489 return db.update(RCS_GROUP_THREAD_TABLE, values, selection, selectionArgs); 490 } 491 updateGroupThreadWithId(ContentValues values, Uri uri)492 int updateGroupThreadWithId(ContentValues values, Uri uri) { 493 return updateGroupThread(values, getThreadIdSelection(uri), null); 494 } 495 getThreadIdSelection(Uri uri)496 private String getThreadIdSelection(Uri uri) { 497 return RCS_THREAD_ID_COLUMN + "=" + getThreadIdFromUri(uri); 498 } 499 getThreadIdFromUri(Uri uri)500 static String getThreadIdFromUri(Uri uri) { 501 return uri.getPathSegments().get(THREAD_ID_INDEX_IN_URI); 502 } 503 } 504