• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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