• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 
17 package com.android.internal.telephony.ims;
18 
19 import static android.provider.Telephony.RcsColumns.Rcs1To1ThreadColumns.FALLBACK_THREAD_ID_COLUMN;
20 import static android.provider.Telephony.RcsColumns.Rcs1To1ThreadColumns.RCS_1_TO_1_THREAD_URI;
21 import static android.provider.Telephony.RcsColumns.RcsEventTypes.ICON_CHANGED_EVENT_TYPE;
22 import static android.provider.Telephony.RcsColumns.RcsEventTypes.NAME_CHANGED_EVENT_TYPE;
23 import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE;
24 import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_LEFT_EVENT_TYPE;
25 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.CONTENT_TYPE_COLUMN;
26 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.CONTENT_URI_COLUMN;
27 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.DURATION_MILLIS_COLUMN;
28 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.FILE_SIZE_COLUMN;
29 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.FILE_TRANSFER_ID_COLUMN;
30 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.FILE_TRANSFER_URI;
31 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.HEIGHT_COLUMN;
32 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.PREVIEW_TYPE_COLUMN;
33 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.PREVIEW_URI_COLUMN;
34 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.SESSION_ID_COLUMN;
35 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.SUCCESSFULLY_TRANSFERRED_BYTES;
36 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.TRANSFER_STATUS_COLUMN;
37 import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.WIDTH_COLUMN;
38 import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.CONFERENCE_URI_COLUMN;
39 import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_ICON_COLUMN;
40 import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.GROUP_NAME_COLUMN;
41 import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.OWNER_PARTICIPANT_COLUMN;
42 import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.RCS_GROUP_THREAD_URI;
43 import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.ARRIVAL_TIMESTAMP_COLUMN;
44 import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.INCOMING_MESSAGE_URI;
45 import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN;
46 import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.SENDER_PARTICIPANT_ID_COLUMN;
47 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.GLOBAL_ID_COLUMN;
48 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.LATITUDE_COLUMN;
49 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.LONGITUDE_COLUMN;
50 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_ID_COLUMN;
51 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_TEXT_COLUMN;
52 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.ORIGINATION_TIMESTAMP_COLUMN;
53 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.STATUS_COLUMN;
54 import static android.provider.Telephony.RcsColumns.RcsMessageColumns.SUB_ID_COLUMN;
55 import static android.provider.Telephony.RcsColumns.RcsMessageDeliveryColumns.DELIVERED_TIMESTAMP_COLUMN;
56 import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.CANONICAL_ADDRESS_ID_COLUMN;
57 import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_ALIAS_COLUMN;
58 import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
59 import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_URI;
60 import static android.provider.Telephony.RcsColumns.RcsParticipantEventColumns.NEW_ALIAS_COLUMN;
61 import static android.provider.Telephony.RcsColumns.RcsThreadColumns.RCS_THREAD_ID_COLUMN;
62 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.DESTINATION_PARTICIPANT_ID_COLUMN;
63 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.NEW_ICON_URI_COLUMN;
64 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.NEW_NAME_COLUMN;
65 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.SOURCE_PARTICIPANT_ID_COLUMN;
66 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.TIMESTAMP_COLUMN;
67 import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_GROUP;
68 import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED;
69 import static android.telephony.ims.RcsEventQueryParams.EVENT_QUERY_PARAMETERS_KEY;
70 import static android.telephony.ims.RcsMessageQueryParams.MESSAGE_QUERY_PARAMETERS_KEY;
71 import static android.telephony.ims.RcsParticipantQueryParams.PARTICIPANT_QUERY_PARAMETERS_KEY;
72 import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN;
73 import static android.telephony.ims.RcsThreadQueryParams.THREAD_QUERY_PARAMETERS_KEY;
74 
75 import static com.android.internal.telephony.ims.RcsMessageStoreUtil.getMessageTableUri;
76 import static com.android.internal.telephony.ims.RcsParticipantQueryHelper.getUriForParticipant;
77 import static com.android.internal.telephony.ims.RcsThreadQueryHelper.get1To1ThreadUri;
78 import static com.android.internal.telephony.ims.RcsThreadQueryHelper.getAllParticipantsInThreadUri;
79 import static com.android.internal.telephony.ims.RcsThreadQueryHelper.getGroupThreadUri;
80 import static com.android.internal.telephony.ims.RcsThreadQueryHelper.getParticipantInThreadUri;
81 
82 import android.content.ContentResolver;
83 import android.content.ContentValues;
84 import android.content.Context;
85 import android.database.Cursor;
86 import android.net.Uri;
87 import android.os.Bundle;
88 import android.os.RemoteException;
89 import android.os.ServiceManager;
90 import android.provider.Telephony;
91 import android.telephony.Rlog;
92 import android.telephony.ims.RcsEventQueryParams;
93 import android.telephony.ims.RcsEventQueryResultDescriptor;
94 import android.telephony.ims.RcsFileTransferCreationParams;
95 import android.telephony.ims.RcsIncomingMessageCreationParams;
96 import android.telephony.ims.RcsMessageQueryParams;
97 import android.telephony.ims.RcsMessageQueryResultParcelable;
98 import android.telephony.ims.RcsMessageSnippet;
99 import android.telephony.ims.RcsMessageStore;
100 import android.telephony.ims.RcsOutgoingMessageCreationParams;
101 import android.telephony.ims.RcsParticipantQueryParams;
102 import android.telephony.ims.RcsParticipantQueryResultParcelable;
103 import android.telephony.ims.RcsQueryContinuationToken;
104 import android.telephony.ims.RcsThreadQueryParams;
105 import android.telephony.ims.RcsThreadQueryResultParcelable;
106 import android.telephony.ims.aidl.IRcs;
107 
108 import com.android.internal.annotations.VisibleForTesting;
109 
110 /**
111  * Backing implementation of {@link RcsMessageStore}.
112  */
113 public class RcsMessageStoreController extends IRcs.Stub {
114 
115     static final String TAG = "RcsMsgStoreController";
116     private static final String RCS_SERVICE_NAME = "ircs";
117 
118     private static RcsMessageStoreController sInstance;
119 
120     private final Context mContext;
121     private final ContentResolver mContentResolver;
122     private final RcsParticipantQueryHelper mParticipantQueryHelper;
123     private final RcsMessageQueryHelper mMessageQueryHelper;
124     private final RcsEventQueryHelper mEventQueryHelper;
125     private final RcsThreadQueryHelper mThreadQueryHelper;
126     private final RcsMessageStoreUtil mMessageStoreUtil;
127 
128     /**
129      * Initialize the instance. Should only be called once.
130      */
init(Context context)131     public static RcsMessageStoreController init(Context context) {
132         synchronized (RcsMessageStoreController.class) {
133             if (sInstance == null) {
134                 sInstance = new RcsMessageStoreController(context);
135                 if (ServiceManager.getService(RCS_SERVICE_NAME) == null) {
136                     ServiceManager.addService(RCS_SERVICE_NAME, sInstance);
137                 }
138             } else {
139                 Rlog.e(TAG, "init() called multiple times! sInstance = " + sInstance);
140             }
141         }
142         return sInstance;
143     }
144 
145     interface ThrowingSupplier<T> {
get()146         T get() throws RemoteException;
147     }
148 
149     interface ThrowingRunnable {
run()150         void run() throws RemoteException;
151     }
152 
performWriteOperation(String callingPackage, ThrowingRunnable fn)153     private void performWriteOperation(String callingPackage, ThrowingRunnable fn) {
154         RcsPermissions.checkWritePermissions(mContext, callingPackage);
155 
156         try {
157             fn.run();
158         } catch (RemoteException e) {
159             throw new RuntimeException(e);
160         }
161     }
162 
performCreateOperation(String callingPackage, ThrowingSupplier<T> fn)163     private <T> T performCreateOperation(String callingPackage, ThrowingSupplier<T> fn) {
164         RcsPermissions.checkWritePermissions(mContext, callingPackage);
165 
166         try {
167             return fn.get();
168         } catch (RemoteException e) {
169             throw new RuntimeException(e);
170         }
171     }
172 
performReadOperation(String callingPackage, ThrowingSupplier<T> fn)173     private <T> T performReadOperation(String callingPackage, ThrowingSupplier<T> fn) {
174         RcsPermissions.checkReadPermissions(mContext, callingPackage);
175 
176         try {
177             return fn.get();
178         } catch (RemoteException e) {
179             throw new RuntimeException(e);
180         }
181     }
182 
183     @VisibleForTesting
RcsMessageStoreController(Context context)184     public RcsMessageStoreController(Context context) {
185         mContext = context;
186         mContentResolver = context.getContentResolver();
187         mParticipantQueryHelper = new RcsParticipantQueryHelper(mContentResolver);
188         mMessageQueryHelper = new RcsMessageQueryHelper(mContentResolver);
189         mThreadQueryHelper = new RcsThreadQueryHelper(mContentResolver, mParticipantQueryHelper);
190         mEventQueryHelper = new RcsEventQueryHelper(mContentResolver);
191         mMessageStoreUtil = new RcsMessageStoreUtil(mContentResolver);
192     }
193 
194     @Override
deleteThread(int threadId, int threadType, String callingPackage)195     public boolean deleteThread(int threadId, int threadType, String callingPackage) {
196         return performCreateOperation(callingPackage, () -> {
197             int deletionCount = mContentResolver.delete(
198                     threadType == THREAD_TYPE_GROUP ? RCS_GROUP_THREAD_URI : RCS_1_TO_1_THREAD_URI,
199                     RCS_THREAD_ID_COLUMN + "=?",
200                     new String[]{Integer.toString(threadId)});
201 
202             return deletionCount > 0;
203         });
204     }
205 
206     @Override
getMessageSnippet(int threadId, String callingPackage)207     public RcsMessageSnippet getMessageSnippet(int threadId, String callingPackage) {
208         return performReadOperation(callingPackage, () -> {
209             // TODO - implement
210             return null;
211         });
212     }
213 
214     @Override
215     public RcsThreadQueryResultParcelable getRcsThreads(RcsThreadQueryParams queryParameters,
216             String callingPackage) {
217         return performReadOperation(callingPackage, () -> {
218             Bundle bundle = new Bundle();
219             bundle.putParcelable(THREAD_QUERY_PARAMETERS_KEY, queryParameters);
220             return mThreadQueryHelper.performThreadQuery(bundle);
221         });
222     }
223 
224     @Override
225     public RcsThreadQueryResultParcelable getRcsThreadsWithToken(
226             RcsQueryContinuationToken continuationToken, String callingPackage) {
227         return performReadOperation(callingPackage, () -> {
228             Bundle bundle = new Bundle();
229             bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
230             return mThreadQueryHelper.performThreadQuery(bundle);
231         });
232     }
233 
234     @Override
235     public RcsParticipantQueryResultParcelable getParticipants(
236             RcsParticipantQueryParams queryParameters, String callingPackage) {
237         return performReadOperation(callingPackage, () -> {
238             Bundle bundle = new Bundle();
239             bundle.putParcelable(PARTICIPANT_QUERY_PARAMETERS_KEY, queryParameters);
240             return mParticipantQueryHelper.performParticipantQuery(bundle);
241         });
242     }
243 
244     @Override
245     public RcsParticipantQueryResultParcelable getParticipantsWithToken(
246             RcsQueryContinuationToken continuationToken, String callingPackage) {
247         return performReadOperation(callingPackage, () -> {
248             Bundle bundle = new Bundle();
249             bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
250             return mParticipantQueryHelper.performParticipantQuery(bundle);
251         });
252     }
253 
254     @Override
255     public RcsMessageQueryResultParcelable getMessages(RcsMessageQueryParams queryParams,
256             String callingPackage) {
257         return performReadOperation(callingPackage, () -> {
258             Bundle bundle = new Bundle();
259             bundle.putParcelable(MESSAGE_QUERY_PARAMETERS_KEY, queryParams);
260             return mMessageQueryHelper.performMessageQuery(bundle);
261         });
262     }
263 
264     @Override
265     public RcsMessageQueryResultParcelable getMessagesWithToken(
266             RcsQueryContinuationToken continuationToken, String callingPackage) {
267         return performReadOperation(callingPackage, () -> {
268             Bundle bundle = new Bundle();
269             bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
270             return mMessageQueryHelper.performMessageQuery(bundle);
271         });
272     }
273 
274     @Override
275     public RcsEventQueryResultDescriptor getEvents(RcsEventQueryParams queryParameters,
276             String callingPackage) {
277         return performReadOperation(callingPackage, () -> {
278             Bundle bundle = new Bundle();
279             bundle.putParcelable(EVENT_QUERY_PARAMETERS_KEY, queryParameters);
280             return mEventQueryHelper.performEventQuery(bundle);
281         });
282     }
283 
284     @Override
285     public RcsEventQueryResultDescriptor getEventsWithToken(
286             RcsQueryContinuationToken continuationToken, String callingPackage) {
287         return performReadOperation(callingPackage, () -> {
288             Bundle bundle = new Bundle();
289             bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
290             return mEventQueryHelper.performEventQuery(bundle);
291         });
292     }
293 
294     @Override
295     public int createRcs1To1Thread(int recipientId, String callingPackage) {
296         return performCreateOperation(callingPackage,
297                 () -> mThreadQueryHelper.create1To1Thread(recipientId));
298     }
299 
300     @Override
301     public int createGroupThread(int[] participantIds, String groupName, Uri groupIcon,
302             String callingPackage) {
303         return performCreateOperation(callingPackage, () -> {
304             int groupThreadId = mThreadQueryHelper.createGroupThread(groupName, groupIcon);
305             if (groupThreadId <= 0) {
306                 throw new RemoteException("Could not create RcsGroupThread.");
307             }
308 
309             // Insert participants
310             // TODO(123718879): Instead of adding participants here, add them under RcsProvider
311             //  under one transaction
312             if (participantIds != null) {
313                 for (int participantId : participantIds) {
314                     addParticipantToGroupThread(groupThreadId, participantId, callingPackage);
315                 }
316             }
317 
318             return groupThreadId;
319         });
320     }
321 
322     /**
323      * TODO(109759350) Instead of sending the update query directly to RcsProvider, this function
324      * orchestrates between RcsProvider and MmsSmsProvider. This is because we are not fully decided
325      * on whether we should have RCS storage in a separate database file.
326      */
327     @Override
328     public int createRcsParticipant(String canonicalAddress, String alias, String callingPackage) {
329         return performCreateOperation(callingPackage, () -> {
330             ContentValues contentValues = new ContentValues();
331 
332             long canonicalAddressId = Telephony.RcsColumns.RcsCanonicalAddressHelper
333                     .getOrCreateCanonicalAddressId(mContentResolver, canonicalAddress);
334 
335             if (canonicalAddressId == TRANSACTION_FAILED) {
336                 throw new RemoteException("Could not create or make canonical address entry");
337             }
338 
339             contentValues.put(CANONICAL_ADDRESS_ID_COLUMN, canonicalAddressId);
340             contentValues.put(RCS_ALIAS_COLUMN, alias);
341 
342             // TODO (123719857) - Disallow creation of duplicate participants
343             Uri newParticipantUri = mContentResolver.insert(RCS_PARTICIPANT_URI, contentValues);
344             int newParticipantRowId;
345 
346             if (newParticipantUri == null) {
347                 throw new RemoteException("Error inserting new participant into RcsProvider");
348             }
349 
350             try {
351                 newParticipantRowId = Integer.parseInt(newParticipantUri.getLastPathSegment());
352             } catch (NumberFormatException e) {
353                 throw new RemoteException(
354                         "Uri returned after creating a participant is malformed: "
355                                 + newParticipantUri);
356             }
357 
358             return newParticipantRowId;
359         });
360     }
361 
362     @Override
363     public String getRcsParticipantCanonicalAddress(int participantId, String callingPackage) {
364         return performReadOperation(callingPackage,
365                 () -> mMessageStoreUtil.getStringValueFromTableRow(RCS_PARTICIPANT_URI,
366                         Telephony.CanonicalAddressesColumns.ADDRESS, RCS_PARTICIPANT_ID_COLUMN,
367                         participantId));
368     }
369 
370     @Override
371     public String getRcsParticipantAlias(int participantId, String callingPackage) {
372         return performReadOperation(callingPackage,
373                 () -> mMessageStoreUtil.getStringValueFromTableRow(RCS_PARTICIPANT_URI,
374                         RCS_ALIAS_COLUMN,
375                         RCS_PARTICIPANT_ID_COLUMN, participantId));
376     }
377 
378     @Override
379     public void setRcsParticipantAlias(int id, String alias, String callingPackage) {
380         performWriteOperation(callingPackage,
381                 () -> mMessageStoreUtil.updateValueOfProviderUri(getUriForParticipant(id),
382                         RCS_ALIAS_COLUMN,
383                         alias, "Could not update RCS participant alias"));
384     }
385 
386     @Override
387     public String getRcsParticipantContactId(int participantId, String callingPackage) {
388         return performReadOperation(callingPackage, () -> {
389             // TODO - implement
390             return null;
391         });
392     }
393 
394     @Override
395     public void setRcsParticipantContactId(int participantId, String contactId,
396             String callingPackage) {
397         performWriteOperation(callingPackage, () -> {
398             // TODO - implement
399         });
400     }
401 
402     @Override
403     public void set1To1ThreadFallbackThreadId(int rcsThreadId, long fallbackId,
404             String callingPackage) {
405         performWriteOperation(callingPackage,
406                 () -> mMessageStoreUtil.updateValueOfProviderUri(get1To1ThreadUri(rcsThreadId),
407                         FALLBACK_THREAD_ID_COLUMN, fallbackId, "Could not set fallback thread ID"));
408     }
409 
410     @Override
411     public long get1To1ThreadFallbackThreadId(int rcsThreadId, String callingPackage) {
412         return performReadOperation(callingPackage,
413                 () -> mMessageStoreUtil.getLongValueFromTableRow(RCS_1_TO_1_THREAD_URI,
414                         FALLBACK_THREAD_ID_COLUMN,
415                         RCS_THREAD_ID_COLUMN, rcsThreadId));
416     }
417 
418     @Override
419     public int get1To1ThreadOtherParticipantId(int rcsThreadId, String callingPackage) {
420         return performReadOperation(callingPackage, () -> {
421             Uri uri = get1To1ThreadUri(rcsThreadId);
422             String[] projection = new String[]{RCS_PARTICIPANT_ID_COLUMN};
423             try (Cursor cursor = mContentResolver.query(uri, projection, null, null)) {
424                 if (cursor == null || cursor.getCount() != 1) {
425                     throw new RemoteException("Could not get the thread recipient");
426                 }
427                 cursor.moveToNext();
428                 return cursor.getInt(
429                         cursor.getColumnIndex(RCS_PARTICIPANT_ID_COLUMN));
430             }
431         });
432     }
433 
434     @Override
435     public void setGroupThreadName(int rcsThreadId, String groupName, String callingPackage) {
436         performWriteOperation(callingPackage,
437                 () -> mMessageStoreUtil.updateValueOfProviderUri(getGroupThreadUri(rcsThreadId),
438                         GROUP_NAME_COLUMN, groupName, "Could not update group name"));
439     }
440 
441     @Override
442     public String getGroupThreadName(int rcsThreadId, String callingPackage) {
443         return performReadOperation(callingPackage,
444                 () -> mMessageStoreUtil.getStringValueFromTableRow(RCS_GROUP_THREAD_URI,
445                         GROUP_NAME_COLUMN,
446                         RCS_THREAD_ID_COLUMN, rcsThreadId));
447     }
448 
449     @Override
450     public void setGroupThreadIcon(int rcsThreadId, Uri groupIcon, String callingPackage) {
451         performWriteOperation(callingPackage,
452                 () -> mMessageStoreUtil.updateValueOfProviderUri(getGroupThreadUri(rcsThreadId),
453                         GROUP_ICON_COLUMN, groupIcon, "Could not update group icon"));
454     }
455 
456     @Override
457     public Uri getGroupThreadIcon(int rcsThreadId, String callingPackage) {
458         return performReadOperation(callingPackage,
459                 () -> mMessageStoreUtil.getUriValueFromTableRow(RCS_GROUP_THREAD_URI,
460                         GROUP_ICON_COLUMN,
461                         RCS_THREAD_ID_COLUMN, rcsThreadId));
462     }
463 
464     @Override
465     public void setGroupThreadOwner(int rcsThreadId, int participantId, String callingPackage) {
466         performWriteOperation(callingPackage,
467                 () -> mMessageStoreUtil.updateValueOfProviderUri(getGroupThreadUri(rcsThreadId),
468                         OWNER_PARTICIPANT_COLUMN, participantId, "Could not set the group owner"));
469     }
470 
471     @Override
472     public int getGroupThreadOwner(int rcsThreadId, String callingPackage) {
473         return performReadOperation(callingPackage,
474                 () -> mMessageStoreUtil.getIntValueFromTableRow(RCS_GROUP_THREAD_URI,
475                         OWNER_PARTICIPANT_COLUMN,
476                         RCS_THREAD_ID_COLUMN, rcsThreadId));
477     }
478 
479     @Override
480     public void setGroupThreadConferenceUri(int rcsThreadId, Uri uri, String callingPackage) {
481         performWriteOperation(callingPackage,
482                 () -> mMessageStoreUtil.updateValueOfProviderUri(getGroupThreadUri(rcsThreadId),
483                         CONFERENCE_URI_COLUMN, uri, "Could not set the conference URI for group"));
484     }
485 
486     @Override
487     public Uri getGroupThreadConferenceUri(int rcsThreadId, String callingPackage) {
488         return performReadOperation(callingPackage,
489                 () -> mMessageStoreUtil.getUriValueFromTableRow(RCS_GROUP_THREAD_URI,
490                         CONFERENCE_URI_COLUMN, RCS_THREAD_ID_COLUMN, rcsThreadId));
491     }
492 
493     @Override
494     public void addParticipantToGroupThread(int rcsThreadId, int participantId,
495             String callingPackage) {
496         performWriteOperation(callingPackage, () -> {
497             ContentValues contentValues = new ContentValues(2);
498             contentValues.put(RCS_THREAD_ID_COLUMN, rcsThreadId);
499             contentValues.put(RCS_PARTICIPANT_ID_COLUMN, participantId);
500 
501             mContentResolver.insert(getAllParticipantsInThreadUri(rcsThreadId), contentValues);
502         });
503     }
504 
505     @Override
506     public void removeParticipantFromGroupThread(int rcsThreadId, int participantId,
507             String callingPackage) {
508         performWriteOperation(callingPackage,
509                 () -> mContentResolver.delete(getParticipantInThreadUri(rcsThreadId, participantId),
510                         null,
511                         null));
512     }
513 
514     @Override
515     public int addIncomingMessage(int rcsThreadId,
516             RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams,
517             String callingPackage) {
518         return performCreateOperation(callingPackage, () -> {
519             ContentValues contentValues = new ContentValues();
520 
521             contentValues.put(ARRIVAL_TIMESTAMP_COLUMN,
522                     rcsIncomingMessageCreationParams.getArrivalTimestamp());
523             contentValues.put(SEEN_TIMESTAMP_COLUMN,
524                     rcsIncomingMessageCreationParams.getSeenTimestamp());
525             contentValues.put(SENDER_PARTICIPANT_ID_COLUMN,
526                     rcsIncomingMessageCreationParams.getSenderParticipantId());
527 
528             mMessageQueryHelper.createContentValuesForGenericMessage(contentValues, rcsThreadId,
529                     rcsIncomingMessageCreationParams);
530 
531             return addMessage(rcsThreadId, true, contentValues);
532         });
533     }
534 
535     @Override
536     public int addOutgoingMessage(int rcsThreadId,
537             RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParameters,
538             String callingPackage) {
539         return performCreateOperation(callingPackage, () -> {
540             ContentValues contentValues = new ContentValues();
541 
542             mMessageQueryHelper.createContentValuesForGenericMessage(contentValues, rcsThreadId,
543                     rcsOutgoingMessageCreationParameters);
544 
545             return addMessage(rcsThreadId, false, contentValues);
546         });
547     }
548 
549     private int addMessage(int rcsThreadId, boolean isIncoming, ContentValues contentValues)
550             throws RemoteException {
551         Uri uri = mContentResolver.insert(mMessageQueryHelper.getMessageInsertionUri(isIncoming),
552                 contentValues);
553 
554         if (uri == null) {
555             throw new RemoteException(
556                     "Could not create message on thread, threadId: " + rcsThreadId);
557         }
558 
559         return Integer.parseInt(uri.getLastPathSegment());
560     }
561 
562     @Override
563     public void deleteMessage(int messageId, boolean isIncoming, int rcsThreadId, boolean isGroup,
564             String callingPackage) {
565         performWriteOperation(callingPackage, () -> mContentResolver.delete(
566                 mMessageQueryHelper.getMessageDeletionUri(messageId, isIncoming, rcsThreadId,
567                         isGroup),
568                 null, null));
569     }
570 
571     @Override
572     public void setMessageSubId(int messageId, boolean isIncoming, int subId,
573             String callingPackage) {
574         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
575                 mMessageQueryHelper.getMessageUpdateUri(messageId, isIncoming), SUB_ID_COLUMN,
576                 subId, "Could not set subscription ID for message"));
577     }
578 
579     @Override
580     public int getMessageSubId(int messageId, boolean isIncoming, String callingPackage) {
581         return performReadOperation(callingPackage,
582                 () -> mMessageStoreUtil.getIntValueFromTableRow(getMessageTableUri(isIncoming),
583                         SUB_ID_COLUMN, MESSAGE_ID_COLUMN, messageId));
584     }
585 
586     @Override
587     public void setMessageStatus(int messageId, boolean isIncoming, int status,
588             String callingPackage) {
589         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
590                 mMessageQueryHelper.getMessageUpdateUri(messageId, isIncoming), STATUS_COLUMN,
591                 status, "Could not set the status for message"));
592     }
593 
594     @Override
595     public int getMessageStatus(int messageId, boolean isIncoming, String callingPackage) {
596         return performReadOperation(callingPackage,
597                 () -> mMessageStoreUtil.getIntValueFromTableRow(getMessageTableUri(isIncoming),
598                         STATUS_COLUMN, MESSAGE_ID_COLUMN, messageId));
599     }
600 
601     @Override
602     public void setMessageOriginationTimestamp(int messageId, boolean isIncoming,
603             long originationTimestamp, String callingPackage) {
604         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
605                 mMessageQueryHelper.getMessageUpdateUri(messageId, isIncoming),
606                 ORIGINATION_TIMESTAMP_COLUMN, originationTimestamp,
607                 "Could not set the origination timestamp for message"));
608     }
609 
610     @Override
611     public long getMessageOriginationTimestamp(int messageId, boolean isIncoming,
612             String callingPackage) {
613         return performReadOperation(callingPackage,
614                 () -> mMessageStoreUtil.getLongValueFromTableRow(getMessageTableUri(isIncoming),
615                         ORIGINATION_TIMESTAMP_COLUMN, MESSAGE_ID_COLUMN, messageId));
616     }
617 
618     @Override
619     public void setGlobalMessageIdForMessage(int messageId, boolean isIncoming, String globalId,
620             String callingPackage) {
621         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
622                 mMessageQueryHelper.getMessageUpdateUri(messageId, isIncoming),
623                 GLOBAL_ID_COLUMN,
624                 globalId, "Could not set the global ID for message"));
625     }
626 
627     @Override
628     public String getGlobalMessageIdForMessage(int messageId, boolean isIncoming,
629             String callingPackage) {
630         return performReadOperation(callingPackage,
631                 () -> mMessageStoreUtil.getStringValueFromTableRow(getMessageTableUri(isIncoming),
632                         GLOBAL_ID_COLUMN, MESSAGE_ID_COLUMN, messageId));
633     }
634 
635     @Override
636     public void setMessageArrivalTimestamp(int messageId, boolean isIncoming,
637             long arrivalTimestamp, String callingPackage) {
638         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
639                 mMessageQueryHelper.getMessageUpdateUri(messageId, isIncoming),
640                 ARRIVAL_TIMESTAMP_COLUMN, arrivalTimestamp,
641                 "Could not update the arrival timestamp for message"));
642     }
643 
644     @Override
645     public long getMessageArrivalTimestamp(int messageId, boolean isIncoming,
646             String callingPackage) {
647         return performReadOperation(callingPackage,
648                 () -> mMessageStoreUtil.getLongValueFromTableRow(getMessageTableUri(isIncoming),
649                         ARRIVAL_TIMESTAMP_COLUMN, MESSAGE_ID_COLUMN, messageId));
650     }
651 
652     @Override
653     public void setMessageSeenTimestamp(int messageId, boolean isIncoming,
654             long notifiedTimestamp, String callingPackage) {
655         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
656                 mMessageQueryHelper.getMessageUpdateUri(messageId, isIncoming),
657                 SEEN_TIMESTAMP_COLUMN, notifiedTimestamp,
658                 "Could not set the notified timestamp for message"));
659     }
660 
661     @Override
662     public long getMessageSeenTimestamp(int messageId, boolean isIncoming, String callingPackage) {
663         return performReadOperation(callingPackage,
664                 () -> mMessageStoreUtil.getLongValueFromTableRow(getMessageTableUri(isIncoming),
665                         SEEN_TIMESTAMP_COLUMN, MESSAGE_ID_COLUMN, messageId));
666     }
667 
668     @Override
669     public int[] getMessageRecipients(int messageId, String callingPackage) {
670         return performReadOperation(callingPackage,
671                 () -> mMessageQueryHelper.getDeliveryParticipantsForMessage(messageId));
672     }
673 
674     @Override
675     public long getOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId,
676             String callingPackage) {
677         return performReadOperation(callingPackage,
678                 () -> mMessageQueryHelper.getLongValueFromDelivery(messageId, participantId,
679                         DELIVERED_TIMESTAMP_COLUMN));
680     }
681 
682     @Override
683     public void setOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId,
684             long deliveredTimestamp, String callingPackage) {
685         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
686                 mMessageQueryHelper.getMessageDeliveryUri(messageId, participantId),
687                 DELIVERED_TIMESTAMP_COLUMN, deliveredTimestamp,
688                 "Could not update the delivered timestamp for outgoing delivery"));
689     }
690 
691     @Override
692     public long getOutgoingDeliverySeenTimestamp(int messageId, int participantId,
693             String callingPackage) {
694         return performReadOperation(callingPackage,
695                 () -> mMessageQueryHelper.getLongValueFromDelivery(messageId, participantId,
696                         SEEN_TIMESTAMP_COLUMN));
697     }
698 
699     @Override
700     public void setOutgoingDeliverySeenTimestamp(int messageId, int participantId,
701             long seenTimestamp, String callingPackage) {
702         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
703                 mMessageQueryHelper.getMessageDeliveryUri(messageId, participantId),
704                 SEEN_TIMESTAMP_COLUMN, seenTimestamp,
705                 "Could not update the seen timestamp for outgoing delivery"));
706     }
707 
708     @Override
709     public int getOutgoingDeliveryStatus(int messageId, int participantId, String callingPackage) {
710         return performReadOperation(callingPackage, () -> {
711             // TODO - implement
712             return 0;
713         });
714     }
715 
716     @Override
717     public void setOutgoingDeliveryStatus(int messageId, int participantId, int status,
718             String callingPackage) {
719         performWriteOperation(callingPackage, () -> {
720             // TODO - implement
721         });
722     }
723 
724     @Override
725     public void setTextForMessage(int messageId, boolean isIncoming, String text,
726             String callingPackage) {
727         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
728                 mMessageQueryHelper.getMessageUpdateUri(messageId, isIncoming),
729                 MESSAGE_TEXT_COLUMN,
730                 text, "Could not set the text for message"));
731     }
732 
733     @Override
734     public String getTextForMessage(int messageId, boolean isIncoming, String callingPackage) {
735         return performReadOperation(callingPackage,
736                 () -> mMessageStoreUtil.getStringValueFromTableRow(getMessageTableUri(isIncoming),
737                         MESSAGE_TEXT_COLUMN, MESSAGE_ID_COLUMN, messageId));
738     }
739 
740     @Override
741     public void setLatitudeForMessage(int messageId, boolean isIncoming, double latitude,
742             String callingPackage) {
743         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
744                 mMessageQueryHelper.getMessageUpdateUri(messageId, isIncoming), LATITUDE_COLUMN,
745                 latitude, "Could not update latitude for message"));
746     }
747 
748     @Override
749     public double getLatitudeForMessage(int messageId, boolean isIncoming, String callingPackage) {
750         return performReadOperation(callingPackage,
751                 () -> mMessageStoreUtil.getDoubleValueFromTableRow(getMessageTableUri(isIncoming),
752                         LATITUDE_COLUMN, MESSAGE_ID_COLUMN, messageId));
753     }
754 
755     @Override
756     public void setLongitudeForMessage(int messageId, boolean isIncoming, double longitude,
757             String callingPackage) {
758         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
759                 mMessageQueryHelper.getMessageUpdateUri(messageId, isIncoming),
760                 LONGITUDE_COLUMN,
761                 longitude, "Could not set longitude for message"));
762     }
763 
764     @Override
765     public double getLongitudeForMessage(int messageId, boolean isIncoming, String callingPackage) {
766         return performReadOperation(callingPackage,
767                 () -> mMessageStoreUtil.getDoubleValueFromTableRow(getMessageTableUri(isIncoming),
768                         LONGITUDE_COLUMN, MESSAGE_ID_COLUMN, messageId));
769     }
770 
771     @Override
772     public int[] getFileTransfersAttachedToMessage(int messageId, boolean isIncoming,
773             String callingPackage) {
774         return performReadOperation(callingPackage, () -> {
775             // TODO - implement
776             return new int[0];
777         });
778     }
779 
780     @Override
781     public int getSenderParticipant(int messageId, String callingPackage) {
782         return performReadOperation(callingPackage,
783                 () -> mMessageStoreUtil.getIntValueFromTableRow(INCOMING_MESSAGE_URI,
784                         SENDER_PARTICIPANT_ID_COLUMN, MESSAGE_ID_COLUMN, messageId));
785     }
786 
787     @Override
788     public void deleteFileTransfer(int partId, String callingPackage) {
789         performWriteOperation(callingPackage,
790                 () -> mContentResolver.delete(mMessageQueryHelper.getFileTransferUpdateUri(partId),
791                         null,
792                         null));
793     }
794 
795     @Override
796     public int storeFileTransfer(int messageId, boolean isIncoming,
797             RcsFileTransferCreationParams fileTransferCreationParameters, String callingPackage) {
798         return performCreateOperation(callingPackage, () -> {
799             ContentValues contentValues = mMessageQueryHelper.getContentValuesForFileTransfer(
800                     fileTransferCreationParameters);
801             Uri uri = mContentResolver.insert(
802                     mMessageQueryHelper.getFileTransferInsertionUri(messageId), contentValues);
803 
804             if (uri != null) {
805                 return Integer.parseInt(uri.getLastPathSegment());
806             }
807 
808             return TRANSACTION_FAILED;
809         });
810     }
811 
812     @Override
813     public void setFileTransferSessionId(int partId, String sessionId, String callingPackage) {
814         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
815                 mMessageQueryHelper.getFileTransferUpdateUri(partId), SESSION_ID_COLUMN,
816                 sessionId,
817                 "Could not set session ID for file transfer"));
818     }
819 
820     @Override
821     public String getFileTransferSessionId(int partId, String callingPackage) {
822         return performReadOperation(callingPackage,
823                 () -> mMessageStoreUtil.getStringValueFromTableRow(FILE_TRANSFER_URI,
824                         SESSION_ID_COLUMN,
825                         FILE_TRANSFER_ID_COLUMN, partId));
826     }
827 
828     @Override
829     public void setFileTransferContentUri(int partId, Uri contentUri, String callingPackage) {
830         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
831                 mMessageQueryHelper.getFileTransferUpdateUri(partId), CONTENT_URI_COLUMN,
832                 contentUri, "Could not set content URI for file transfer"));
833     }
834 
835     @Override
836     public Uri getFileTransferContentUri(int partId, String callingPackage) {
837         return performReadOperation(callingPackage,
838                 () -> mMessageStoreUtil.getUriValueFromTableRow(FILE_TRANSFER_URI,
839                         CONTENT_URI_COLUMN,
840                         FILE_TRANSFER_ID_COLUMN, partId));
841     }
842 
843     @Override
844     public void setFileTransferContentType(int partId, String contentType, String callingPackage) {
845         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
846                 mMessageQueryHelper.getFileTransferUpdateUri(partId), CONTENT_TYPE_COLUMN,
847                 contentType, "Could not set content type for file transfer"));
848     }
849 
850     @Override
851     public String getFileTransferContentType(int partId, String callingPackage) {
852         return performReadOperation(callingPackage,
853                 () -> mMessageStoreUtil.getStringValueFromTableRow(FILE_TRANSFER_URI,
854                         CONTENT_TYPE_COLUMN,
855                         FILE_TRANSFER_ID_COLUMN, partId));
856     }
857 
858     @Override
859     public void setFileTransferFileSize(int partId, long fileSize, String callingPackage) {
860         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
861                 mMessageQueryHelper.getFileTransferUpdateUri(partId), FILE_SIZE_COLUMN,
862                 fileSize,
863                 "Could not set file size for file transfer"));
864     }
865 
866     @Override
867     public long getFileTransferFileSize(int partId, String callingPackage) {
868         return performReadOperation(callingPackage,
869                 () -> mMessageStoreUtil.getLongValueFromTableRow(FILE_TRANSFER_URI,
870                         FILE_SIZE_COLUMN,
871                         FILE_TRANSFER_ID_COLUMN, partId));
872     }
873 
874     @Override
875     public void setFileTransferTransferOffset(int partId, long transferOffset,
876             String callingPackage) {
877         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
878                 mMessageQueryHelper.getFileTransferUpdateUri(partId),
879                 SUCCESSFULLY_TRANSFERRED_BYTES,
880                 transferOffset, "Could not set transfer offset for file transfer"));
881     }
882 
883     @Override
884     public long getFileTransferTransferOffset(int partId, String callingPackage) {
885         return performReadOperation(callingPackage,
886                 () -> mMessageStoreUtil.getLongValueFromTableRow(FILE_TRANSFER_URI,
887                         SUCCESSFULLY_TRANSFERRED_BYTES,
888                         FILE_TRANSFER_ID_COLUMN, partId));
889     }
890 
891     @Override
892     public void setFileTransferStatus(int partId, int transferStatus, String callingPackage) {
893         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
894                 mMessageQueryHelper.getFileTransferUpdateUri(partId), TRANSFER_STATUS_COLUMN,
895                 transferStatus, "Could not set transfer status for file transfer"));
896     }
897 
898     @Override
899     public int getFileTransferStatus(int partId, String callingPackage) {
900         return performReadOperation(callingPackage,
901                 () -> mMessageStoreUtil.getIntValueFromTableRow(FILE_TRANSFER_URI, STATUS_COLUMN,
902                         FILE_TRANSFER_ID_COLUMN, partId));
903     }
904 
905     @Override
906     public void setFileTransferWidth(int partId, int width, String callingPackage) {
907         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
908                 mMessageQueryHelper.getFileTransferUpdateUri(partId), WIDTH_COLUMN, width,
909                 "Could not set width of file transfer"));
910     }
911 
912     @Override
913     public int getFileTransferWidth(int partId, String callingPackage) {
914         return performReadOperation(callingPackage,
915                 () -> mMessageStoreUtil.getIntValueFromTableRow(FILE_TRANSFER_URI, WIDTH_COLUMN,
916                         FILE_TRANSFER_ID_COLUMN, partId));
917     }
918 
919     @Override
920     public void setFileTransferHeight(int partId, int height, String callingPackage) {
921         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
922                 mMessageQueryHelper.getFileTransferUpdateUri(partId), HEIGHT_COLUMN, height,
923                 "Could not set height of file transfer"));
924     }
925 
926     @Override
927     public int getFileTransferHeight(int partId, String callingPackage) {
928         return performReadOperation(callingPackage,
929                 () -> mMessageStoreUtil.getIntValueFromTableRow(FILE_TRANSFER_URI, HEIGHT_COLUMN,
930                         FILE_TRANSFER_ID_COLUMN, partId));
931     }
932 
933     @Override
934     public void setFileTransferLength(int partId, long length, String callingPackage) {
935         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
936                 mMessageQueryHelper.getFileTransferUpdateUri(partId), DURATION_MILLIS_COLUMN,
937                 length,
938                 "Could not set length of file transfer"));
939     }
940 
941     @Override
942     public long getFileTransferLength(int partId, String callingPackage) {
943         return performReadOperation(callingPackage,
944                 () -> mMessageStoreUtil.getLongValueFromTableRow(FILE_TRANSFER_URI,
945                         DURATION_MILLIS_COLUMN,
946                         FILE_TRANSFER_ID_COLUMN, partId));
947     }
948 
949     @Override
950     public void setFileTransferPreviewUri(int partId, Uri uri, String callingPackage) {
951         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
952                 mMessageQueryHelper.getFileTransferUpdateUri(partId), PREVIEW_URI_COLUMN, uri,
953                 "Could not set preview URI of file transfer"));
954     }
955 
956     @Override
957     public Uri getFileTransferPreviewUri(int partId, String callingPackage) {
958         return performReadOperation(callingPackage,
959                 () -> mMessageStoreUtil.getUriValueFromTableRow(FILE_TRANSFER_URI,
960                         DURATION_MILLIS_COLUMN,
961                         FILE_TRANSFER_ID_COLUMN, partId));
962     }
963 
964     @Override
965     public void setFileTransferPreviewType(int partId, String type, String callingPackage) {
966         performWriteOperation(callingPackage, () -> mMessageStoreUtil.updateValueOfProviderUri(
967                 mMessageQueryHelper.getFileTransferUpdateUri(partId), PREVIEW_TYPE_COLUMN, type,
968                 "Could not set preview type of file transfer"));
969     }
970 
971     @Override
972     public String getFileTransferPreviewType(int partId, String callingPackage) {
973         return performReadOperation(callingPackage,
974                 () -> mMessageStoreUtil.getStringValueFromTableRow(FILE_TRANSFER_URI,
975                         PREVIEW_TYPE_COLUMN,
976                         FILE_TRANSFER_ID_COLUMN, partId));
977     }
978 
979     @Override
980     public int createGroupThreadNameChangedEvent(long timestamp, int threadId,
981             int originationParticipantId, String newName, String callingPackage) {
982         return performCreateOperation(callingPackage, () -> {
983             ContentValues eventSpecificValues = new ContentValues();
984             eventSpecificValues.put(NEW_NAME_COLUMN, newName);
985 
986             return mEventQueryHelper.createGroupThreadEvent(NAME_CHANGED_EVENT_TYPE, timestamp,
987                     threadId, originationParticipantId, eventSpecificValues);
988         });
989     }
990 
991     @Override
992     public int createGroupThreadIconChangedEvent(long timestamp, int threadId,
993             int originationParticipantId, Uri newIcon, String callingPackage) {
994         return performCreateOperation(callingPackage, () -> {
995             ContentValues eventSpecificValues = new ContentValues();
996             eventSpecificValues.put(NEW_ICON_URI_COLUMN,
997                     newIcon == null ? null : newIcon.toString());
998 
999             return mEventQueryHelper.createGroupThreadEvent(ICON_CHANGED_EVENT_TYPE, timestamp,
1000                     threadId, originationParticipantId, eventSpecificValues);
1001         });
1002     }
1003 
1004     @Override
1005     public int createGroupThreadParticipantJoinedEvent(long timestamp, int threadId,
1006             int originationParticipantId, int participantId, String callingPackage) {
1007         return performCreateOperation(callingPackage, () -> {
1008             ContentValues eventSpecificValues = new ContentValues();
1009             eventSpecificValues.put(DESTINATION_PARTICIPANT_ID_COLUMN, participantId);
1010 
1011             return mEventQueryHelper.createGroupThreadEvent(PARTICIPANT_JOINED_EVENT_TYPE,
1012                     timestamp,
1013                     threadId, originationParticipantId, eventSpecificValues);
1014         });
1015     }
1016 
1017     @Override
1018     public int createGroupThreadParticipantLeftEvent(long timestamp, int threadId,
1019             int originationParticipantId, int participantId, String callingPackage) {
1020         return performCreateOperation(callingPackage, () -> {
1021             ContentValues eventSpecificValues = new ContentValues();
1022             eventSpecificValues.put(DESTINATION_PARTICIPANT_ID_COLUMN, participantId);
1023 
1024             return mEventQueryHelper.createGroupThreadEvent(PARTICIPANT_LEFT_EVENT_TYPE, timestamp,
1025                     threadId, originationParticipantId, eventSpecificValues);
1026         });
1027     }
1028 
1029     @Override
1030     public int createParticipantAliasChangedEvent(long timestamp, int participantId,
1031             String newAlias, String callingPackage) {
1032         return performCreateOperation(callingPackage, () -> {
1033             ContentValues contentValues = new ContentValues(4);
1034             contentValues.put(TIMESTAMP_COLUMN, timestamp);
1035             contentValues.put(SOURCE_PARTICIPANT_ID_COLUMN, participantId);
1036             contentValues.put(NEW_ALIAS_COLUMN, newAlias);
1037 
1038             Uri uri = mContentResolver.insert(
1039                     mEventQueryHelper.getParticipantEventInsertionUri(participantId),
1040                     contentValues);
1041 
1042             if (uri == null) {
1043                 throw new RemoteException(
1044                         "Could not create RcsParticipantAliasChangedEvent with participant id: "
1045                                 + participantId);
1046             }
1047 
1048             return Integer.parseInt(uri.getLastPathSegment());
1049         });
1050     }
1051 }
1052