summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers/contacts/VoicemailContentTable.java
diff options
context:
space:
mode:
authorDebashish Chatterjee <debashishc@google.com>2011-07-05 08:12:29 +0100
committerDebashish Chatterjee <debashishc@google.com>2011-07-06 14:48:18 +0100
commit4b571ba0de4fac4ff9d2a4277032b8c6548fdbfa (patch)
tree0b4d0d6470e0b8e584a41c6c9a0cdb02382af3c6 /src/com/android/providers/contacts/VoicemailContentTable.java
parent6fbb4b739c288395bd80343400d3455b0292a0fd (diff)
downloadpackages_providers_ContactsProvider-4b571ba0de4fac4ff9d2a4277032b8c6548fdbfa.zip
packages_providers_ContactsProvider-4b571ba0de4fac4ff9d2a4277032b8c6548fdbfa.tar.gz
packages_providers_ContactsProvider-4b571ba0de4fac4ff9d2a4277032b8c6548fdbfa.tar.bz2
Refactored VoicemailContentProvider to simplify using multiple tables.
This is needed to simplify the voicemail provider to be able to serve operations on the voicemail_status table as well. The idea is to retain all common functionality related to voicemail permission check etc in the main VoicemailContentProvider class. And delegate database level operations to another class that only deals with functionality speficic to the underlying table. In the favor of code reuse the interaction between voicemail_content_provider and voicemail_table_implementations is both ways. VoicemailTable interface defines the operations exposed by both sides. I have also added a couple of new test cases to cover getType() and source_package check in the uri. Old test cases continue to pass to prove that the refactoring did not break any functionality. Change-Id: I4cb031234c2f5746084c51557e2ba0edbaf3d6de
Diffstat (limited to 'src/com/android/providers/contacts/VoicemailContentTable.java')
-rw-r--r--src/com/android/providers/contacts/VoicemailContentTable.java289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/com/android/providers/contacts/VoicemailContentTable.java b/src/com/android/providers/contacts/VoicemailContentTable.java
new file mode 100644
index 0000000..8f6f3bf
--- /dev/null
+++ b/src/com/android/providers/contacts/VoicemailContentTable.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.providers.contacts;
+
+import static com.android.providers.contacts.util.DbQueryUtils.checkForSupportedColumns;
+import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses;
+import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
+
+import com.android.providers.contacts.VoicemailContentProvider.UriData;
+import com.android.providers.contacts.util.CloseUtils;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.CallLog.Calls;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Voicemails;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Implementation of {@link VoicemailTable.Delegate} for the voicemail content table.
+ */
+public class VoicemailContentTable implements VoicemailTable.Delegate {
+ private static final String TAG = "VoicemailContentProvider";
+ // Voicemail projection map
+ private static final ProjectionMap sVoicemailProjectionMap = new ProjectionMap.Builder()
+ .add(Voicemails._ID)
+ .add(Voicemails.NUMBER)
+ .add(Voicemails.DATE)
+ .add(Voicemails.DURATION)
+ .add(Voicemails.NEW)
+ .add(Voicemails.STATE)
+ .add(Voicemails.SOURCE_DATA)
+ .add(Voicemails.SOURCE_PACKAGE)
+ .add(Voicemails.HAS_CONTENT)
+ .add(Voicemails.MIME_TYPE)
+ .add(Voicemails._DATA)
+ .build();
+
+ /** The private directory in which to store the data associated with the voicemail. */
+ private static final String DATA_DIRECTORY = "voicemail-data";
+
+ private static final String[] MIME_TYPE_ONLY_PROJECTION = new String[] { Voicemails.MIME_TYPE };
+ private static final String[] FILENAME_ONLY_PROJECTION = new String[] { Voicemails._DATA };
+
+ private final String mTableName;
+ private final SQLiteOpenHelper mDbHelper;
+ private final Context mContext;
+ private final VoicemailTable.DelegateHelper mDelegateHelper;
+
+ public VoicemailContentTable(String tableName, Context context, SQLiteOpenHelper dbHelper,
+ VoicemailTable.DelegateHelper contentProviderHelper) {
+ mTableName = tableName;
+ mContext = context;
+ mDbHelper = dbHelper;
+ mDelegateHelper = contentProviderHelper;
+ }
+
+ @Override
+ public int bulkInsert(UriData uriData, ContentValues[] valuesArray) {
+ int numInserted = 0;
+ for (ContentValues values : valuesArray) {
+ if (insertInternal(uriData, values, false) != null) {
+ numInserted++;
+ }
+ }
+ if (numInserted > 0) {
+ mDelegateHelper.notifyChange(uriData.getUri(), Intent.ACTION_PROVIDER_CHANGED);
+ }
+ return numInserted;
+ }
+
+ @Override
+ public Uri insert(UriData uriData, ContentValues values) {
+ return insertInternal(uriData, values, true);
+ }
+
+ private Uri insertInternal(UriData uriData, ContentValues values,
+ boolean sendProviderChangedNotification) {
+ checkForSupportedColumns(sVoicemailProjectionMap, values);
+ ContentValues copiedValues = new ContentValues(values);
+ checkInsertSupported(uriData);
+ mDelegateHelper.checkAndAddSourcePackageIntoValues(uriData, copiedValues);
+
+ // "_data" column is used by base ContentProvider's openFileHelper() to determine filename
+ // when Input/Output stream is requested to be opened.
+ copiedValues.put(Voicemails._DATA, generateDataFile());
+
+ // call type is always voicemail.
+ copiedValues.put(Calls.TYPE, Calls.VOICEMAIL_TYPE);
+
+ SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ long rowId = db.insert(mTableName, null, copiedValues);
+ if (rowId > 0) {
+ Uri newUri = ContentUris.withAppendedId(uriData.getUri(), rowId);
+ mDelegateHelper.notifyChange(newUri, VoicemailContract.ACTION_NEW_VOICEMAIL);
+ if (sendProviderChangedNotification) {
+ mDelegateHelper.notifyChange(newUri, Intent.ACTION_PROVIDER_CHANGED);
+ }
+ // Populate the 'voicemail_uri' field to be used by the call_log provider.
+ updateVoicemailUri(db, newUri);
+ return newUri;
+ }
+ return null;
+ }
+
+ private void checkInsertSupported(UriData uriData) {
+ if (uriData.hasId()) {
+ throw new UnsupportedOperationException(String.format(
+ "Cannot insert URI: %s. Inserted URIs should not contain an id.",
+ uriData.getUri()));
+ }
+ }
+
+ /** Generates a random file for storing audio data. */
+ private String generateDataFile() {
+ try {
+ File dataDirectory = mContext.getDir(DATA_DIRECTORY, Context.MODE_PRIVATE);
+ File voicemailFile = File.createTempFile("voicemail", "", dataDirectory);
+ return voicemailFile.getAbsolutePath();
+ } catch (IOException e) {
+ // If we are unable to create a temporary file, something went horribly wrong.
+ throw new RuntimeException("unable to create temp file", e);
+ }
+ }
+ private void updateVoicemailUri(SQLiteDatabase db, Uri newUri) {
+ ContentValues values = new ContentValues();
+ values.put(Calls.VOICEMAIL_URI, newUri.toString());
+ // Directly update the db because we cannot update voicemail_uri through external
+ // update() due to projectionMap check. This also avoids unnecessary permission
+ // checks that are already done as part of insert request.
+ db.update(mTableName, values, getWhereClause(
+ UriData.createUriData(newUri)), null);
+ }
+
+ @Override
+ public int delete(UriData uriData, String selection, String[] selectionArgs) {
+ final SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ String combinedClause = concatenateClauses(selection, getWhereClause(uriData),
+ getCallTypeClause());
+
+ // Delete all the files associated with this query. Once we've deleted the rows, there will
+ // be no way left to get hold of the files.
+ Cursor cursor = null;
+ try {
+ cursor = query(uriData, FILENAME_ONLY_PROJECTION, selection, selectionArgs, null);
+ while (cursor.moveToNext()) {
+ File file = new File(cursor.getString(0));
+ if (file.exists()) {
+ boolean success = file.delete();
+ if (!success) {
+ Log.e(TAG, "Failed to delete file: " + file.getAbsolutePath());
+ }
+ }
+ }
+ } finally {
+ CloseUtils.closeQuietly(cursor);
+ }
+
+ // Now delete the rows themselves.
+ int count = db.delete(mTableName, combinedClause, selectionArgs);
+ if (count > 0) {
+ mDelegateHelper.notifyChange(uriData.getUri(), Intent.ACTION_PROVIDER_CHANGED);
+ }
+ return count;
+ }
+
+ @Override
+ public Cursor query(UriData uriData, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(mTableName);
+ qb.setProjectionMap(sVoicemailProjectionMap);
+ qb.setStrict(true);
+
+ String combinedClause = concatenateClauses(selection, getWhereClause(uriData),
+ getCallTypeClause());
+ SQLiteDatabase db = mDbHelper.getReadableDatabase();
+ Cursor c = qb.query(db, projection, combinedClause, selectionArgs, null, null, sortOrder);
+ if (c != null) {
+ c.setNotificationUri(mContext.getContentResolver(), Voicemails.CONTENT_URI);
+ }
+ return c;
+ }
+
+ @Override
+ public int update(UriData uriData, ContentValues values, String selection,
+ String[] selectionArgs) {
+ checkForSupportedColumns(sVoicemailProjectionMap, values);
+ checkUpdateSupported(uriData);
+ final SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ // TODO: This implementation does not allow bulk update because it only accepts
+ // URI that include message Id. I think we do want to support bulk update.
+ String combinedClause = concatenateClauses(selection, getWhereClause(uriData),
+ getCallTypeClause());
+ int count = db.update(mTableName, values, combinedClause, selectionArgs);
+ if (count > 0) {
+ mDelegateHelper.notifyChange(uriData.getUri(), Intent.ACTION_PROVIDER_CHANGED);
+ }
+ return count;
+ }
+
+ private void checkUpdateSupported(UriData uriData) {
+ if (!uriData.hasId()) {
+ throw new UnsupportedOperationException(String.format(
+ "Cannot update URI: %s. Bulk update not supported", uriData.getUri()));
+ }
+ }
+
+ @Override
+ public String getType(UriData uriData) {
+ // TODO: DB lookup for the mime type may cause strict mode exception for the callers of
+ // getType(). See if this could be avoided.
+ if (uriData.hasId()) {
+ // An individual voicemail - so lookup the MIME type in the db.
+ return lookupMimeType(uriData);
+ }
+ // Not an individual voicemail - must be a directory listing type.
+ return Voicemails.DIR_TYPE;
+ }
+
+ /** Query the db for the MIME type of the given URI, called only from getType(). */
+ private String lookupMimeType(UriData uriData) {
+ Cursor cursor = null;
+ try {
+ // Use queryInternal, bypassing provider permission check. This is needed because
+ // getType() can be called from any application context (even without voicemail
+ // permissions) to know the MIME type of the URI. There is no security issue here as we
+ // do not expose any sensitive data through this interface.
+ cursor = query(uriData, MIME_TYPE_ONLY_PROJECTION, null, null, null);
+ if (cursor.moveToFirst()) {
+ return cursor.getString(cursor.getColumnIndex(Voicemails.MIME_TYPE));
+ }
+ } finally {
+ CloseUtils.closeQuietly(cursor);
+ }
+ return null;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(UriData uriData, String mode,
+ ParcelFileDescriptor openFileHelper) {
+ // If the open succeeded, then update the has_content bit in the table.
+ if (mode.contains("w")) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Voicemails.HAS_CONTENT, 1);
+ update(uriData, contentValues, null, null);
+ }
+ return openFileHelper;
+ }
+
+ private String getWhereClause(UriData uriData) {
+ return concatenateClauses(
+ (uriData.hasId() ?
+ getEqualityClause(Voicemails._ID, uriData.getId())
+ : null),
+ (uriData.hasSourcePackage() ?
+ getEqualityClause(Voicemails.SOURCE_PACKAGE, uriData.getSourcePackage())
+ : null));
+ }
+
+ /** Creates a clause to restrict the selection to only voicemail call type.*/
+ private String getCallTypeClause() {
+ return getEqualityClause(Calls.TYPE, String.valueOf(Calls.VOICEMAIL_TYPE));
+ }
+}