blob: 5e6cb3aa003af87d7cf440bf64f786739e13120f [file] [log] [blame]
/*
* Copyright (C) 2016 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.telephony;
import android.annotation.TargetApi;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.ContentObserver;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.net.Uri;
import android.os.Build;
import android.os.FileUtils;
import android.os.Process;
import android.provider.Telephony.Carriers;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
import android.util.Log;
import com.android.providers.telephony.TelephonyProvider;
import junit.framework.TestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Map;
import java.util.Set;
/**
* Tests for testing CRUD operations of TelephonyProvider.
* Uses a MockContentResolver to get permission WRITE_APN_SETTINGS in order to test insert/delete
* Uses TelephonyProviderTestable to set up in-memory database
*
* Build, install and run the tests by running the commands below:
* runtest --path <dir or file>
* runtest --path <dir or file> --test-method <testMethodName>
* e.g.)
* runtest --path tests/src/com/android/providers/telephony/TelephonyProviderTest.java \
* --test-method testInsertCarriers
*/
public class TelephonyProviderTest extends TestCase {
private static final String TAG = "TelephonyProviderTest";
private MockContextWithProvider mContext;
private MockContentResolver mContentResolver;
private TelephonyProviderTestable mTelephonyProviderTestable;
private int notifyChangeCount;
private int notifyChangeRestoreCount;
private static final String TEST_SUBID = "1";
private static final String TEST_OPERATOR = "123456";
// Used to test the path for URL_TELEPHONY_USING_SUBID with subid 1
private static final Uri CONTENT_URI_WITH_SUBID = Uri.parse(
"content://telephony/carriers/subId/" + TEST_SUBID);
// Used to test the "restore to default"
private static final Uri URL_RESTOREAPN_USING_SUBID = Uri.parse(
"content://telephony/carriers/restore/subId/" + TEST_SUBID);
// Constants for DPC related tests.
private static final Uri URI_DPC = Uri.parse("content://telephony/carriers/dpc");
private static final Uri URI_TELEPHONY = Carriers.CONTENT_URI;
private static final Uri URI_FILTERED = Uri.parse("content://telephony/carriers/filtered");
private static final Uri URI_ENFORCE_MANAGED= Uri.parse("content://telephony/carriers/enforce_managed");
private static final String ENFORCED_KEY = "enforced";
/**
* This is used to give the TelephonyProviderTest a mocked context which takes a
* TelephonyProvider and attaches it to the ContentResolver with telephony authority.
* The mocked context also gives WRITE_APN_SETTINGS permissions
*/
private class MockContextWithProvider extends MockContext {
private final MockContentResolver mResolver;
private final SharedPreferences mSharedPreferences = mock(SharedPreferences.class);
private final SharedPreferences.Editor mEditor = mock(SharedPreferences.Editor.class);
private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
public MockContextWithProvider(TelephonyProvider telephonyProvider) {
mResolver = new MockContentResolver() {
@Override
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
int userHandle) {
notifyChangeCount++;
if (URL_RESTOREAPN_USING_SUBID.equals(uri)) {
notifyChangeRestoreCount++;
}
}
};
// return test subId 0 for all operators
doReturn(TEST_OPERATOR).when(mTelephonyManager).getSimOperator(anyInt());
// Add authority="telephony" to given telephonyProvider
ProviderInfo providerInfo = new ProviderInfo();
providerInfo.authority = "telephony";
// Add context to given telephonyProvider
telephonyProvider.attachInfoForTesting(this, providerInfo);
Log.d(TAG, "MockContextWithProvider: telephonyProvider.getContext(): "
+ telephonyProvider.getContext());
// Add given telephonyProvider to mResolver with authority="telephony" so that
// mResolver can send queries to mTelephonyProvider
mResolver.addProvider("telephony", telephonyProvider);
Log.d(TAG, "MockContextWithProvider: Add telephonyProvider to mResolver");
when(mSharedPreferences.edit()).thenReturn(mEditor);
}
@Override
public Object getSystemService(String name) {
if (name.equals(Context.TELEPHONY_SERVICE)) {
Log.d(TAG, "getSystemService: returning mock TM");
return mTelephonyManager;
} else {
Log.d(TAG, "getSystemService: returning null");
return null;
}
}
@Override
public Resources getResources() {
Log.d(TAG, "getResources: returning null");
return null;
}
@Override
public MockContentResolver getContentResolver() {
return mResolver;
}
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return mSharedPreferences;
}
// Gives permission to write to the APN table within the MockContext
@Override
public int checkCallingOrSelfPermission(String permission) {
if (TextUtils.equals(permission, "android.permission.WRITE_APN_SETTINGS")) {
Log.d(TAG, "checkCallingOrSelfPermission: permission=" + permission
+ ", returning PackageManager.PERMISSION_GRANTED");
return PackageManager.PERMISSION_GRANTED;
} else {
Log.d(TAG, "checkCallingOrSelfPermission: permission=" + permission
+ ", returning PackageManager.PERMISSION_DENIED");
return PackageManager.PERMISSION_DENIED;
}
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
mTelephonyProviderTestable = new TelephonyProviderTestable();
mContext = new MockContextWithProvider(mTelephonyProviderTestable);
mContentResolver = (MockContentResolver) mContext.getContentResolver();
notifyChangeCount = 0;
notifyChangeRestoreCount = 0;
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
mTelephonyProviderTestable.closeDatabase();
}
/**
* Test bulk inserting, querying;
* Verify that the inserted values match the result of the query.
*/
@Test
@SmallTest
public void testBulkInsertCarriers() {
// insert 2 test contentValues
ContentValues contentValues = new ContentValues();
final String insertApn = "exampleApnName";
final String insertName = "exampleName";
final Integer insertCurrent = 1;
final String insertNumeric = TEST_OPERATOR;
contentValues.put(Carriers.APN, insertApn);
contentValues.put(Carriers.NAME, insertName);
contentValues.put(Carriers.CURRENT, insertCurrent);
contentValues.put(Carriers.NUMERIC, insertNumeric);
ContentValues contentValues2 = new ContentValues();
final String insertApn2 = "exampleApnName2";
final String insertName2 = "exampleName2";
final Integer insertCurrent2 = 1;
final String insertNumeric2 = "789123";
contentValues2.put(Carriers.APN, insertApn2);
contentValues2.put(Carriers.NAME, insertName2);
contentValues2.put(Carriers.CURRENT, insertCurrent2);
contentValues2.put(Carriers.NUMERIC, insertNumeric2);
Log.d(TAG, "testInsertCarriers: Bulk inserting contentValues=" + contentValues
+ ", " + contentValues2);
ContentValues[] values = new ContentValues[]{ contentValues, contentValues2 };
int rows = mContentResolver.bulkInsert(Carriers.CONTENT_URI, values);
assertEquals(2, rows);
assertEquals(1, notifyChangeCount);
// get values in table
final String[] testProjection =
{
Carriers.APN,
Carriers.NAME,
Carriers.CURRENT,
};
final String selection = Carriers.NUMERIC + "=?";
String[] selectionArgs = { insertNumeric };
Log.d(TAG, "testInsertCarriers query projection: " + testProjection
+ "\ntestInsertCarriers selection: " + selection
+ "\ntestInsertCarriers selectionArgs: " + selectionArgs);
Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
testProjection, selection, selectionArgs, null);
// verify that inserted values match results of query
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
final String resultApn = cursor.getString(0);
final String resultName = cursor.getString(1);
final Integer resultCurrent = cursor.getInt(2);
assertEquals(insertApn, resultApn);
assertEquals(insertName, resultName);
assertEquals(insertCurrent, resultCurrent);
}
/**
* Test inserting, querying, and deleting values in carriers table.
* Verify that the inserted values match the result of the query and are deleted.
*/
@Test
@SmallTest
public void testInsertCarriers() {
doSimpleTestForUri(Carriers.CONTENT_URI);
}
/**
* Test inserting, querying, and deleting values in carriers table.
* Verify that the inserted values match the result of the query and are deleted.
*/
@Test
@SmallTest
public void testInsertCarriersWithSubId() {
doSimpleTestForUri(CONTENT_URI_WITH_SUBID);
}
private void doSimpleTestForUri(Uri uri) {
// insert test contentValues
ContentValues contentValues = new ContentValues();
final String insertApn = "exampleApnName";
final String insertName = "exampleName";
final String insertNumeric = TEST_OPERATOR;
contentValues.put(Carriers.APN, insertApn);
contentValues.put(Carriers.NAME, insertName);
contentValues.put(Carriers.NUMERIC, insertNumeric);
Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues);
mContentResolver.insert(uri, contentValues);
// get values in table
final String[] testProjection =
{
Carriers.APN,
Carriers.NAME,
};
final String selection = Carriers.NUMERIC + "=?";
String[] selectionArgs = { insertNumeric };
Log.d(TAG, "testInsertCarriers query projection: " + testProjection
+ "\ntestInsertCarriers selection: " + selection
+ "\ntestInsertCarriers selectionArgs: " + selectionArgs);
Cursor cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
// verify that inserted values match results of query
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
final String resultApn = cursor.getString(0);
final String resultName = cursor.getString(1);
assertEquals(insertApn, resultApn);
assertEquals(insertName, resultName);
// delete test content
final String selectionToDelete = Carriers.NUMERIC + "=?";
String[] selectionArgsToDelete = { insertNumeric };
Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+ "testInsertCarriers selectionArgs: " + selectionArgs);
int numRowsDeleted = mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
assertEquals(1, numRowsDeleted);
// verify that deleted values are gone
cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
assertEquals(0, cursor.getCount());
}
@Test
@SmallTest
public void testOwnedBy() {
// insert test contentValues
ContentValues contentValues = new ContentValues();
final String insertApn = "exampleApnName";
final String insertName = "exampleName";
final String insertNumeric = TEST_OPERATOR;
final Integer insertOwnedBy = Carriers.OWNED_BY_OTHERS;
contentValues.put(Carriers.APN, insertApn);
contentValues.put(Carriers.NAME, insertName);
contentValues.put(Carriers.NUMERIC, insertNumeric);
contentValues.put(Carriers.OWNED_BY, insertOwnedBy);
Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues);
mContentResolver.insert(Carriers.CONTENT_URI, contentValues);
// get values in table
final String[] testProjection =
{
Carriers.APN,
Carriers.NAME,
Carriers.OWNED_BY,
};
final String selection = Carriers.NUMERIC + "=?";
String[] selectionArgs = { insertNumeric };
Log.d(TAG, "testInsertCarriers query projection: " + testProjection
+ "\ntestInsertCarriers selection: " + selection
+ "\ntestInsertCarriers selectionArgs: " + selectionArgs);
Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
testProjection, selection, selectionArgs, null);
// verify that inserted values match results of query
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
final String resultApn = cursor.getString(0);
final String resultName = cursor.getString(1);
final Integer resultOwnedBy = cursor.getInt(2);
assertEquals(insertApn, resultApn);
assertEquals(insertName, resultName);
// Verify that OWNED_BY is force set to OWNED_BY_OTHERS when inserted with general uri
assertEquals(insertOwnedBy, resultOwnedBy);
// delete test content
final String selectionToDelete = Carriers.NUMERIC + "=?";
String[] selectionArgsToDelete = { insertNumeric };
Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+ "testInsertCarriers selectionArgs: " + selectionArgs);
int numRowsDeleted = mContentResolver.delete(Carriers.CONTENT_URI,
selectionToDelete, selectionArgsToDelete);
assertEquals(1, numRowsDeleted);
// verify that deleted values are gone
cursor = mContentResolver.query(Carriers.CONTENT_URI,
testProjection, selection, selectionArgs, null);
assertEquals(0, cursor.getCount());
}
/**
* Test inserting, querying, and deleting values in carriers table.
* Verify that the inserted values match the result of the query and are deleted.
*/
@Test
@SmallTest
public void testSimTable() {
// insert test contentValues
ContentValues contentValues = new ContentValues();
final int insertSubId = 11;
final String insertDisplayName = "exampleDisplayName";
final String insertCarrierName = "exampleCarrierName";
final String insertIccId = "exampleIccId";
final String insertCardId = "exampleCardId";
contentValues.put(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, insertSubId);
contentValues.put(SubscriptionManager.DISPLAY_NAME, insertDisplayName);
contentValues.put(SubscriptionManager.CARRIER_NAME, insertCarrierName);
contentValues.put(SubscriptionManager.ICC_ID, insertIccId);
contentValues.put(SubscriptionManager.CARD_ID, insertCardId);
Log.d(TAG, "testSimTable Inserting contentValues: " + contentValues);
mContentResolver.insert(SubscriptionManager.CONTENT_URI, contentValues);
// get values in table
final String[] testProjection =
{
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
SubscriptionManager.CARRIER_NAME,
SubscriptionManager.CARD_ID,
};
final String selection = SubscriptionManager.DISPLAY_NAME + "=?";
String[] selectionArgs = { insertDisplayName };
Log.d(TAG,"\ntestSimTable selection: " + selection
+ "\ntestSimTable selectionArgs: " + selectionArgs.toString());
Cursor cursor = mContentResolver.query(SubscriptionManager.CONTENT_URI,
testProjection, selection, selectionArgs, null);
// verify that inserted values match results of query
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
final int resultSubId = cursor.getInt(0);
final String resultCarrierName = cursor.getString(1);
final String resultCardId = cursor.getString(2);
assertEquals(insertSubId, resultSubId);
assertEquals(insertCarrierName, resultCarrierName);
assertEquals(insertCardId, resultCardId);
// delete test content
final String selectionToDelete = SubscriptionManager.DISPLAY_NAME + "=?";
String[] selectionArgsToDelete = { insertDisplayName };
Log.d(TAG, "testSimTable deleting selection: " + selectionToDelete
+ "testSimTable selectionArgs: " + selectionArgs);
int numRowsDeleted = mContentResolver.delete(SubscriptionManager.CONTENT_URI,
selectionToDelete, selectionArgsToDelete);
assertEquals(1, numRowsDeleted);
// verify that deleted values are gone
cursor = mContentResolver.query(SubscriptionManager.CONTENT_URI,
testProjection, selection, selectionArgs, null);
assertEquals(0, cursor.getCount());
}
private int parseIdFromInsertedUri(Uri uri) throws NumberFormatException {
return (uri != null) ? Integer.parseInt(uri.getLastPathSegment()) : -1;
}
private int insertApnRecord(Uri uri, String apn, String name, int current, String numeric) {
ContentValues contentValues = new ContentValues();
contentValues.put(Carriers.APN, apn);
contentValues.put(Carriers.NAME, name);
contentValues.put(Carriers.CURRENT, current);
contentValues.put(Carriers.NUMERIC, numeric);
Uri resultUri = mContentResolver.insert(uri, contentValues);
return parseIdFromInsertedUri(resultUri);
}
/**
* Test URL_ENFORCE_MANAGED and URL_FILTERED works correctly.
* Verify that when enforce is set true via URL_ENFORCE_MANAGED, only DPC records are returned
* for URL_FILTERED and URL_FILTERED_ID.
* Verify that when enforce is set false via URL_ENFORCE_MANAGED, only non-DPC records
* are returned for URL_FILTERED and URL_FILTERED_ID.
*/
@Test
@SmallTest
public void testEnforceManagedUri() {
mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
final int current = 1;
final String numeric = "123456789";
// Insert DPC record.
final String dpcRecordApn = "exampleApnNameDPC";
final String dpcRecordName = "exampleNameDPC";
final int dpcRecordId = insertApnRecord(URI_DPC, dpcRecordApn, dpcRecordName,
current, numeric);
// Insert non-DPC record.
final String othersRecordApn = "exampleApnNameOTHERS";
final String othersRecordName = "exampleNameDPOTHERS";
final int othersRecordId = insertApnRecord(URI_TELEPHONY, othersRecordApn, othersRecordName,
current, numeric);
// Set enforced = false.
ContentValues enforceManagedValue = new ContentValues();
enforceManagedValue.put(ENFORCED_KEY, false);
Log.d(TAG, "testEnforceManagedUri Updating enforced = false: "
+ enforceManagedValue);
mContentResolver.update(URI_ENFORCE_MANAGED, enforceManagedValue, "", new String[]{});
// Verify that enforced is set to false in TelephonyProvider.
Cursor enforceCursor = mContentResolver.query(URI_ENFORCE_MANAGED,
null, null, null, null);
assertNotNull(enforceCursor);
assertEquals(1, enforceCursor.getCount());
enforceCursor.moveToFirst();
assertEquals(0, enforceCursor.getInt(0));
// Verify URL_FILTERED query only returns non-DPC record.
final String[] testProjection =
{
Carriers._ID,
Carriers.OWNED_BY
};
final String selection = Carriers.NUMERIC + "=?";
final String[] selectionArgs = { numeric };
final Cursor cursorNotEnforced = mContentResolver.query(URI_FILTERED,
testProjection, selection, selectionArgs, null);
assertNotNull(cursorNotEnforced);
assertEquals(1, cursorNotEnforced.getCount());
cursorNotEnforced.moveToFirst();
assertEquals(othersRecordId, cursorNotEnforced.getInt(0));
assertEquals(Carriers.OWNED_BY_OTHERS, cursorNotEnforced.getInt(1));
// Verify that URL_FILTERED_ID cannot get DPC record.
Cursor cursorNotEnforcedDpc = mContentResolver.query(Uri.withAppendedPath(URI_FILTERED,
Integer.toString(dpcRecordId)), null, null, null, null);
assertNotNull(cursorNotEnforcedDpc);
assertTrue(cursorNotEnforcedDpc.getCount() == 0);
// Verify that URL_FILTERED_ID can get non-DPC record.
Cursor cursorNotEnforcedOthers = mContentResolver.query(Uri.withAppendedPath(URI_FILTERED,
Integer.toString(othersRecordId)), null, null, null, null);
assertNotNull(cursorNotEnforcedOthers);
assertTrue(cursorNotEnforcedOthers.getCount() == 1);
// Set enforced = true.
enforceManagedValue.put(ENFORCED_KEY, true);
Log.d(TAG, "testEnforceManagedUri Updating enforced = true: "
+ enforceManagedValue);
mContentResolver.update(URI_ENFORCE_MANAGED, enforceManagedValue, "", new String[]{});
// Verify that enforced is set to true in TelephonyProvider.
enforceCursor = mContentResolver.query(URI_ENFORCE_MANAGED,
null, null, null, null);
assertNotNull(enforceCursor);
assertEquals(1, enforceCursor.getCount());
enforceCursor.moveToFirst();
assertEquals(1, enforceCursor.getInt(0));
// Verify URL_FILTERED query only returns DPC record.
final Cursor cursorEnforced = mContentResolver.query(URI_FILTERED,
testProjection, selection, selectionArgs, null);
assertNotNull(cursorEnforced);
assertEquals(1, cursorEnforced.getCount());
cursorEnforced.moveToFirst();
assertEquals(dpcRecordId, cursorEnforced.getInt(0));
assertEquals(Carriers.OWNED_BY_DPC, cursorEnforced.getInt(1));
// Verify that URL_FILTERED_ID can get DPC record.
cursorNotEnforcedDpc = mContentResolver.query(Uri.withAppendedPath(URI_FILTERED,
Integer.toString(dpcRecordId)), null, null, null, null);
assertNotNull(cursorNotEnforcedDpc);
assertTrue(cursorNotEnforcedDpc.getCount() == 1);
// Verify that URL_FILTERED_ID cannot get non-DPC record.
cursorNotEnforcedOthers = mContentResolver.query(Uri.withAppendedPath(URI_FILTERED,
Integer.toString(othersRecordId)), null, null, null, null);
assertNotNull(cursorNotEnforcedOthers);
assertTrue(cursorNotEnforcedOthers.getCount() == 0);
// Delete testing records.
int numRowsDeleted = mContentResolver.delete(URI_TELEPHONY, selection, selectionArgs);
assertEquals(1, numRowsDeleted);
numRowsDeleted = mContentResolver.delete(
ContentUris.withAppendedId(URI_DPC, dpcRecordId), "", null);
assertEquals(1, numRowsDeleted);
}
private Cursor queryFullTestApnRecord(Uri uri, String numeric) {
final String selection = Carriers.NUMERIC + "=?";
String[] selectionArgs = { numeric };
final String[] testProjection =
{
Carriers._ID,
Carriers.APN,
Carriers.NAME,
Carriers.CURRENT,
Carriers.OWNED_BY,
};
return mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
}
@Test
@SmallTest
/**
* Test URL_TELEPHONY cannot insert, query, update or delete DPC records.
*/
public void testTelephonyUriDpcRecordAccessControl() {
mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
final int current = 1;
final String numeric = "123456789";
final String selection = Carriers.NUMERIC + "=?";
final String[] selectionArgs = { numeric };
// Insert DPC record.
final String dpcRecordApn = "exampleApnNameDPC";
final String dpcRecordName = "exampleNameDPC";
final int dpcRecordId = insertApnRecord(URI_DPC, dpcRecordApn, dpcRecordName,
current, numeric);
// Insert non-DPC record.
final String othersRecordApn = "exampleApnNameOTHERS";
final String othersRecordName = "exampleNameDPOTHERS";
final int othersRecordId = insertApnRecord(URI_TELEPHONY, othersRecordApn, othersRecordName,
current, numeric);
// Verify URL_TELEPHONY query only returns non-DPC record.
final Cursor cursorTelephony = queryFullTestApnRecord(URI_TELEPHONY, numeric);
assertNotNull(cursorTelephony);
assertEquals(1, cursorTelephony.getCount());
cursorTelephony.moveToFirst();
assertApnEquals(cursorTelephony, othersRecordId, othersRecordApn, othersRecordName,
current, Carriers.OWNED_BY_OTHERS);
// Verify URI_TELEPHONY updates only non-DPC records.
ContentValues contentValuesOthersUpdate = new ContentValues();
final String othersRecordUpdatedApn = "exampleApnNameOTHERSUpdated";
final String othersRecordUpdatedName = "exampleNameOTHERSpdated";
contentValuesOthersUpdate.put(Carriers.APN, othersRecordUpdatedApn);
contentValuesOthersUpdate.put(Carriers.NAME, othersRecordUpdatedName);
final int updateCount = mContentResolver.update(URI_TELEPHONY, contentValuesOthersUpdate,
selection, selectionArgs);
assertEquals(1, updateCount);
final Cursor cursorNonDPCUpdate = queryFullTestApnRecord(URI_TELEPHONY, numeric);
final Cursor cursorDPCUpdate = queryFullTestApnRecord(URI_DPC, numeric);
// Verify that non-DPC records are updated.
assertNotNull(cursorNonDPCUpdate);
assertEquals(1, cursorNonDPCUpdate.getCount());
cursorNonDPCUpdate.moveToFirst();
assertApnEquals(cursorNonDPCUpdate, othersRecordId, othersRecordUpdatedApn,
othersRecordUpdatedName);
// Verify that DPC records are not updated.
assertNotNull(cursorDPCUpdate);
assertEquals(1, cursorDPCUpdate.getCount());
cursorDPCUpdate.moveToFirst();
assertApnEquals(cursorDPCUpdate, dpcRecordId, dpcRecordApn, dpcRecordName);
// Verify URI_TELEPHONY deletes only non-DPC records.
int numRowsDeleted = mContentResolver.delete(URI_TELEPHONY, selection, selectionArgs);
assertEquals(1, numRowsDeleted);
final Cursor cursorTelephonyRemaining = queryFullTestApnRecord(URI_TELEPHONY, numeric);
assertNotNull(cursorTelephonyRemaining);
assertEquals(0, cursorTelephonyRemaining.getCount());
final Cursor cursorDPCDeleted = queryFullTestApnRecord(URI_DPC, numeric);
assertNotNull(cursorDPCDeleted);
assertEquals(1, cursorDPCDeleted.getCount());
// Delete remaining test records.
numRowsDeleted = mContentResolver.delete(
ContentUris.withAppendedId(URI_DPC, dpcRecordId), "", null);
assertEquals(1, numRowsDeleted);
}
/**
* Test URL_DPC cannot insert or query non-DPC records.
* Test URL_DPC_ID cannot update or delete non-DPC records.
*/
@Test
@SmallTest
public void testDpcUri() {
int dpcRecordId = 0, othersRecordId = 0;
try {
mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
final int current = 1;
final String numeric = "123456789";
// Insert DPC record.
final String dpcRecordApn = "exampleApnNameDPC";
final String dpcRecordName = "exampleNameDPC";
dpcRecordId = insertApnRecord(URI_DPC, dpcRecordApn, dpcRecordName,
current, numeric);
// Insert non-DPC record.
final String othersRecordApn = "exampleApnNameOTHERS";
final String othersRecordName = "exampleNameDPOTHERS";
othersRecordId = insertApnRecord(URI_TELEPHONY, othersRecordApn, othersRecordName,
current, numeric);
Log.d(TAG, "testDPCIdUri Id for inserted DPC record: " + dpcRecordId);
Log.d(TAG, "testDPCIdUri Id for inserted non-DPC record: " + othersRecordId);
// Verify that URI_DPC query only returns DPC records.
final Cursor cursorDPC = queryFullTestApnRecord(URI_DPC, numeric);
assertNotNull(cursorDPC);
assertEquals(1, cursorDPC.getCount());
cursorDPC.moveToFirst();
assertApnEquals(cursorDPC, dpcRecordId, dpcRecordApn, dpcRecordName, current,
Carriers.OWNED_BY_DPC);
// Verify that URI_DPC_ID updates only DPC records.
ContentValues contentValuesDpcUpdate = new ContentValues();
final String dpcRecordUpdatedApn = "exampleApnNameDPCUpdated";
final String dpcRecordUpdatedName = "exampleNameDPCUpdated";
contentValuesDpcUpdate.put(Carriers.APN, dpcRecordUpdatedApn);
contentValuesDpcUpdate.put(Carriers.NAME, dpcRecordUpdatedName);
final int updateCount = mContentResolver.update(
ContentUris.withAppendedId(URI_DPC, dpcRecordId),
contentValuesDpcUpdate, null, null);
assertEquals(1, updateCount);
final Cursor cursorNonDPCUpdate = queryFullTestApnRecord(URI_TELEPHONY, numeric);
final Cursor cursorDPCUpdate = queryFullTestApnRecord(URI_DPC, numeric);
// Verify that non-DPC records are not updated.
assertNotNull(cursorNonDPCUpdate);
assertEquals(1, cursorNonDPCUpdate.getCount());
cursorNonDPCUpdate.moveToFirst();
assertApnEquals(cursorNonDPCUpdate, othersRecordId, othersRecordApn, othersRecordName);
// Verify that DPC records are updated.
assertNotNull(cursorDPCUpdate);
assertEquals(1, cursorDPCUpdate.getCount());
cursorDPCUpdate.moveToFirst();
assertApnEquals(cursorDPCUpdate, dpcRecordId, dpcRecordUpdatedApn,
dpcRecordUpdatedName);
// Test URI_DPC_ID deletes only DPC records.
int numRowsDeleted = mContentResolver.delete(
ContentUris.withAppendedId(URI_DPC, dpcRecordId), null, null);
assertEquals(1, numRowsDeleted);
numRowsDeleted = mContentResolver.delete(
ContentUris.withAppendedId(URI_DPC, dpcRecordId), null, null);
assertEquals(0, numRowsDeleted);
} finally {
// Delete remaining test records.
int numRowsDeleted = mContentResolver.delete(
ContentUris.withAppendedId(URI_TELEPHONY, othersRecordId), null, null);
assertEquals(1, numRowsDeleted);
}
}
private void assertApnEquals(Cursor cursor, Object... values) {
assertTrue(values.length <= cursor.getColumnCount());
for (int i = 0; i < values.length; i ++) {
if (values[i] instanceof Integer) {
assertEquals(values[i], cursor.getInt(i));
} else if (values[i] instanceof String) {
assertEquals(values[i], cursor.getString(i));
} else {
fail("values input type not correct");
}
}
}
/**
* Test URL_DPC does not change database on conflict for insert and update.
*/
@Test
@SmallTest
public void testDpcUriOnConflict() {
int dpcRecordId1 = 0, dpcRecordId2 = 0;
try {
mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
final int current = 1;
final String numeric = "123456789";
// Insert DPC record 1.
final String dpcRecordApn1 = "exampleApnNameDPC";
final String dpcRecordName = "exampleNameDPC";
dpcRecordId1 = insertApnRecord(URI_DPC, dpcRecordApn1, dpcRecordName,
current, numeric);
Log.d(TAG, "testDpcUriOnConflict Id for DPC record 1: " + dpcRecordId1);
// Insert conflicting DPC record.
final String dpcRecordNameConflict = "exampleNameDPCConflict";
final int dpcRecordIdConflict = insertApnRecord(URI_DPC, dpcRecordApn1,
dpcRecordNameConflict, current, numeric);
// Verity that conflicting DPC record is not inserted.
assertEquals(-1, dpcRecordIdConflict);
// Verify that APN 1 is not replaced or updated.
Cursor cursorDPC1 = queryFullTestApnRecord(URI_DPC, numeric);
assertNotNull(cursorDPC1);
assertEquals(1, cursorDPC1.getCount());
cursorDPC1.moveToFirst();
assertApnEquals(cursorDPC1, dpcRecordId1, dpcRecordApn1, dpcRecordName, current,
Carriers.OWNED_BY_DPC);
// Insert DPC record 2.
final String dpcRecordApn2 = "exampleApnNameDPC2";
dpcRecordId2 = insertApnRecord(URI_DPC, dpcRecordApn2, dpcRecordName,
current, numeric);
Log.d(TAG, "testDpcUriOnConflict Id for DPC record 2: " + dpcRecordId2);
// Update DPC record 2 to the values of DPC record 1.
ContentValues contentValuesDpcUpdate = new ContentValues();
contentValuesDpcUpdate.put(Carriers.APN, dpcRecordApn1);
contentValuesDpcUpdate.put(Carriers.NAME, dpcRecordNameConflict);
final int updateCount = mContentResolver.update(
ContentUris.withAppendedId(URI_DPC, dpcRecordId2),
contentValuesDpcUpdate, null, null);
// Verify that database is not updated.
assertEquals(0, updateCount);
Cursor cursorDPC2 = queryFullTestApnRecord(URI_DPC, numeric);
assertNotNull(cursorDPC2);
assertEquals(2, cursorDPC2.getCount());
cursorDPC2.moveToFirst();
assertApnEquals(cursorDPC2, dpcRecordId1, dpcRecordApn1, dpcRecordName, current,
Carriers.OWNED_BY_DPC);
cursorDPC2.moveToNext();
assertApnEquals(cursorDPC2, dpcRecordId2, dpcRecordApn2, dpcRecordName, current,
Carriers.OWNED_BY_DPC);
} finally {
// Delete test records.
int numRowsDeleted = mContentResolver.delete(
ContentUris.withAppendedId(URI_DPC, dpcRecordId1), null, null);
assertEquals(1, numRowsDeleted);
numRowsDeleted = mContentResolver.delete(
ContentUris.withAppendedId(URI_DPC, dpcRecordId2), null, null);
assertEquals(1, numRowsDeleted);
}
}
/**
* Verify that SecurityException is thrown if URL_DPC, URL_FILTERED and
* URL_ENFORCE_MANAGED is accessed from neither SYSTEM_UID nor PHONE_UID.
*/
@Test
@SmallTest
public void testAccessUrlDpcThrowSecurityExceptionFromOtherUid() {
mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID + 123456);
// Test insert().
ContentValues contentValuesDPC = new ContentValues();
try {
mContentResolver.insert(URI_DPC, contentValuesDPC);
assertFalse("SecurityException should be thrown when URI_DPC is called from"
+ " neither SYSTEM_UID nor PHONE_UID", true);
} catch (SecurityException e) {
// Should catch SecurityException.
}
// Test query().
try {
mContentResolver.query(URI_DPC,
new String[]{}, "", new String[]{}, null);
assertFalse("SecurityException should be thrown when URI_DPC is called from"
+ " neither SYSTEM_UID nor PHONE_UID", true);
} catch (SecurityException e) {
// Should catch SecurityException.
}
try {
mContentResolver.query(URI_ENFORCE_MANAGED,
new String[]{}, "", new String[]{}, null);
assertFalse("SecurityException should be thrown when URI_ENFORCE_MANAGED is "
+ "called from neither SYSTEM_UID nor PHONE_UID", true);
} catch (SecurityException e) {
// Should catch SecurityException.
}
// Test update().
ContentValues contentValuesDPCUpdate = new ContentValues();
try {
mContentResolver.update(
Uri.parse(URI_DPC + "/1"),
contentValuesDPCUpdate, "", new String[]{});
assertFalse("SecurityException should be thrown when URI_DPC is called"
+ " from neither SYSTEM_UID nor PHONE_UID", true);
} catch (SecurityException e) {
// Should catch SecurityException.
}
try {
mContentResolver.update(URI_ENFORCE_MANAGED, contentValuesDPCUpdate,
"", new String[]{});
assertFalse("SecurityException should be thrown when URI_DPC is called"
+ " from neither SYSTEM_UID nor PHONE_UID", true);
} catch (SecurityException e) {
// Should catch SecurityException.
}
// Test delete().
try {
mContentResolver.delete(
Uri.parse(URI_DPC + "/0"), "", new String[]{});
assertFalse("SecurityException should be thrown when URI_DPC is called"
+ " from neither SYSTEM_UID nor PHONE_UID", true);
} catch (SecurityException e) {
// Should catch SecurityException.
}
}
/**
* Verify that user/carrier edited/deleted APNs have priority in the EDITED field over
* insertions which set EDITED=UNEDITED. In these cases instead of merging the APNs using the
* new APN's value we keep the old value.
*/
@Test
@SmallTest
public void testPreserveEdited() {
preserveEditedValueInMerge(Carriers.USER_EDITED);
}
@Test
@SmallTest
public void testPreserveUserDeleted() {
preserveDeletedValueInMerge(Carriers.USER_DELETED);
}
@Test
@SmallTest
public void testPreserveUserDeletedButPresentInXml() {
preserveDeletedValueInMerge(Carriers.USER_DELETED_BUT_PRESENT_IN_XML);
}
@Test
@SmallTest
public void testPreserveCarrierEdited() {
preserveEditedValueInMerge(Carriers.CARRIER_EDITED);
}
@Test
@SmallTest
public void testPreserveCarrierDeleted() {
preserveDeletedValueInMerge(Carriers.CARRIER_DELETED);
}
@Test
@SmallTest
public void testPreserveCarrierDeletedButPresentInXml() {
preserveDeletedValueInMerge(Carriers.CARRIER_DELETED_BUT_PRESENT_IN_XML);
}
private void preserveEditedValueInMerge(int value) {
// insert user deleted APN
String carrierName1 = "carrier1";
String numeric1 = "123234";
String mcc1 = "123";
String mnc1 = "234";
ContentValues editedValue = new ContentValues();
editedValue.put(Carriers.NAME, carrierName1);
editedValue.put(Carriers.NUMERIC, numeric1);
editedValue.put(Carriers.MCC, mcc1);
editedValue.put(Carriers.MNC, mnc1);
editedValue.put(Carriers.EDITED, value);
assertNotNull(mContentResolver.insert(URI_TELEPHONY, editedValue));
Cursor cur = mContentResolver.query(URI_TELEPHONY, null, null, null, null);
assertEquals(1, cur.getCount());
// insert APN that conflicts with edited APN
String carrierName2 = "carrier2";
ContentValues values = new ContentValues();
values.put(Carriers.NAME, carrierName2);
values.put(Carriers.NUMERIC, numeric1);
values.put(Carriers.MCC, mcc1);
values.put(Carriers.MNC, mnc1);
values.put(Carriers.EDITED, Carriers.UNEDITED);
mContentResolver.insert(URI_TELEPHONY, values);
String[] testProjection = {
Carriers.NAME,
Carriers.APN,
Carriers.EDITED,
Carriers.TYPE,
Carriers.PROTOCOL,
Carriers.BEARER_BITMASK,
};
final int indexOfName = 0;
final int indexOfEdited = 2;
// Assert that the conflicting APN is merged into the existing user-edited APN, so only 1
// APN exists in the db
cur = mContentResolver.query(URI_TELEPHONY, testProjection, null, null, null);
assertEquals(1, cur.getCount());
cur.moveToFirst();
assertEquals(carrierName2, cur.getString(indexOfName));
assertEquals(value, cur.getInt(indexOfEdited));
}
private void preserveDeletedValueInMerge(int value) {
// insert user deleted APN
String carrierName1 = "carrier1";
String numeric1 = "123234";
String mcc1 = "123";
String mnc1 = "234";
ContentValues editedValue = new ContentValues();
editedValue.put(Carriers.NAME, carrierName1);
editedValue.put(Carriers.NUMERIC, numeric1);
editedValue.put(Carriers.MCC, mcc1);
editedValue.put(Carriers.MNC, mnc1);
editedValue.put(Carriers.EDITED, value);
assertNotNull(mContentResolver.insert(URI_TELEPHONY, editedValue));
// insert APN that conflicts with edited APN
String carrierName2 = "carrier2";
ContentValues values = new ContentValues();
values.put(Carriers.NAME, carrierName2);
values.put(Carriers.NUMERIC, numeric1);
values.put(Carriers.MCC, mcc1);
values.put(Carriers.MNC, mnc1);
values.put(Carriers.EDITED, Carriers.UNEDITED);
mContentResolver.insert(URI_TELEPHONY, values);
String[] testProjection = {
Carriers.NAME,
Carriers.APN,
Carriers.EDITED,
Carriers.TYPE,
Carriers.PROTOCOL,
Carriers.BEARER_BITMASK,
};
final int indexOfEdited = 2;
// Assert that the conflicting APN is merged into the existing user-deleted APN.
// Entries marked deleted will not show up in queries so we verify that no APNs can
// be seen
Cursor cur = mContentResolver.query(URI_TELEPHONY, testProjection, null, null, null);
assertEquals(0, cur.getCount());
}
/**
* Test URL_RESTOREAPN_USING_SUBID works correctly.
*/
@Test
@SmallTest
public void testRestoreDefaultApn() {
// setup for multi-SIM
TelephonyManager telephonyManager =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
doReturn(2).when(telephonyManager).getPhoneCount();
// create APN to be deleted (including MVNO values)
ContentValues targetValues = new ContentValues();
targetValues.put(Carriers.APN, "apnName");
targetValues.put(Carriers.NAME, "name");
targetValues.put(Carriers.NUMERIC, TEST_OPERATOR);
targetValues.put(Carriers.MVNO_TYPE, "spn");
targetValues.put(Carriers.MVNO_MATCH_DATA, TelephonyProviderTestable.TEST_SPN);
// create other operator APN (sama MCCMNC)
ContentValues otherValues = new ContentValues();
final String otherApn = "otherApnName";
final String otherName = "otherName";
final String otherMvnoTyp = "spn";
final String otherMvnoMatchData = "testOtherOperator";
otherValues.put(Carriers.APN, otherApn);
otherValues.put(Carriers.NAME, otherName);
otherValues.put(Carriers.NUMERIC, TEST_OPERATOR);
otherValues.put(Carriers.MVNO_TYPE, otherMvnoTyp);
otherValues.put(Carriers.MVNO_MATCH_DATA, otherMvnoMatchData);
// insert APNs
Log.d(TAG, "testRestoreDefaultApn: Bulk inserting contentValues=" + targetValues + ", "
+ otherValues);
ContentValues[] values = new ContentValues[]{ targetValues, otherValues };
mContentResolver.bulkInsert(Carriers.CONTENT_URI, values);
// restore to default
mContentResolver.delete(URL_RESTOREAPN_USING_SUBID, null, null);
// get values in table
final String[] testProjection =
{
Carriers.APN,
Carriers.NAME,
Carriers.MVNO_TYPE,
Carriers.MVNO_MATCH_DATA,
};
// verify that deleted result match results of query
Cursor cursor = mContentResolver.query(
Carriers.CONTENT_URI, testProjection, null, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals(otherApn, cursor.getString(0));
assertEquals(otherName, cursor.getString(1));
assertEquals(otherMvnoTyp, cursor.getString(2));
assertEquals(otherMvnoMatchData, cursor.getString(3));
// create APN to be deleted (not include MVNO values)
ContentValues targetValues2 = new ContentValues();
targetValues2.put(Carriers.APN, "apnName");
targetValues2.put(Carriers.NAME, "name");
targetValues2.put(Carriers.NUMERIC, TEST_OPERATOR);
// insert APN
mContentResolver.insert(Carriers.CONTENT_URI, targetValues2);
// restore to default
mContentResolver.delete(URL_RESTOREAPN_USING_SUBID, null, null);
// verify that deleted result match results of query
cursor = mContentResolver.query(Carriers.CONTENT_URI, testProjection, null, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals(otherApn, cursor.getString(0));
assertEquals(otherName, cursor.getString(1));
assertEquals(otherMvnoTyp, cursor.getString(2));
assertEquals(otherMvnoMatchData, cursor.getString(3));
// setup for single-SIM
doReturn(1).when(telephonyManager).getPhoneCount();
// restore to default
mContentResolver.delete(URL_RESTOREAPN_USING_SUBID, null, null);
// verify that deleted values are gone
cursor = mContentResolver.query(
Carriers.CONTENT_URI, testProjection, null, null, null);
assertEquals(0, cursor.getCount());
assertEquals(3, notifyChangeRestoreCount);
}
}