1 /* 2 * Copyright (c) 2015, Motorola Mobility LLC 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * - Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * - Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * - Neither the name of Motorola Mobility nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 * DAMAGE. 27 */ 28 29 package com.android.service.ims.presence; 30 31 import java.io.FileNotFoundException; 32 33 import android.content.ContentUris; 34 import android.content.ContentValues; 35 import android.content.Intent; 36 import android.content.UriMatcher; 37 import android.database.Cursor; 38 import android.database.SQLException; 39 import android.database.sqlite.SQLiteDatabase; 40 import android.database.sqlite.SQLiteQueryBuilder; 41 import android.net.Uri; 42 import android.os.ParcelFileDescriptor; 43 import android.provider.BaseColumns; 44 import android.provider.ContactsContract.Contacts; 45 import android.content.ComponentName; 46 47 import com.android.ims.RcsPresenceInfo; 48 import com.android.ims.internal.EABContract; 49 import com.android.ims.internal.Logger; 50 51 /** 52 * @author Vishal Patil (A22809) 53 */ 54 55 public class EABProvider extends DatabaseContentProvider{ 56 private Logger logger = Logger.getLogger(this.getClass().getName()); 57 58 private static final String EAB_DB_NAME = "rcseab.db"; 59 60 private static final int EAB_DB_VERSION = 2; 61 62 private static final int EAB_TABLE = 1; 63 64 private static final int EAB_TABLE_ID = 2; 65 66 private static final int EAB_GROUPITEMS_TABLE = 3; 67 68 private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH) { 69 { 70 addURI(EABContract.AUTHORITY, EABContract.EABColumns.TABLE_NAME, EAB_TABLE); 71 addURI(EABContract.AUTHORITY, EABContract.EABColumns.TABLE_NAME + "/#", EAB_TABLE_ID); 72 addURI(EABContract.AUTHORITY, EABContract.EABColumns.GROUPITEMS_NAME, 73 EAB_GROUPITEMS_TABLE); 74 } 75 }; 76 77 /* Statement to create VMM Album table. */ 78 private static final String EAB_CREATE_STATEMENT = "create table if not exists " 79 + EABContract.EABColumns.TABLE_NAME 80 + "(" 81 + EABContract.EABColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 82 + EABContract.EABColumns.CONTACT_NAME + " TEXT, " 83 + EABContract.EABColumns.CONTACT_NUMBER + " TEXT, " 84 + EABContract.EABColumns.RAW_CONTACT_ID + " TEXT, " 85 + EABContract.EABColumns.CONTACT_ID + " TEXT, " 86 + EABContract.EABColumns.DATA_ID + " TEXT, " 87 + EABContract.EABColumns.ACCOUNT_TYPE + " TEXT, " 88 + EABContract.EABColumns.VOLTE_CALL_SERVICE_CONTACT_ADDRESS + " TEXT, " 89 + EABContract.EABColumns.VOLTE_CALL_CAPABILITY + " INTEGER, " 90 + EABContract.EABColumns.VOLTE_CALL_CAPABILITY_TIMESTAMP + " LONG, " 91 + EABContract.EABColumns.VOLTE_CALL_AVAILABILITY + " INTEGER, " 92 + EABContract.EABColumns.VOLTE_CALL_AVAILABILITY_TIMESTAMP + " LONG, " 93 + EABContract.EABColumns.VIDEO_CALL_SERVICE_CONTACT_ADDRESS + " TEXT, " 94 + EABContract.EABColumns.VIDEO_CALL_CAPABILITY + " INTEGER, " 95 + EABContract.EABColumns.VIDEO_CALL_CAPABILITY_TIMESTAMP + " LONG, " 96 + EABContract.EABColumns.VIDEO_CALL_AVAILABILITY + " INTEGER, " 97 + EABContract.EABColumns.VIDEO_CALL_AVAILABILITY_TIMESTAMP + " LONG, " 98 + EABContract.EABColumns.CONTACT_LAST_UPDATED_TIMESTAMP + " LONG " 99 + ");"; 100 101 private static final String EAB_DROP_STATEMENT = "drop table if exists " 102 + EABContract.EABColumns.TABLE_NAME + ";"; 103 EABProvider()104 public EABProvider() { 105 super(EAB_DB_NAME, EAB_DB_VERSION); 106 } 107 108 @Override bootstrapDatabase(SQLiteDatabase db)109 public void bootstrapDatabase(SQLiteDatabase db) { 110 logger.info("Enter: bootstrapDatabase() Creating new EAB database"); 111 upgradeDatabase(db, 0, EAB_DB_VERSION); 112 logger.info("Exit: bootstrapDatabase()"); 113 } 114 115 /* 116 * In upgradeDatabase, 117 * oldVersion - the current version of Provider on the phone. 118 * newVersion - the version of Provider where the phone will be upgraded to. 119 */ 120 @Override upgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion)121 public boolean upgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion) { 122 logger.debug("Enter: upgradeDatabase() - oldVersion = " + oldVersion + 123 " newVersion = " + newVersion); 124 125 if (oldVersion == newVersion) { 126 logger.debug("upgradeDatabase oldVersion == newVersion, No Upgrade Required"); 127 return true; 128 } 129 130 try { 131 if (oldVersion == 0) { 132 db.execSQL(EAB_CREATE_STATEMENT); 133 134 oldVersion++; 135 logger.debug("upgradeDatabase : DB has been upgraded to " + oldVersion); 136 } 137 if (oldVersion == 1) { 138 addColumn(db, EABContract.EABColumns.TABLE_NAME, 139 EABContract.EABColumns.VOLTE_STATUS, "INTEGER NOT NULL DEFAULT -1"); 140 141 oldVersion++; 142 logger.debug("upgradeDatabase : DB has been upgraded to " + oldVersion); 143 } 144 } catch (SQLException exception) { 145 logger.error("Exception during upgradeDatabase. " + exception.getMessage()); 146 // DB file had problem 147 throw new InvalidDBException(); 148 } 149 150 // add further upgrade code above this 151 if (oldVersion == newVersion) { 152 logger.debug("DB upgrade complete : to " + newVersion); 153 } 154 155 logger.debug("Exit: upgradeDatabase()"); 156 return true; 157 } 158 159 @Override downgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion)160 protected boolean downgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion) { 161 // throwing the custom created Exception to catch it in 162 // getWritableDatabase or getReadableDatabase 163 throw new InvalidDBException(); 164 } 165 166 @Override deleteInternal(SQLiteDatabase db, Uri uri, String selection, String[] selectionArgs)167 protected int deleteInternal(SQLiteDatabase db, Uri uri, String selection, 168 String[] selectionArgs) { 169 logger.info("Enter: deleteInternal()"); 170 final int match = URI_MATCHER.match(uri); 171 String table = null; 172 switch (match) { 173 case EAB_TABLE_ID: 174 if (selection == null) { 175 selection = "_id=?"; 176 selectionArgs = new String[] {uri.getPathSegments().get(1)}; 177 } 178 case EAB_TABLE: 179 table = EABContract.EABColumns.TABLE_NAME; 180 break; 181 default: 182 logger.info("No match for " + uri); 183 logger.info("Exit: deleteInternal()"); 184 return 0; 185 } 186 logger.debug("Deleting from the table" + table + " selection= " + selection); 187 printDeletingValues(uri, selection, selectionArgs); 188 logger.info("Exit: deleteInternal()"); 189 return db.delete(table, selection, selectionArgs); 190 } 191 192 @Override insertInternal(SQLiteDatabase db, Uri uri, ContentValues values)193 protected Uri insertInternal(SQLiteDatabase db, Uri uri, ContentValues values) { 194 logger.info("Enter: insertInternal()"); 195 final int match = URI_MATCHER.match(uri); 196 String table = null; 197 String nullColumnHack = null; 198 switch (match) { 199 case EAB_TABLE: 200 table = EABContract.EABColumns.TABLE_NAME; 201 break; 202 default: 203 logger.warn("No match for " + uri); 204 logger.info("Exit: insertInternal() with null"); 205 return null; 206 } 207 values = verifyIfMdnExists(values); 208 // Do the insert. 209 logger.debug("Inserting to the table" + table + " values=" + values.toString()); 210 211 final long id = db.insert(table, nullColumnHack, values); 212 if (id > 0) { 213 String contactNumber = values.getAsString(EABContract.EABColumns.CONTACT_NUMBER); 214 sendInsertBroadcast(contactNumber); 215 logger.info("Exit: insertInternal()"); 216 return ContentUris.withAppendedId(uri, id); 217 } else { 218 logger.info("Exit: insertInternal() with null"); 219 return null; 220 } 221 } 222 223 @Override queryInternal(SQLiteDatabase db, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)224 protected Cursor queryInternal(SQLiteDatabase db, Uri uri, String[] projection, 225 String selection, String[] selectionArgs, String sortOrder) { 226 logger.info("Enter: queryInternal()"); 227 final int match = URI_MATCHER.match(uri); 228 229 switch (match) { 230 case EAB_TABLE_ID: 231 long id = ContentUris.parseId(uri); 232 logger.debug("queryInternal id=" + id); 233 String idSelection = BaseColumns._ID + "=" + id; 234 if(null != selection) { 235 selection = "((" + idSelection + ") AND (" + selection + "))"; 236 } else { 237 selection = idSelection; 238 } 239 break; 240 case EAB_GROUPITEMS_TABLE: 241 SQLiteQueryBuilder sqb = new SQLiteQueryBuilder(); 242 sqb.setTables(EABContract.EABColumns.TABLE_NAME); 243 String rawquery = "select DISTINCT " + EABContract.EABColumns.CONTACT_ID 244 + " from " + EABContract.EABColumns.TABLE_NAME 245 + " where " + EABContract.EABColumns.VOLTE_CALL_CAPABILITY + " >'" + 246 RcsPresenceInfo.ServiceState.OFFLINE+"' AND " 247 + EABContract.EABColumns.VIDEO_CALL_CAPABILITY + " >'" 248 + RcsPresenceInfo.ServiceState.OFFLINE + "'"; 249 StringBuffer sb = new StringBuffer(); 250 Cursor cursor = db.rawQuery(rawquery, null); 251 if (cursor != null && cursor.moveToFirst()) { 252 do { 253 if (sb.length() != 0) sb.append(","); 254 String contactId = cursor.getString(cursor 255 .getColumnIndex(EABContract.EABColumns.CONTACT_ID)); 256 sb.append(contactId); 257 } while (cursor.moveToNext()); 258 } 259 if (cursor != null) cursor.close(); 260 String contactSel = Contacts._ID + " IN ( " + sb.toString() + ")"; 261 return getContext().getContentResolver().query(Contacts.CONTENT_URI, projection, 262 contactSel, null, sortOrder); 263 } 264 265 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 266 267 String groupBy = uri.getQueryParameter("groupby"); 268 String having = null; 269 270 switch (match) { 271 case EAB_TABLE: 272 case EAB_TABLE_ID: 273 qb.setTables(EABContract.EABColumns.TABLE_NAME); 274 break; 275 default: 276 logger.warn("No match for " + uri); 277 logger.info("Exit: queryInternal()"); 278 return null; 279 } 280 logger.info("Exit: queryInternal()"); 281 return qb.query(db, projection, selection, selectionArgs, groupBy, having, sortOrder); 282 } 283 284 @Override updateInternal(SQLiteDatabase db, Uri uri, ContentValues values, String selection, String[] selectionArgs)285 protected int updateInternal(SQLiteDatabase db, Uri uri, ContentValues values, 286 String selection, String[] selectionArgs) { 287 logger.info("Enter: updateInternal()"); 288 int result = 0; 289 final int match = URI_MATCHER.match(uri); 290 291 switch (match) { 292 case EAB_TABLE_ID: 293 long id = ContentUris.parseId(uri); 294 logger.debug("updateInternal id=" + id); 295 String idSelection = BaseColumns._ID + "=" + id; 296 if(null != selection){ 297 selection = "((" + idSelection + ") AND (" + selection + "))"; 298 } else { 299 selection = idSelection; 300 } 301 break; 302 } 303 304 String table = null; 305 switch (match) { 306 case EAB_TABLE: 307 case EAB_TABLE_ID: 308 table = EABContract.EABColumns.TABLE_NAME; 309 break; 310 default: 311 logger.warn("No match for " + uri); 312 break; 313 } 314 315 if (table != null && values != null) { 316 logger.debug("Updating the table " + table + " values= " + values.toString()); 317 result = db.update(table, values, selection, selectionArgs); 318 } 319 logger.info("Exit: updateInternal()"); 320 return result; 321 } 322 323 @Override getType(Uri uri)324 public String getType(Uri uri) { 325 logger.info("Enter: getType()"); 326 final int match = URI_MATCHER.match(uri); 327 switch (match) { 328 case EAB_TABLE: 329 return EABContract.EABColumns.CONTENT_TYPE; 330 case EAB_TABLE_ID: 331 return EABContract.EABColumns.CONTENT_ITEM_TYPE; 332 default: 333 throw (new IllegalArgumentException("EABProvider URI: " + uri)); 334 } 335 } 336 337 @Override openFile(Uri uri, String mode)338 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 339 return null; 340 } 341 sendInsertBroadcast(String contactNumber)342 private void sendInsertBroadcast(String contactNumber) { 343 Intent intent = new Intent(com.android.service.ims.presence.Contacts 344 .ACTION_NEW_CONTACT_INSERTED); 345 ComponentName component = new ComponentName("com.android.service.ims.presence", 346 "com.android.service.ims.presence.AlarmBroadcastReceiver"); 347 intent.setComponent(component); 348 349 intent.putExtra(com.android.service.ims.presence.Contacts.NEW_PHONE_NUMBER, contactNumber); 350 getContext().sendBroadcast(intent); 351 } 352 verifyIfMdnExists(ContentValues cvalues)353 private ContentValues verifyIfMdnExists(ContentValues cvalues) { 354 String phoneNumber = null; 355 if (cvalues.containsKey(EABContract.EABColumns.CONTACT_NUMBER)) { 356 phoneNumber = cvalues.getAsString(EABContract.EABColumns.CONTACT_NUMBER); 357 } else { 358 return cvalues; 359 } 360 if (null == phoneNumber) { 361 return cvalues; 362 } 363 String[] projection = new String[] { 364 EABContract.EABColumns.VOLTE_CALL_SERVICE_CONTACT_ADDRESS, 365 EABContract.EABColumns.VOLTE_CALL_CAPABILITY, 366 EABContract.EABColumns.VOLTE_CALL_CAPABILITY_TIMESTAMP, 367 EABContract.EABColumns.VOLTE_CALL_AVAILABILITY, 368 EABContract.EABColumns.VOLTE_CALL_AVAILABILITY_TIMESTAMP, 369 EABContract.EABColumns.VIDEO_CALL_SERVICE_CONTACT_ADDRESS, 370 EABContract.EABColumns.VIDEO_CALL_CAPABILITY, 371 EABContract.EABColumns.VIDEO_CALL_CAPABILITY_TIMESTAMP, 372 EABContract.EABColumns.VIDEO_CALL_AVAILABILITY, 373 EABContract.EABColumns.VIDEO_CALL_AVAILABILITY_TIMESTAMP}; 374 String whereClause = "PHONE_NUMBERS_EQUAL(" + 375 EABContract.EABColumns.CONTACT_NUMBER + ", ?, 0)"; 376 String[] selectionArgs = new String[] { phoneNumber }; 377 Cursor cursor = getContext().getContentResolver().query(EABContract.EABColumns.CONTENT_URI, 378 projection, whereClause, selectionArgs, null); 379 if ((null != cursor) && (cursor.getCount() > 0)) { 380 logger.error("Inserting another copy of MDN to EAB DB."); 381 // Update data only from first cursor element. 382 cursor.moveToNext(); 383 cvalues.put(EABContract.EABColumns.VOLTE_CALL_SERVICE_CONTACT_ADDRESS, 384 cursor.getString(cursor. 385 getColumnIndex(EABContract.EABColumns.VOLTE_CALL_SERVICE_CONTACT_ADDRESS))); 386 cvalues.put(EABContract.EABColumns.VOLTE_CALL_CAPABILITY, cursor.getString(cursor 387 .getColumnIndex(EABContract.EABColumns.VOLTE_CALL_CAPABILITY))); 388 cvalues.put(EABContract.EABColumns.VOLTE_CALL_CAPABILITY_TIMESTAMP, 389 cursor.getLong(cursor 390 .getColumnIndex(EABContract.EABColumns.VOLTE_CALL_CAPABILITY_TIMESTAMP))); 391 cvalues.put(EABContract.EABColumns.VOLTE_CALL_AVAILABILITY, cursor.getString(cursor 392 .getColumnIndex(EABContract.EABColumns.VOLTE_CALL_AVAILABILITY))); 393 cvalues.put(EABContract.EABColumns.VOLTE_CALL_AVAILABILITY_TIMESTAMP, 394 cursor.getLong(cursor 395 .getColumnIndex(EABContract.EABColumns.VOLTE_CALL_AVAILABILITY_TIMESTAMP))); 396 cvalues.put(EABContract.EABColumns.VIDEO_CALL_SERVICE_CONTACT_ADDRESS, 397 cursor.getString(cursor 398 .getColumnIndex(EABContract.EABColumns.VIDEO_CALL_SERVICE_CONTACT_ADDRESS))); 399 cvalues.put(EABContract.EABColumns.VIDEO_CALL_CAPABILITY, cursor.getString(cursor 400 .getColumnIndex(EABContract.EABColumns.VIDEO_CALL_CAPABILITY))); 401 cvalues.put(EABContract.EABColumns.VIDEO_CALL_CAPABILITY_TIMESTAMP, 402 cursor.getLong(cursor 403 .getColumnIndex(EABContract.EABColumns.VIDEO_CALL_CAPABILITY_TIMESTAMP))); 404 cvalues.put(EABContract.EABColumns.VIDEO_CALL_AVAILABILITY, cursor.getString(cursor 405 .getColumnIndex(EABContract.EABColumns.VIDEO_CALL_AVAILABILITY))); 406 cvalues.put(EABContract.EABColumns.VIDEO_CALL_AVAILABILITY_TIMESTAMP, 407 cursor.getLong(cursor 408 .getColumnIndex(EABContract.EABColumns.VIDEO_CALL_AVAILABILITY_TIMESTAMP))); 409 cvalues.put(EABContract.EABColumns.CONTACT_LAST_UPDATED_TIMESTAMP, 0); 410 } 411 if (null != cursor) { 412 cursor.close(); 413 } 414 return cvalues; 415 } 416 printDeletingValues(Uri uri, String selection, String[] selectionArgs)417 private void printDeletingValues(Uri uri, String selection, String[] selectionArgs) { 418 String[] projection = new String[] { 419 EABContract.EABColumns.CONTACT_NUMBER, 420 EABContract.EABColumns.CONTACT_NAME, 421 EABContract.EABColumns.RAW_CONTACT_ID, 422 EABContract.EABColumns.CONTACT_ID, 423 EABContract.EABColumns.DATA_ID}; 424 Cursor cursor = getContext().getContentResolver().query(EABContract.EABColumns.CONTENT_URI, 425 projection, selection, selectionArgs, null); 426 if ((null != cursor) && (cursor.getCount() > 0)) { 427 logger.debug("Before deleting the cursor count is " + cursor.getCount()); 428 // Update data only from first cursor element. 429 while (cursor.moveToNext()) { 430 long dataId = cursor.getLong(cursor.getColumnIndex( 431 EABContract.EABColumns.DATA_ID)); 432 long contactId = cursor.getLong(cursor.getColumnIndex( 433 EABContract.EABColumns.CONTACT_ID)); 434 long rawContactId = cursor.getLong(cursor.getColumnIndex( 435 EABContract.EABColumns.RAW_CONTACT_ID)); 436 String phoneNumber = cursor.getString(cursor.getColumnIndex( 437 EABContract.EABColumns.CONTACT_NUMBER)); 438 String displayName = cursor.getString(cursor.getColumnIndex( 439 EABContract.EABColumns.CONTACT_NAME)); 440 } 441 } else { 442 logger.error("cursor is null!"); 443 } 444 if (null != cursor) { 445 cursor.close(); 446 } 447 } 448 } 449