1/* 2 * Copyright (C) 2011 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 17package com.android.providers.contacts; 18 19import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses; 20 21import android.content.ContentUris; 22import android.content.ContentValues; 23import android.content.Context; 24import android.database.Cursor; 25import android.database.sqlite.SQLiteDatabase; 26import android.database.sqlite.SQLiteQueryBuilder; 27import android.net.Uri; 28import android.os.ParcelFileDescriptor; 29import android.provider.VoicemailContract.Status; 30import android.util.ArraySet; 31 32import com.android.common.content.ProjectionMap; 33import com.android.providers.contacts.VoicemailContentProvider.UriData; 34 35/** 36 * Implementation of {@link VoicemailTable.Delegate} for the voicemail status table. 37 * 38 * Public methods of this class are thread-safe as it is used in a content provider, which should 39 * be thread-safe. 40 */ 41public class VoicemailStatusTable implements VoicemailTable.Delegate { 42 43 private static final ProjectionMap sStatusProjectionMap = new ProjectionMap.Builder() 44 .add(Status._ID) 45 .add(Status.PHONE_ACCOUNT_COMPONENT_NAME) 46 .add(Status.PHONE_ACCOUNT_ID) 47 .add(Status.CONFIGURATION_STATE) 48 .add(Status.DATA_CHANNEL_STATE) 49 .add(Status.NOTIFICATION_CHANNEL_STATE) 50 .add(Status.SETTINGS_URI) 51 .add(Status.SOURCE_PACKAGE) 52 .add(Status.VOICEMAIL_ACCESS_URI) 53 .add(Status.QUOTA_OCCUPIED) 54 .add(Status.QUOTA_TOTAL) 55 .add(Status.SOURCE_TYPE) 56 .build(); 57 58 private static final Object DATABASE_LOCK = new Object(); 59 60 private final String mTableName; 61 private final Context mContext; 62 private final CallLogDatabaseHelper mDbHelper; 63 private final VoicemailTable.DelegateHelper mDelegateHelper; 64 65 public VoicemailStatusTable(String tableName, Context context, CallLogDatabaseHelper dbHelper, 66 VoicemailTable.DelegateHelper delegateHelper) { 67 mTableName = tableName; 68 mContext = context; 69 mDbHelper = dbHelper; 70 mDelegateHelper = delegateHelper; 71 } 72 73 @Override 74 public Uri insert(UriData uriData, ContentValues values) { 75 synchronized (DATABASE_LOCK) { 76 SQLiteDatabase db = mDbHelper.getWritableDatabase(); 77 // Try to update before insert. 78 String combinedClause = uriData.getWhereClause(); 79 int rowsChanged = createDatabaseModifier(db) 80 .update(uriData.getUri(), mTableName, values, combinedClause, null); 81 if (rowsChanged != 0) { 82 final String[] selection = new String[] {Status._ID}; 83 Cursor c = db.query(mTableName, selection, combinedClause, null, null, null, null); 84 c.moveToFirst(); 85 int rowId = c.getInt(0); 86 c.close(); 87 return ContentUris.withAppendedId(uriData.getUri(), rowId); 88 } 89 ContentValues copiedValues = new ContentValues(values); 90 mDelegateHelper.checkAndAddSourcePackageIntoValues(uriData, copiedValues); 91 long rowId = createDatabaseModifier(db).insert(mTableName, null, copiedValues); 92 if (rowId > 0) { 93 return ContentUris.withAppendedId(uriData.getUri(), rowId); 94 } else { 95 return null; 96 } 97 } 98 } 99 100 @Override 101 public int bulkInsert(UriData uriData, ContentValues[] values) { 102 int count = 0; 103 for (ContentValues value : values) { 104 Uri uri = insert(uriData, value); 105 if (uri != null) { 106 count++; 107 } 108 } 109 return count; 110 } 111 112 @Override 113 public int delete(UriData uriData, String selection, String[] selectionArgs) { 114 synchronized (DATABASE_LOCK) { 115 SQLiteDatabase db = mDbHelper.getWritableDatabase(); 116 String combinedClause = concatenateClauses(selection, uriData.getWhereClause()); 117 return createDatabaseModifier(db).delete(mTableName, combinedClause, 118 selectionArgs); 119 } 120 } 121 122 @Override 123 public Cursor query(UriData uriData, String[] projection, String selection, 124 String[] selectionArgs, String sortOrder) { 125 synchronized (DATABASE_LOCK) { 126 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 127 qb.setTables(mTableName); 128 qb.setProjectionMap(sStatusProjectionMap); 129 qb.setStrict(true); 130 131 String combinedClause = concatenateClauses(selection, uriData.getWhereClause()); 132 SQLiteDatabase db = mDbHelper.getReadableDatabase(); 133 Cursor c = qb 134 .query(db, projection, combinedClause, selectionArgs, null, null, sortOrder); 135 if (c != null) { 136 c.setNotificationUri(mContext.getContentResolver(), Status.CONTENT_URI); 137 } 138 return c; 139 } 140 } 141 142 @Override 143 public int update(UriData uriData, ContentValues values, String selection, 144 String[] selectionArgs) { 145 synchronized (DATABASE_LOCK) { 146 SQLiteDatabase db = mDbHelper.getWritableDatabase(); 147 String combinedClause = concatenateClauses(selection, uriData.getWhereClause()); 148 return createDatabaseModifier(db) 149 .update(uriData.getUri(), mTableName, values, combinedClause, selectionArgs); 150 } 151 } 152 153 @Override 154 public String getType(UriData uriData) { 155 if (uriData.hasId()) { 156 return Status.ITEM_TYPE; 157 } else { 158 return Status.DIR_TYPE; 159 } 160 } 161 162 @Override 163 public ParcelFileDescriptor openFile(UriData uriData, String mode) { 164 throw new UnsupportedOperationException("File operation is not supported for status table"); 165 } 166 167 private DatabaseModifier createDatabaseModifier(SQLiteDatabase db) { 168 return new DbModifierWithNotification(mTableName, db, mContext); 169 } 170 171 @Override 172 public ArraySet<String> getSourcePackages() { 173 return mDbHelper.selectDistinctColumn(mTableName, Status.SOURCE_PACKAGE); 174 } 175} 176