blob: c9d09635fb527918d115525109cf8e48ad91764c [file] [log] [blame]
/*
* Copyright (C) 2009 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 android.provider.CallLog.Calls.MISSED_REASON_NOT_MISSED;
import android.telecom.CallerInfo;
import com.android.providers.contacts.testutil.CommonDatabaseUtils;
import com.android.providers.contacts.util.ContactsPermissions;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.VoicemailContract.Voicemails;
import android.telecom.PhoneAccountHandle;
import android.test.suitebuilder.annotation.MediumTest;
import java.util.Arrays;
import java.util.List;
/**
* Unit tests for {@link CallLogProvider}.
*
* Run the test like this:
* <code>
* adb shell am instrument -e class com.android.providers.contacts.CallLogProviderTest -w \
* com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
* </code>
*/
@MediumTest
public class CallLogProviderTest extends BaseContactsProvider2Test {
/** Fields specific to voicemail provider that should not be exposed by call_log*/
private static final String[] VOICEMAIL_PROVIDER_SPECIFIC_COLUMNS = new String[] {
Voicemails._DATA,
Voicemails.HAS_CONTENT,
Voicemails.MIME_TYPE,
Voicemails.SOURCE_PACKAGE,
Voicemails.SOURCE_DATA,
Voicemails.STATE,
Voicemails.DIRTY,
Voicemails.DELETED};
/** Total number of columns exposed by call_log provider. */
private static final int NUM_CALLLOG_FIELDS = 36;
private static final int MIN_MATCH = 7;
private int mOldMinMatch;
private CallLogProviderTestable mCallLogProvider;
@Override
protected Class<? extends ContentProvider> getProviderClass() {
return SynchronousContactsProvider2.class;
}
@Override
protected String getAuthority() {
return ContactsContract.AUTHORITY;
}
@Override
protected void setUp() throws Exception {
super.setUp();
mCallLogProvider = addProvider(CallLogProviderTestable.class, CallLog.AUTHORITY);
mOldMinMatch = mCallLogProvider.getMinMatchForTest();
mCallLogProvider.setMinMatchForTest(MIN_MATCH);
}
@Override
protected void tearDown() throws Exception {
setUpWithVoicemailPermissions();
mResolver.delete(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null);
setTimeForTest(null);
mCallLogProvider.setMinMatchForTest(mOldMinMatch);
super.tearDown();
}
public void testInsert_RegularCallRecord() {
setTimeForTest(1000L);
ContentValues values = getDefaultCallValues();
Uri uri = mResolver.insert(Calls.CONTENT_URI, values);
values.put(Calls.COUNTRY_ISO, "us");
assertStoredValues(uri, values);
assertSelection(uri, values, Calls._ID, ContentUris.parseId(uri));
assertLastModified(uri, 1000);
}
private void setUpWithVoicemailPermissions() {
mActor.addPermissions(ADD_VOICEMAIL_PERMISSION);
mActor.addPermissions(READ_VOICEMAIL_PERMISSION);
mActor.addPermissions(WRITE_VOICEMAIL_PERMISSION);
}
public void testInsert_VoicemailCallRecord() {
setUpWithVoicemailPermissions();
setTimeForTest(1000L);
final ContentValues values = getDefaultCallValues();
values.put(Calls.TYPE, Calls.VOICEMAIL_TYPE);
values.put(Calls.VOICEMAIL_URI, "content://foo/voicemail/2");
// Should fail with the base content uri without the voicemail param.
EvenMoreAsserts.assertThrows(IllegalArgumentException.class, new Runnable() {
@Override
public void run() {
mResolver.insert(Calls.CONTENT_URI, values);
}
});
// Now grant voicemail permission - should succeed.
Uri uri = mResolver.insert(Calls.CONTENT_URI_WITH_VOICEMAIL, values);
assertStoredValues(uri, values);
assertSelection(uri, values, Calls._ID, ContentUris.parseId(uri));
assertLastModified(uri, 1000);
}
public void testUpdate() {
setTimeForTest(1000L);
Uri uri = insertCallRecord();
ContentValues values = new ContentValues();
values.put(Calls.TYPE, Calls.OUTGOING_TYPE);
values.put(Calls.NUMBER, "1-800-263-7643");
values.put(Calls.NUMBER_PRESENTATION, Calls.PRESENTATION_ALLOWED);
values.put(Calls.DATE, 2000);
values.put(Calls.DURATION, 40);
values.put(Calls.CACHED_NAME, "1-800-GOOG-411");
values.put(Calls.CACHED_NUMBER_TYPE, Phone.TYPE_CUSTOM);
values.put(Calls.CACHED_NUMBER_LABEL, "Directory");
int count = mResolver.update(uri, values, null, null);
assertEquals(1, count);
assertStoredValues(uri, values);
assertLastModified(uri, 1000);
}
public void testDelete() {
Uri uri = insertCallRecord();
try {
mResolver.delete(uri, null, null);
fail();
} catch (UnsupportedOperationException ex) {
// Expected
}
int count = mResolver.delete(Calls.CONTENT_URI, Calls._ID + "="
+ ContentUris.parseId(uri), null);
assertEquals(1, count);
assertEquals(0, getCount(uri, null, null));
}
public void testCallLogFilter() {
ContentValues values = getDefaultCallValues();
mResolver.insert(Calls.CONTENT_URI, values);
Uri filterUri = Uri.withAppendedPath(Calls.CONTENT_FILTER_URI, "1-800-4664-411");
Cursor c = mResolver.query(filterUri, null, null, null, null);
assertEquals(1, c.getCount());
c.moveToFirst();
assertCursorValues(c, values);
c.close();
filterUri = Uri.withAppendedPath(Calls.CONTENT_FILTER_URI, "1-888-4664-411");
c = mResolver.query(filterUri, null, null, null, null);
assertEquals(0, c.getCount());
c.close();
}
public void testAddCall() {
CallerInfo ci = new CallerInfo();
ci.setName("1-800-GOOG-411");
ci.numberType = Phone.TYPE_CUSTOM;
ci.numberLabel = "Directory";
final ComponentName sComponentName = new ComponentName(
"com.android.server.telecom",
"TelecomServiceImpl");
PhoneAccountHandle subscription = new PhoneAccountHandle(
sComponentName, "sub0");
// Allow self-calls in order to add the call
ContactsPermissions.ALLOW_SELF_CALL = true;
Uri uri = Calls.addCall(ci, getMockContext(), "1-800-263-7643",
Calls.PRESENTATION_ALLOWED, Calls.OUTGOING_TYPE, 0, subscription, 2000,
40, null, MISSED_REASON_NOT_MISSED);
ContactsPermissions.ALLOW_SELF_CALL = false;
assertNotNull(uri);
assertEquals("0@" + CallLog.AUTHORITY, uri.getAuthority());
ContentValues values = new ContentValues();
values.put(Calls.TYPE, Calls.OUTGOING_TYPE);
values.put(Calls.FEATURES, 0);
values.put(Calls.NUMBER, "1-800-263-7643");
values.put(Calls.NUMBER_PRESENTATION, Calls.PRESENTATION_ALLOWED);
values.put(Calls.DATE, 2000);
values.put(Calls.DURATION, 40);
values.put(Calls.CACHED_NAME, ci.getName());
values.put(Calls.CACHED_NUMBER_TYPE, (String) null);
values.put(Calls.CACHED_NUMBER_LABEL, (String) null);
values.put(Calls.COUNTRY_ISO, "us");
values.put(Calls.GEOCODED_LOCATION, "usa");
values.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME,
"com.android.server.telecom/TelecomServiceImpl");
values.put(Calls.PHONE_ACCOUNT_ID, "sub0");
// Casting null to Long as there are many forms of "put" which have nullable second
// parameters and the compiler needs a hint as to which form is correct.
values.put(Calls.DATA_USAGE, (Long) null);
values.put(Calls.MISSED_REASON, 0);
assertStoredValues(uri, values);
}
// Test to check that the calls and voicemail uris returns expected results.
public void testDifferentContentUris() {
setUpWithVoicemailPermissions();
// Insert one voicemaail and two regular call record.
insertVoicemailRecord();
insertCallRecord();
insertCallRecord();
// With the default uri, only 2 call entries should be returned.
// With the voicemail uri all 3 should be returned.
assertEquals(2, getCount(Calls.CONTENT_URI, null, null));
assertEquals(3, getCount(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null));
}
public void testLimitParamReturnsCorrectLimit() {
for (int i=0; i<10; i++) {
insertCallRecord();
}
Uri uri = Calls.CONTENT_URI.buildUpon()
.appendQueryParameter(Calls.LIMIT_PARAM_KEY, "4")
.build();
assertEquals(4, getCount(uri, null, null));
}
public void testLimitAndOffsetParamReturnsCorrectEntries() {
for (int i=0; i<10; i++) {
mResolver.insert(Calls.CONTENT_URI, getDefaultValues(Calls.INCOMING_TYPE));
}
for (int i=0; i<10; i++) {
mResolver.insert(Calls.CONTENT_URI, getDefaultValues(Calls.MISSED_TYPE));
}
// Limit 4 records. Discard first 8.
Uri uri = Calls.CONTENT_URI.buildUpon()
.appendQueryParameter(Calls.LIMIT_PARAM_KEY, "4")
.appendQueryParameter(Calls.OFFSET_PARAM_KEY, "8")
.build();
String[] projection = new String[] {Calls._ID, Calls.TYPE};
Cursor c = mResolver.query(uri, projection, null, null, null);
try {
// First two should be incoming, next two should be missed.
for (int i = 0; i < 2; i++) {
c.moveToNext();
assertEquals(Calls.INCOMING_TYPE, c.getInt(1));
}
for (int i = 0; i < 2; i++) {
c.moveToNext();
assertEquals(Calls.MISSED_TYPE, c.getInt(1));
}
} finally {
c.close();
}
}
public void testUriWithBadLimitParamThrowsException() {
assertParamThrowsIllegalArgumentException(Calls.LIMIT_PARAM_KEY, "notvalid");
}
public void testUriWithBadOffsetParamThrowsException() {
assertParamThrowsIllegalArgumentException(Calls.OFFSET_PARAM_KEY, "notvalid");
}
private void assertParamThrowsIllegalArgumentException(String key, String value) {
Uri uri = Calls.CONTENT_URI.buildUpon()
.appendQueryParameter(key, value)
.build();
try {
mResolver.query(uri, null, null, null, null);
fail();
} catch (IllegalArgumentException e) {
assertTrue("Error does not contain value in question.",
e.toString().contains(value));
}
}
// Test to check that none of the voicemail provider specific fields are
// insertable through call_log provider.
public void testCannotAccessVoicemailSpecificFields_Insert() {
for (String voicemailColumn : VOICEMAIL_PROVIDER_SPECIFIC_COLUMNS) {
final ContentValues values = getDefaultCallValues();
values.put(voicemailColumn, "foo");
EvenMoreAsserts.assertThrows("Column: " + voicemailColumn,
IllegalArgumentException.class, new Runnable() {
@Override
public void run() {
mResolver.insert(Calls.CONTENT_URI, values);
}
});
}
}
// Test to check that none of the voicemail provider specific fields are
// exposed through call_log provider query.
public void testCannotAccessVoicemailSpecificFields_Query() {
// Query.
Cursor cursor = mResolver.query(Calls.CONTENT_URI, null, null, null, null);
List<String> columnNames = Arrays.asList(cursor.getColumnNames());
assertEquals(NUM_CALLLOG_FIELDS, columnNames.size());
// None of the voicemail provider specific columns should be present.
for (String voicemailColumn : VOICEMAIL_PROVIDER_SPECIFIC_COLUMNS) {
assertFalse("Unexpected column: '" + voicemailColumn + "' returned.",
columnNames.contains(voicemailColumn));
}
}
// Test to check that none of the voicemail provider specific fields are
// updatable through call_log provider.
public void testCannotAccessVoicemailSpecificFields_Update() {
for (String voicemailColumn : VOICEMAIL_PROVIDER_SPECIFIC_COLUMNS) {
final Uri insertedUri = insertCallRecord();
final ContentValues values = new ContentValues();
values.put(voicemailColumn, "foo");
EvenMoreAsserts.assertThrows("Column: " + voicemailColumn,
IllegalArgumentException.class, new Runnable() {
@Override
public void run() {
mResolver.update(insertedUri, values, null, null);
}
});
}
}
public void testVoicemailPermissions_Insert() {
EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
@Override
public void run() {
mResolver.insert(Calls.CONTENT_URI_WITH_VOICEMAIL, getDefaultVoicemailValues());
}
});
// Should now succeed with permissions granted.
setUpWithVoicemailPermissions();
mResolver.insert(Calls.CONTENT_URI_WITH_VOICEMAIL, getDefaultVoicemailValues());
}
public void testVoicemailPermissions_Update() {
EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
@Override
public void run() {
mResolver.update(Calls.CONTENT_URI_WITH_VOICEMAIL, getDefaultVoicemailValues(),
null, null);
}
});
// Should succeed with manage permission granted
mActor.addPermissions(WRITE_VOICEMAIL_PERMISSION);
mResolver.update(Calls.CONTENT_URI_WITH_VOICEMAIL, getDefaultCallValues(), null, null);
mActor.removePermissions(WRITE_VOICEMAIL_PERMISSION);
// Should also succeed with full permissions granted.
setUpWithVoicemailPermissions();
mResolver.update(Calls.CONTENT_URI_WITH_VOICEMAIL, getDefaultCallValues(), null, null);
}
public void testVoicemailPermissions_Query() {
EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
@Override
public void run() {
mResolver.query(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null, null, null);
}
});
// Should succeed with read_all permission granted
mActor.addPermissions(READ_VOICEMAIL_PERMISSION);
mResolver.query(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null, null, null);
mActor.removePermissions(READ_VOICEMAIL_PERMISSION);
// Should also succeed with full permissions granted.
setUpWithVoicemailPermissions();
mResolver.query(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null, null, null);
}
public void testVoicemailPermissions_Delete() {
EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
@Override
public void run() {
mResolver.delete(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null);
}
});
// Should succeed with manage permission granted
mActor.addPermissions(WRITE_VOICEMAIL_PERMISSION);
mResolver.delete(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null);
mActor.removePermissions(WRITE_VOICEMAIL_PERMISSION);
// Should now succeed with permissions granted.
setUpWithVoicemailPermissions();
mResolver.delete(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null);
}
public void testCopyEntriesFromCursor_AllEntriesSyncedWithoutDuplicatesPresent() {
assertStoredValues(Calls.CONTENT_URI);
assertEquals(10, mCallLogProvider.copyEntriesFromCursor(
getTestCallLogCursor(), 5, /* forShadow =*/ true));
assertStoredValues(Calls.CONTENT_URI,
getTestCallLogValues(2),
getTestCallLogValues(1),
getTestCallLogValues(0));
assertEquals(10, mCallLogProvider.getLastSyncTime(/* forShadow =*/ true));
assertEquals(0, mCallLogProvider.getLastSyncTime(/* forShadow =*/ false));
}
public void testCopyEntriesFromCursor_DuplicatesIgnoredCorrectly() {
mResolver.insert(Calls.CONTENT_URI, getTestCallLogValues(1));
assertStoredValues(Calls.CONTENT_URI, getTestCallLogValues(1));
assertEquals(10, mCallLogProvider.copyEntriesFromCursor(
getTestCallLogCursor(), 5, /* forShadow =*/ false));
assertStoredValues(Calls.CONTENT_URI,
getTestCallLogValues(2),
getTestCallLogValues(1),
getTestCallLogValues(0));
assertEquals(0, mCallLogProvider.getLastSyncTime(/* forShadow =*/ true));
assertEquals(10, mCallLogProvider.getLastSyncTime(/* forShadow =*/ false));
}
private ContentValues getDefaultValues(int callType) {
ContentValues values = new ContentValues();
values.put(Calls.TYPE, callType);
values.put(Calls.NUMBER, "1-800-4664-411");
values.put(Calls.NUMBER_PRESENTATION, Calls.PRESENTATION_ALLOWED);
values.put(Calls.DATE, 1000);
values.put(Calls.DURATION, 30);
values.put(Calls.NEW, 1);
return values;
}
private ContentValues getDefaultCallValues() {
return getDefaultValues(Calls.INCOMING_TYPE);
}
private ContentValues getDefaultVoicemailValues() {
return getDefaultValues(Calls.VOICEMAIL_TYPE);
}
private Uri insertCallRecord() {
return mResolver.insert(Calls.CONTENT_URI, getDefaultCallValues());
}
private Uri insertVoicemailRecord() {
return mResolver.insert(Calls.CONTENT_URI_WITH_VOICEMAIL, getDefaultVoicemailValues());
}
private Cursor getTestCallLogCursor() {
final MatrixCursor cursor = new MatrixCursor(CallLogProvider.CALL_LOG_SYNC_PROJECTION);
for (int i = 2; i >= 0; i--) {
cursor.addRow(CommonDatabaseUtils.getArrayFromContentValues(getTestCallLogValues(i),
CallLogProvider.CALL_LOG_SYNC_PROJECTION));
}
return cursor;
}
/**
* Returns a predefined {@link ContentValues} object based on the provided index.
*/
private ContentValues getTestCallLogValues(int i) {
ContentValues values = new ContentValues();
values.put(Calls.ADD_FOR_ALL_USERS,1);
switch (i) {
case 0:
values.put(Calls.NUMBER, "123456");
values.put(Calls.NUMBER_PRESENTATION, Calls.PRESENTATION_ALLOWED);
values.put(Calls.TYPE, Calls.MISSED_TYPE);
values.put(Calls.FEATURES, 0);
values.put(Calls.DATE, 10);
values.put(Calls.DURATION, 100);
values.put(Calls.DATA_USAGE, 1000);
values.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, (String) null);
values.put(Calls.PHONE_ACCOUNT_ID, (Long) null);
break;
case 1:
values.put(Calls.NUMBER, "654321");
values.put(Calls.NUMBER_PRESENTATION, Calls.PRESENTATION_ALLOWED);
values.put(Calls.TYPE, Calls.INCOMING_TYPE);
values.put(Calls.FEATURES, 0);
values.put(Calls.DATE, 5);
values.put(Calls.DURATION, 200);
values.put(Calls.DATA_USAGE, 0);
values.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, (String) null);
values.put(Calls.PHONE_ACCOUNT_ID, (Long) null);
break;
case 2:
values.put(Calls.NUMBER, "123456");
values.put(Calls.NUMBER_PRESENTATION, Calls.PRESENTATION_ALLOWED);
values.put(Calls.TYPE, Calls.OUTGOING_TYPE);
values.put(Calls.FEATURES, Calls.FEATURES_VIDEO);
values.put(Calls.DATE, 1);
values.put(Calls.DURATION, 50);
values.put(Calls.DATA_USAGE, 2000);
values.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, (String) null);
values.put(Calls.PHONE_ACCOUNT_ID, (Long) null);
break;
}
return values;
}
}