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