1e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov/* 2e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * Copyright (C) 2009 The Android Open Source Project 3e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * 4e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); 5e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * you may not use this file except in compliance with the License. 6e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * You may obtain a copy of the License at 7e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * 8e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 9e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * 10e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 11e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, 12e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * See the License for the specific language governing permissions and 14e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * limitations under the License 15e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov */ 16e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 17e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovpackage com.android.providers.contacts; 18e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 1900e7c94b70f4b477653534dbe559d1759d796157Debashish Chatterjeeimport static com.android.providers.contacts.util.DbQueryUtils.checkForSupportedColumns; 20aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjeeimport static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause; 21aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjeeimport static com.android.providers.contacts.util.DbQueryUtils.getInequalityClause; 2200e7c94b70f4b477653534dbe559d1759d796157Debashish Chatterjee 23e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.content.ContentProvider; 24e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.content.ContentUris; 25e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.content.ContentValues; 26e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.content.Context; 27e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.content.UriMatcher; 28e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.database.Cursor; 29e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.database.DatabaseUtils; 30e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.database.sqlite.SQLiteDatabase; 31e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.database.sqlite.SQLiteQueryBuilder; 32e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.net.Uri; 33e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.provider.CallLog; 34e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport android.provider.CallLog.Calls; 35663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onukiimport android.util.Log; 36e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 3738210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.Tables; 3838210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.providers.contacts.util.SelectionBuilder; 3938210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.google.common.annotations.VisibleForTesting; 4038210445730ee04c351c7cc1b3800cfe23e34325Makoto Onuki 41e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovimport java.util.HashMap; 42e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 43e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov/** 44e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov * Call log content provider. 45e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov */ 46e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikovpublic class CallLogProvider extends ContentProvider { 47aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee /** Selection clause to use to exclude voicemail records. */ 48aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee private static final String EXCLUDE_VOICEMAIL_SELECTION = getInequalityClause( 499978b26dd17bb2b20b91101f1e4682604336b5f6Flavio Lerda Calls.TYPE, Calls.VOICEMAIL_TYPE); 50aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee 51e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov private static final int CALLS = 1; 52e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 53e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov private static final int CALLS_ID = 2; 54e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 55e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov private static final int CALLS_FILTER = 3; 56e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 57e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); 58e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov static { 59e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sURIMatcher.addURI(CallLog.AUTHORITY, "calls", CALLS); 60e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sURIMatcher.addURI(CallLog.AUTHORITY, "calls/#", CALLS_ID); 61e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sURIMatcher.addURI(CallLog.AUTHORITY, "calls/filter/*", CALLS_FILTER); 62e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 63e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 64e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov private static final HashMap<String, String> sCallsProjectionMap; 65e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov static { 66e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 67e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov // Calls projection map 68e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap = new HashMap<String, String>(); 69e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap.put(Calls._ID, Calls._ID); 70e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap.put(Calls.NUMBER, Calls.NUMBER); 71e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap.put(Calls.DATE, Calls.DATE); 72e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap.put(Calls.DURATION, Calls.DURATION); 73e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap.put(Calls.TYPE, Calls.TYPE); 74e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap.put(Calls.NEW, Calls.NEW); 7559f6477e10203617f504857f7e9aee2fda393f4fDebashish Chatterjee sCallsProjectionMap.put(Calls.VOICEMAIL_URI, Calls.VOICEMAIL_URI); 76b2197b235e3d96e7f70c24d548b7dac52dab88d8Flavio Lerda sCallsProjectionMap.put(Calls.IS_READ, Calls.IS_READ); 77e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap.put(Calls.CACHED_NAME, Calls.CACHED_NAME); 78e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap.put(Calls.CACHED_NUMBER_TYPE, Calls.CACHED_NUMBER_TYPE); 79e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov sCallsProjectionMap.put(Calls.CACHED_NUMBER_LABEL, Calls.CACHED_NUMBER_LABEL); 802530512f639c4979fd7371c7dd25dd67e8118124Bai Tao sCallsProjectionMap.put(Calls.COUNTRY_ISO, Calls.COUNTRY_ISO); 81aeaba441ddb05dede8bd86291ca78f42d670d54cFlavio Lerda sCallsProjectionMap.put(Calls.GEOCODED_LOCATION, Calls.GEOCODED_LOCATION); 827a24e1c1476b57a6268de8d57e5ef4a2d5f46794Flavio Lerda sCallsProjectionMap.put(Calls.CACHED_LOOKUP_URI, Calls.CACHED_LOOKUP_URI); 837a24e1c1476b57a6268de8d57e5ef4a2d5f46794Flavio Lerda sCallsProjectionMap.put(Calls.CACHED_MATCHED_NUMBER, Calls.CACHED_MATCHED_NUMBER); 847a24e1c1476b57a6268de8d57e5ef4a2d5f46794Flavio Lerda sCallsProjectionMap.put(Calls.CACHED_NORMALIZED_NUMBER, Calls.CACHED_NORMALIZED_NUMBER); 857a24e1c1476b57a6268de8d57e5ef4a2d5f46794Flavio Lerda sCallsProjectionMap.put(Calls.CACHED_PHOTO_ID, Calls.CACHED_PHOTO_ID); 8613ed28505ed1af4f0b4a6297c4c6840d91f10c8cFlavio Lerda sCallsProjectionMap.put(Calls.CACHED_FORMATTED_NUMBER, Calls.CACHED_FORMATTED_NUMBER); 87e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 88e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 89b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov private ContactsDatabaseHelper mDbHelper; 90e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov private DatabaseUtils.InsertHelper mCallsInserter; 913a6a49cfb06272e3e25f3c390a9cf4002da6e34dDaisuke Miyakawa private boolean mUseStrictPhoneNumberComparation; 92aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee private VoicemailPermissions mVoicemailPermissions; 93aeaba441ddb05dede8bd86291ca78f42d670d54cFlavio Lerda private CallLogInsertionHelper mCallLogInsertionHelper; 94e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 95e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov @Override 96e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov public boolean onCreate() { 97663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) { 98663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki Log.d(Constants.PERFORMANCE_TAG, "CallLogProvider.onCreate start"); 99663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki } 100e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov final Context context = getContext(); 101b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov mDbHelper = getDatabaseHelper(context); 1023a6a49cfb06272e3e25f3c390a9cf4002da6e34dDaisuke Miyakawa mUseStrictPhoneNumberComparation = 1033a6a49cfb06272e3e25f3c390a9cf4002da6e34dDaisuke Miyakawa context.getResources().getBoolean( 1043a6a49cfb06272e3e25f3c390a9cf4002da6e34dDaisuke Miyakawa com.android.internal.R.bool.config_use_strict_phone_number_comparation); 105aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee mVoicemailPermissions = new VoicemailPermissions(context); 106aeaba441ddb05dede8bd86291ca78f42d670d54cFlavio Lerda mCallLogInsertionHelper = createCallLogInsertionHelper(context); 107663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) { 108663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki Log.d(Constants.PERFORMANCE_TAG, "CallLogProvider.onCreate finish"); 109663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki } 110e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov return true; 111e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 112e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 113aeaba441ddb05dede8bd86291ca78f42d670d54cFlavio Lerda @VisibleForTesting 114aeaba441ddb05dede8bd86291ca78f42d670d54cFlavio Lerda protected CallLogInsertionHelper createCallLogInsertionHelper(final Context context) { 115c4144727cd740079f47e74ae5078d1613874f72aMakoto Onuki return DefaultCallLogInsertionHelper.getInstance(context); 116aeaba441ddb05dede8bd86291ca78f42d670d54cFlavio Lerda } 117aeaba441ddb05dede8bd86291ca78f42d670d54cFlavio Lerda 118aeaba441ddb05dede8bd86291ca78f42d670d54cFlavio Lerda @VisibleForTesting 119b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov protected ContactsDatabaseHelper getDatabaseHelper(final Context context) { 120b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov return ContactsDatabaseHelper.getInstance(context); 121e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 122e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 123e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov @Override 124e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 125e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov String sortOrder) { 126143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 12759f6477e10203617f504857f7e9aee2fda393f4fDebashish Chatterjee qb.setTables(Tables.CALLS); 12859f6477e10203617f504857f7e9aee2fda393f4fDebashish Chatterjee qb.setProjectionMap(sCallsProjectionMap); 12959f6477e10203617f504857f7e9aee2fda393f4fDebashish Chatterjee qb.setStrict(true); 13059f6477e10203617f504857f7e9aee2fda393f4fDebashish Chatterjee 131143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng final SelectionBuilder selectionBuilder = new SelectionBuilder(selection); 132aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder); 133aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee 134143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng final int match = sURIMatcher.match(uri); 135e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov switch (match) { 13659f6477e10203617f504857f7e9aee2fda393f4fDebashish Chatterjee case CALLS: 137e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov break; 138e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 139e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov case CALLS_ID: { 140aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee selectionBuilder.addClause(getEqualityClause(Calls._ID, 141aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee parseCallIdFromUri(uri))); 142e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov break; 143e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 144e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 145e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov case CALLS_FILTER: { 146e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov String phoneNumber = uri.getPathSegments().get(2); 147e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov qb.appendWhere("PHONE_NUMBERS_EQUAL(number, "); 148e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov qb.appendWhereEscapeString(phoneNumber); 1493a6a49cfb06272e3e25f3c390a9cf4002da6e34dDaisuke Miyakawa qb.appendWhere(mUseStrictPhoneNumberComparation ? ", 1)" : ", 0)"); 150e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov break; 151e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 152e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 153e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov default: 154e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov throw new IllegalArgumentException("Unknown URL " + uri); 155e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 156e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 157143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng final int limit = getIntParam(uri, Calls.LIMIT_PARAM_KEY, 0); 158143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng final int offset = getIntParam(uri, Calls.OFFSET_PARAM_KEY, 0); 159143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng String limitClause = null; 160143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng if (limit > 0) { 161143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng limitClause = offset + "," + limit; 162143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng } 163143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng 164b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 165143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng final Cursor c = qb.query(db, projection, selectionBuilder.build(), selectionArgs, null, 166143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng null, sortOrder, limitClause); 167e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov if (c != null) { 168e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov c.setNotificationUri(getContext().getContentResolver(), CallLog.CONTENT_URI); 169e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 170e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov return c; 171e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 172e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 173143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng /** 174143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng * Gets an integer query parameter from a given uri. 175143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng * 176143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng * @param uri The uri to extract the query parameter from. 177143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng * @param key The query parameter key. 178143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng * @param defaultValue A default value to return if the query parameter does not exist. 179143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng * @return The value from the query parameter in the Uri. Or the default value if the parameter 180143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng * does not exist in the uri. 181143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng * @throws IllegalArgumentException when the value in the query parameter is not an integer. 182143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng */ 183143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng private int getIntParam(Uri uri, String key, int defaultValue) { 184143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng String valueString = uri.getQueryParameter(key); 185143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng if (valueString == null) { 186143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng return defaultValue; 187143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng } 188143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng 189143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng try { 190143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng return Integer.parseInt(valueString); 191143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng } catch (NumberFormatException e) { 192143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng String msg = "Integer required for " + key + " parameter but value '" + valueString + 193143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng "' was found instead."; 194143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng throw new IllegalArgumentException(msg, e); 195143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng } 196143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng } 197143bb522fcfb4a08c7f112876f3fac1cf47cf5aeChiao Cheng 198e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov @Override 199e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov public String getType(Uri uri) { 200e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov int match = sURIMatcher.match(uri); 201e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov switch (match) { 202e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov case CALLS: 203e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov return Calls.CONTENT_TYPE; 204e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov case CALLS_ID: 205e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov return Calls.CONTENT_ITEM_TYPE; 206e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov case CALLS_FILTER: 207e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov return Calls.CONTENT_TYPE; 208e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov default: 209e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov throw new IllegalArgumentException("Unknown URI: " + uri); 210e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 211e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 212e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 213e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov @Override 214e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov public Uri insert(Uri uri, ContentValues values) { 21500e7c94b70f4b477653534dbe559d1759d796157Debashish Chatterjee checkForSupportedColumns(sCallsProjectionMap, values); 216aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee // Inserting a voicemail record through call_log requires the voicemail 217aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee // permission and also requires the additional voicemail param set. 218aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee if (hasVoicemailValue(values)) { 219aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee checkIsAllowVoicemailRequest(uri); 220aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee mVoicemailPermissions.checkCallerHasFullAccess(); 221aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 22237c85b89cc989308749ea22b1ce29c59a47158acDmitri Plotnikov if (mCallsInserter == null) { 22337c85b89cc989308749ea22b1ce29c59a47158acDmitri Plotnikov SQLiteDatabase db = mDbHelper.getWritableDatabase(); 22437c85b89cc989308749ea22b1ce29c59a47158acDmitri Plotnikov mCallsInserter = new DatabaseUtils.InsertHelper(db, Tables.CALLS); 22537c85b89cc989308749ea22b1ce29c59a47158acDmitri Plotnikov } 226f402aaf776fee29d8044d97979b16695f24086ddFlavio Lerda 227f402aaf776fee29d8044d97979b16695f24086ddFlavio Lerda ContentValues copiedValues = new ContentValues(values); 228f402aaf776fee29d8044d97979b16695f24086ddFlavio Lerda 229f402aaf776fee29d8044d97979b16695f24086ddFlavio Lerda // Add the computed fields to the copied values. 230f402aaf776fee29d8044d97979b16695f24086ddFlavio Lerda mCallLogInsertionHelper.addComputedValues(copiedValues); 231f402aaf776fee29d8044d97979b16695f24086ddFlavio Lerda 232f402aaf776fee29d8044d97979b16695f24086ddFlavio Lerda long rowId = getDatabaseModifier(mCallsInserter).insert(copiedValues); 233e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov if (rowId > 0) { 234e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov return ContentUris.withAppendedId(uri, rowId); 235e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 236e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov return null; 237e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 238e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 239e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov @Override 240aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 24100e7c94b70f4b477653534dbe559d1759d796157Debashish Chatterjee checkForSupportedColumns(sCallsProjectionMap, values); 242aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee // Request that involves changing record type to voicemail requires the 243aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee // voicemail param set in the uri. 244aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee if (hasVoicemailValue(values)) { 245aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee checkIsAllowVoicemailRequest(uri); 246aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 247aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee 248aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee SelectionBuilder selectionBuilder = new SelectionBuilder(selection); 249aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder); 250aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee 251b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov final SQLiteDatabase db = mDbHelper.getWritableDatabase(); 252aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee final int matchedUriId = sURIMatcher.match(uri); 253e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov switch (matchedUriId) { 254e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov case CALLS: 255e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov break; 256e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 257e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov case CALLS_ID: 258aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee selectionBuilder.addClause(getEqualityClause(Calls._ID, parseCallIdFromUri(uri))); 259e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov break; 260e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 261e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov default: 262aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee throw new UnsupportedOperationException("Cannot update URL: " + uri); 263e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 264e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 2652e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee return getDatabaseModifier(db).update(Tables.CALLS, values, selectionBuilder.build(), 2662e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee selectionArgs); 267e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 268e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 269e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov @Override 270e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov public int delete(Uri uri, String selection, String[] selectionArgs) { 271aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee SelectionBuilder selectionBuilder = new SelectionBuilder(selection); 272aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder); 273e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov 274aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee final SQLiteDatabase db = mDbHelper.getWritableDatabase(); 275e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov final int matchedUriId = sURIMatcher.match(uri); 276e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov switch (matchedUriId) { 277e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov case CALLS: 2782e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee return getDatabaseModifier(db).delete(Tables.CALLS, 279929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee selectionBuilder.build(), selectionArgs); 280e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov default: 281e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov throw new UnsupportedOperationException("Cannot delete that URL: " + uri); 282e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 283e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov } 2846f157d1cfb219f80d375c1357bfbdc5bf599d91aDmitri Plotnikov 285929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee // Work around to let the test code override the context. getContext() is final so cannot be 286929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee // overridden. 287929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee protected Context context() { 288929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee return getContext(); 289929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee } 290929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee 2912e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee /** 2922e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee * Returns a {@link DatabaseModifier} that takes care of sending necessary notifications 2932e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee * after the operation is performed. 2942e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee */ 295929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee private DatabaseModifier getDatabaseModifier(SQLiteDatabase db) { 2962e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee return new DbModifierWithNotification(Tables.CALLS, db, context()); 297929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee } 298929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee 2992e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee /** 3002e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee * Same as {@link #getDatabaseModifier(SQLiteDatabase)} but used for insert helper operations 3012e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee * only. 3022e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee */ 303929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee private DatabaseModifier getDatabaseModifier(DatabaseUtils.InsertHelper insertHelper) { 3042e757d904e62dbf5bc0b028626fa9319ccc38c45Debashish Chatterjee return new DbModifierWithNotification(Tables.CALLS, insertHelper, context()); 305929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee } 306929a04e2830e30718930d96335dfb0a729b6ab91Debashish Chatterjee 307aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee private boolean hasVoicemailValue(ContentValues values) { 308aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee return values.containsKey(Calls.TYPE) && 309aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee values.getAsInteger(Calls.TYPE).equals(Calls.VOICEMAIL_TYPE); 310aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 311aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee 312aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee /** 313aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * Checks if the supplied uri requests to include voicemails and take appropriate 314aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * action. 315aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * <p> If voicemail is requested, then check for voicemail permissions. Otherwise 316aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * modify the selection to restrict to non-voicemail entries only. 317aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee */ 318aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee private void checkVoicemailPermissionAndAddRestriction(Uri uri, 319aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee SelectionBuilder selectionBuilder) { 320aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee if (isAllowVoicemailRequest(uri)) { 321aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee mVoicemailPermissions.checkCallerHasFullAccess(); 322aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } else { 323aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee selectionBuilder.addClause(EXCLUDE_VOICEMAIL_SELECTION); 324aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 325aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 326aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee 327aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee /** 328aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * Determines if the supplied uri has the request to allow voicemails to be 329aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * included. 330aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee */ 331aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee private boolean isAllowVoicemailRequest(Uri uri) { 33292d97071e56110e0df7f25e6ebc92ff4ebf74a88Flavio Lerda return uri.getBooleanQueryParameter(Calls.ALLOW_VOICEMAILS_PARAM_KEY, false); 333aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 334aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee 335aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee /** 336aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * Checks to ensure that the given uri has allow_voicemail set. Used by 337aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * insert and update operations to check that ContentValues with voicemail 338aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * call type must use the voicemail uri. 339aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * @throws IllegalArgumentException if allow_voicemail is not set. 340aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee */ 341aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee private void checkIsAllowVoicemailRequest(Uri uri) { 342aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee if (!isAllowVoicemailRequest(uri)) { 343aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee throw new IllegalArgumentException( 344aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee String.format("Uri %s cannot be used for voicemail record." + 34592d97071e56110e0df7f25e6ebc92ff4ebf74a88Flavio Lerda " Please set '%s=true' in the uri.", uri, 34692d97071e56110e0df7f25e6ebc92ff4ebf74a88Flavio Lerda Calls.ALLOW_VOICEMAILS_PARAM_KEY)); 347aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 348aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 349aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee 350aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee /** 351aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * Parses the call Id from the given uri, assuming that this is a uri that 352aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * matches CALLS_ID. For other uri types the behaviour is undefined. 353aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee * @throws IllegalArgumentException if the id included in the Uri is not a valid long value. 354aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee */ 3559978b26dd17bb2b20b91101f1e4682604336b5f6Flavio Lerda private long parseCallIdFromUri(Uri uri) { 356aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee try { 3579978b26dd17bb2b20b91101f1e4682604336b5f6Flavio Lerda return Long.parseLong(uri.getPathSegments().get(1)); 358aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } catch (NumberFormatException e) { 359aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee throw new IllegalArgumentException("Invalid call id in uri: " + uri, e); 360aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 361aafbe295d67686870c64c74a59e589d1dfb506faDebashish Chatterjee } 362e99988b266dd1263162583e81e2b408e7329b1c8Dmitri Plotnikov} 363