• 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 package com.android.providers.telephony;
17 
18 import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
19 import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
20 import static android.provider.Telephony.RcsColumns.RcsParticipantEventColumns.NEW_ALIAS_COLUMN;
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.RcsThreadColumns.RCS_THREAD_ID_COLUMN;
26 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.DESTINATION_PARTICIPANT_ID_COLUMN;
27 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.EVENT_ID_COLUMN;
28 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.EVENT_TYPE_COLUMN;
29 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.NEW_ICON_URI_COLUMN;
30 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.NEW_NAME_COLUMN;
31 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.SOURCE_PARTICIPANT_ID_COLUMN;
32 import static android.provider.Telephony.RcsColumns.RcsThreadEventColumns.TIMESTAMP_COLUMN;
33 import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED;
34 import static android.telephony.ims.RcsEventQueryParams.ALL_EVENTS;
35 import static android.telephony.ims.RcsEventQueryParams.ALL_GROUP_THREAD_EVENTS;
36 import static android.telephony.ims.RcsEventQueryParams.EVENT_QUERY_PARAMETERS_KEY;
37 
38 import static android.telephony.ims.RcsQueryContinuationToken.EVENT_QUERY_CONTINUATION_TOKEN_TYPE;
39 import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN;
40 import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_EVENT_TABLE;
41 import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_TABLE;
42 import static com.android.providers.telephony.RcsProvider.RCS_THREAD_EVENT_TABLE;
43 import static com.android.providers.telephony.RcsProvider.RCS_THREAD_TABLE;
44 import static com.android.providers.telephony.RcsProvider.RCS_UNIFIED_EVENT_VIEW;
45 import static com.android.providers.telephony.RcsProvider.TAG;
46 import static com.android.providers.telephony.RcsProviderThreadHelper.getThreadIdFromUri;
47 import static com.android.providers.telephony.RcsProviderUtil.INSERTION_FAILED;
48 
49 import android.content.ContentValues;
50 import android.database.Cursor;
51 import android.database.sqlite.SQLiteDatabase;
52 import android.database.sqlite.SQLiteOpenHelper;
53 import android.net.Uri;
54 import android.os.Bundle;
55 import android.telephony.ims.RcsEventQueryParams;
56 import android.telephony.ims.RcsQueryContinuationToken;
57 import android.util.Log;
58 
59 import com.android.internal.annotations.VisibleForTesting;
60 
61 /**
62  * Constants and helpers related to events for {@link RcsProvider} to keep the code clean.
63  *
64  * @hide
65  */
66 class RcsProviderEventHelper {
67     private static final int PARTICIPANT_INDEX_IN_EVENT_URI = 1;
68     private static final int EVENT_INDEX_IN_EVENT_URI = 3;
69 
70     @VisibleForTesting
createRcsEventTables(SQLiteDatabase db)71     public static void createRcsEventTables(SQLiteDatabase db) {
72         Log.d(TAG, "Creating event tables");
73 
74         // Add the event tables
75         db.execSQL("CREATE TABLE " + RCS_THREAD_EVENT_TABLE + "(" + EVENT_ID_COLUMN
76                 + " INTEGER PRIMARY KEY AUTOINCREMENT, " + RCS_THREAD_ID_COLUMN + " INTEGER, "
77                 + SOURCE_PARTICIPANT_ID_COLUMN + " INTEGER, " + EVENT_TYPE_COLUMN + " INTEGER, "
78                 + TIMESTAMP_COLUMN + " INTEGER, " + DESTINATION_PARTICIPANT_ID_COLUMN + " INTEGER, "
79                 + NEW_ICON_URI_COLUMN + " TEXT, " + NEW_NAME_COLUMN + " TEXT, " + " FOREIGN KEY ("
80                 + RCS_THREAD_ID_COLUMN + ") REFERENCES " + RCS_THREAD_TABLE + " ("
81                 + RCS_THREAD_ID_COLUMN + "), FOREIGN KEY (" + SOURCE_PARTICIPANT_ID_COLUMN
82                 + ") REFERENCES " + RCS_PARTICIPANT_TABLE + " (" + RCS_PARTICIPANT_ID_COLUMN
83                 + "))");
84 
85         db.execSQL("CREATE TABLE " + RCS_PARTICIPANT_EVENT_TABLE + "(" + EVENT_ID_COLUMN
86                 + " INTEGER PRIMARY KEY AUTOINCREMENT, " + SOURCE_PARTICIPANT_ID_COLUMN +
87                 " INTEGER, " + TIMESTAMP_COLUMN + " INTEGER, "
88                 + NEW_ALIAS_COLUMN + " TEXT," + " FOREIGN KEY (" + SOURCE_PARTICIPANT_ID_COLUMN
89                 + ") REFERENCES " + RCS_PARTICIPANT_TABLE + " (" + RCS_PARTICIPANT_ID_COLUMN
90                 + "))");
91 
92         // Add the views
93 
94         // The following is a unified event view that puts every entry in both tables into one query
95         db.execSQL("CREATE VIEW " + RCS_UNIFIED_EVENT_VIEW + " AS "
96                 + "SELECT " + PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE + " AS " + EVENT_TYPE_COLUMN
97                 + ", " + EVENT_ID_COLUMN + ", " + SOURCE_PARTICIPANT_ID_COLUMN + ", "
98                 + TIMESTAMP_COLUMN + ", " + NEW_ALIAS_COLUMN + ", NULL as " + RCS_THREAD_ID_COLUMN
99                 + ", NULL as " + DESTINATION_PARTICIPANT_ID_COLUMN + ", NULL as "
100                 + NEW_ICON_URI_COLUMN + ", NULL as " + NEW_NAME_COLUMN + " "
101                 + "FROM " + RCS_PARTICIPANT_EVENT_TABLE + " "
102                 + "UNION "
103                 + "SELECT " + EVENT_TYPE_COLUMN + ", " + EVENT_ID_COLUMN + ", "
104                 + SOURCE_PARTICIPANT_ID_COLUMN + ", " + TIMESTAMP_COLUMN + ", "
105                 + "NULL as " + NEW_ALIAS_COLUMN + ", " + RCS_THREAD_ID_COLUMN + ", "
106                 + DESTINATION_PARTICIPANT_ID_COLUMN + ", " + NEW_ICON_URI_COLUMN + ", "
107                 + NEW_NAME_COLUMN + " "
108                 + "FROM " + RCS_THREAD_EVENT_TABLE);
109     }
110 
111     private final SQLiteOpenHelper mSqLiteOpenHelper;
112 
queryEvents(Bundle bundle)113     Cursor queryEvents(Bundle bundle) {
114         RcsEventQueryParams queryParameters = null;
115         RcsQueryContinuationToken continuationToken = null;
116 
117         if (bundle != null) {
118             queryParameters = bundle.getParcelable(EVENT_QUERY_PARAMETERS_KEY);
119             continuationToken = bundle.getParcelable(QUERY_CONTINUATION_TOKEN);
120         }
121 
122         if (continuationToken != null) {
123             return RcsProviderUtil.performContinuationQuery(mSqLiteOpenHelper.getReadableDatabase(),
124                     continuationToken);
125         }
126 
127         // if no query parameters were entered, build an empty query parameters object
128         if (queryParameters == null) {
129             queryParameters = new RcsEventQueryParams.Builder().build();
130         }
131 
132         return performInitialQuery(queryParameters);
133     }
134 
performInitialQuery(RcsEventQueryParams queryParameters)135     private Cursor performInitialQuery(RcsEventQueryParams queryParameters) {
136         SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
137         StringBuilder rawQuery = new StringBuilder("SELECT * FROM ").append(RCS_UNIFIED_EVENT_VIEW);
138 
139         int eventType = queryParameters.getEventType();
140         if (eventType != ALL_EVENTS) {
141             rawQuery.append(" WHERE ").append(EVENT_TYPE_COLUMN);
142             if (eventType == ALL_GROUP_THREAD_EVENTS) {
143                 rawQuery.append(" IN (").append(
144                         PARTICIPANT_JOINED_EVENT_TYPE).append(", ").append(
145                         PARTICIPANT_LEFT_EVENT_TYPE).append(", ").append(
146                         ICON_CHANGED_EVENT_TYPE).append(", ").append(
147                         NAME_CHANGED_EVENT_TYPE).append(
148                         ")");
149             } else {
150                 rawQuery.append("=").append(eventType);
151             }
152         }
153 
154         rawQuery.append(" ORDER BY ");
155 
156         int sortingProperty = queryParameters.getSortingProperty();
157         if (sortingProperty == RcsEventQueryParams.SORT_BY_TIMESTAMP) {
158             rawQuery.append(TIMESTAMP_COLUMN);
159         } else {
160             rawQuery.append(EVENT_ID_COLUMN);
161         }
162 
163         rawQuery.append(queryParameters.getSortDirection() ? " ASC " : " DESC ");
164 
165         RcsProviderUtil.appendLimit(rawQuery, queryParameters.getLimit());
166         String rawQueryAsString = rawQuery.toString();
167         Cursor cursor = db.rawQuery(rawQueryAsString, null);
168 
169         // if the query was paginated, build the next query
170         int limit = queryParameters.getLimit();
171         if (limit > 0) {
172             RcsProviderUtil.createContinuationTokenBundle(cursor,
173                     new RcsQueryContinuationToken(EVENT_QUERY_CONTINUATION_TOKEN_TYPE,
174                         rawQueryAsString, limit, limit), QUERY_CONTINUATION_TOKEN);
175         }
176 
177         return cursor;
178     }
179 
RcsProviderEventHelper(SQLiteOpenHelper sqLiteOpenHelper)180     RcsProviderEventHelper(SQLiteOpenHelper sqLiteOpenHelper) {
181         mSqLiteOpenHelper = sqLiteOpenHelper;
182     }
183 
insertParticipantEvent(Uri uri, ContentValues values)184     long insertParticipantEvent(Uri uri, ContentValues values) {
185         String participantId = getParticipantIdFromUri(uri);
186 
187         SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
188         values.put(SOURCE_PARTICIPANT_ID_COLUMN, participantId);
189         long rowId = db.insert(RCS_PARTICIPANT_EVENT_TABLE, SOURCE_PARTICIPANT_ID_COLUMN, values);
190         values.remove(SOURCE_PARTICIPANT_ID_COLUMN);
191 
192         if (rowId == INSERTION_FAILED) {
193             return TRANSACTION_FAILED;
194         }
195 
196         return rowId;
197     }
198 
insertParticipantJoinedEvent(Uri uri, ContentValues values)199     long insertParticipantJoinedEvent(Uri uri, ContentValues values) {
200         return insertGroupThreadEvent(uri, values, PARTICIPANT_JOINED_EVENT_TYPE);
201     }
202 
insertParticipantLeftEvent(Uri uri, ContentValues values)203     long insertParticipantLeftEvent(Uri uri, ContentValues values) {
204         return insertGroupThreadEvent(uri, values, PARTICIPANT_LEFT_EVENT_TYPE);
205     }
206 
insertThreadNameChangeEvent(Uri uri, ContentValues values)207     long insertThreadNameChangeEvent(Uri uri, ContentValues values) {
208         return insertGroupThreadEvent(uri, values, NAME_CHANGED_EVENT_TYPE);
209     }
210 
insertThreadIconChangeEvent(Uri uri, ContentValues values)211     long insertThreadIconChangeEvent(Uri uri, ContentValues values) {
212         return insertGroupThreadEvent(uri, values, ICON_CHANGED_EVENT_TYPE);
213     }
214 
insertGroupThreadEvent(Uri uri, ContentValues valuesParameter, int eventType)215     private long insertGroupThreadEvent(Uri uri, ContentValues valuesParameter,
216             int eventType) {
217         String threadId = getThreadIdFromUri(uri);
218 
219         SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
220         ContentValues values = new ContentValues(valuesParameter);
221         values.put(EVENT_TYPE_COLUMN, eventType);
222         values.put(RCS_THREAD_ID_COLUMN, threadId);
223         long rowId = db.insert(RCS_THREAD_EVENT_TABLE, EVENT_ID_COLUMN, values);
224 
225         if (rowId == INSERTION_FAILED) {
226             return TRANSACTION_FAILED;
227         }
228 
229         return rowId;
230     }
231 
deleteParticipantEvent(Uri uri)232     int deleteParticipantEvent(Uri uri) {
233         String eventId = getEventIdFromEventUri(uri);
234         SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
235 
236         return db.delete(RCS_PARTICIPANT_EVENT_TABLE, EVENT_ID_COLUMN + "=?",
237                 new String[]{eventId});
238     }
239 
deleteGroupThreadEvent(Uri uri)240     int deleteGroupThreadEvent(Uri uri) {
241         String eventId = getEventIdFromEventUri(uri);
242         SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
243 
244         return db.delete(RCS_THREAD_EVENT_TABLE, EVENT_ID_COLUMN + "=?", new String[]{eventId});
245     }
246 
getEventIdFromEventUri(Uri uri)247     private String getEventIdFromEventUri(Uri uri) {
248         return uri.getPathSegments().get(EVENT_INDEX_IN_EVENT_URI);
249     }
250 
getParticipantIdFromUri(Uri uri)251     private String getParticipantIdFromUri(Uri uri) {
252         return uri.getPathSegments().get(PARTICIPANT_INDEX_IN_EVENT_URI);
253     }
254 }
255