remove the extra slash from insert uri
withAppendedPath API adds extra slash before appending,
there is no need to add it twice.
Bug: 64161300
Test: runtest --path
tests/src/com/android/providers/telephony/SmsProviderTest.java --test-method testInsertUri
Change-Id: Ie7e1e1532c1e666752a5513abd345fb3d5816145
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index c9051d6..095313f 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -39,6 +39,7 @@
import android.telephony.SubscriptionManager;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.google.android.mms.pdu.EncodedStringValue;
import com.google.android.mms.pdu.PduHeaders;
@@ -851,33 +852,44 @@
"END;");
}
+ @VisibleForTesting
+ public static String CREATE_SMS_TABLE_STRING =
+ "CREATE TABLE sms (" +
+ "_id INTEGER PRIMARY KEY," +
+ "thread_id INTEGER," +
+ "address TEXT," +
+ "person INTEGER," +
+ "date INTEGER," +
+ "date_sent INTEGER DEFAULT 0," +
+ "protocol INTEGER," +
+ "read INTEGER DEFAULT 0," +
+ "status INTEGER DEFAULT -1," + // a TP-Status value
+ // or -1 if it
+ // status hasn't
+ // been received
+ "type INTEGER," +
+ "reply_path_present INTEGER," +
+ "subject TEXT," +
+ "body TEXT," +
+ "service_center TEXT," +
+ "locked INTEGER DEFAULT 0," +
+ "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
+ "error_code INTEGER DEFAULT 0," +
+ "creator TEXT," +
+ "seen INTEGER DEFAULT 0" +
+ ");";
+
+ @VisibleForTesting
+ public static String CREATE_ATTACHMENTS_TABLE_STRING =
+ "CREATE TABLE attachments (" +
+ "sms_id INTEGER," +
+ "content_url TEXT," +
+ "offset INTEGER);";
+
private void createSmsTables(SQLiteDatabase db) {
// N.B.: Whenever the columns here are changed, the columns in
// {@ref MmsSmsProvider} must be changed to match.
- db.execSQL("CREATE TABLE sms (" +
- "_id INTEGER PRIMARY KEY," +
- "thread_id INTEGER," +
- "address TEXT," +
- "person INTEGER," +
- "date INTEGER," +
- "date_sent INTEGER DEFAULT 0," +
- "protocol INTEGER," +
- "read INTEGER DEFAULT 0," +
- "status INTEGER DEFAULT -1," + // a TP-Status value
- // or -1 if it
- // status hasn't
- // been received
- "type INTEGER," +
- "reply_path_present INTEGER," +
- "subject TEXT," +
- "body TEXT," +
- "service_center TEXT," +
- "locked INTEGER DEFAULT 0," +
- "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
- "error_code INTEGER DEFAULT 0," +
- "creator TEXT," +
- "seen INTEGER DEFAULT 0" +
- ");");
+ db.execSQL(CREATE_SMS_TABLE_STRING);
/**
* This table is used by the SMS dispatcher to hold
@@ -899,10 +911,7 @@
// email address if from an email gateway, otherwise same as address
);
- db.execSQL("CREATE TABLE attachments (" +
- "sms_id INTEGER," +
- "content_url TEXT," +
- "offset INTEGER);");
+ db.execSQL(CREATE_ATTACHMENTS_TABLE_STRING);
/**
* This table is used by the SMS dispatcher to hold pending
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index eca18a6..37a1044 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -120,7 +120,7 @@
// Generate the body of the query.
int match = sURLMatcher.match(url);
- SQLiteDatabase db = getDBOpenHelper(match).getReadableDatabase();
+ SQLiteDatabase db = getReadableDatabase(match);
switch (match) {
case SMS_ALL:
constructQueryForBox(qb, Sms.MESSAGE_TYPE_ALL, smsTable);
@@ -521,7 +521,7 @@
return null;
}
- SQLiteDatabase db = getDBOpenHelper(match).getWritableDatabase();
+ SQLiteDatabase db = getWritableDatabase(match);
if (table.equals(TABLE_SMS)) {
boolean addDate = false;
@@ -632,12 +632,7 @@
db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
}
if (rowID > 0) {
- Uri uri;
- if (table == TABLE_SMS) {
- uri = Uri.withAppendedPath(url, "/" + rowID);
- } else {
- uri = Uri.withAppendedPath(url, "/" + table + "/" + rowID );
- }
+ Uri uri = Uri.withAppendedPath(url, String.valueOf(rowID));
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "insert " + uri + " succeeded");
}
@@ -653,7 +648,7 @@
public int delete(Uri url, String where, String[] whereArgs) {
int count;
int match = sURLMatcher.match(url);
- SQLiteDatabase db = getDBOpenHelper(match).getWritableDatabase();
+ SQLiteDatabase db = getWritableDatabase(match);
boolean notifyIfNotDefault = true;
switch (match) {
case SMS_ALL:
@@ -760,7 +755,7 @@
String extraWhere = null;
boolean notifyIfNotDefault = true;
int match = sURLMatcher.match(url);
- SQLiteDatabase db = getDBOpenHelper(match).getWritableDatabase();
+ SQLiteDatabase db = getWritableDatabase(match);
switch (match) {
case SMS_RAW_MESSAGE:
@@ -922,4 +917,16 @@
sURLMatcher.addURI("sms", "sim", SMS_ALL_ICC);
sURLMatcher.addURI("sms", "sim/#", SMS_ICC);
}
+
+ /**
+ * These methods can be overridden in a subclass for testing SmsProvider using an
+ * in-memory database.
+ */
+ SQLiteDatabase getReadableDatabase(int match) {
+ return getDBOpenHelper(match).getReadableDatabase();
+ }
+
+ SQLiteDatabase getWritableDatabase(int match) {
+ return getDBOpenHelper(match).getWritableDatabase();
+ }
}
diff --git a/tests/src/com/android/providers/telephony/SmsProviderTest.java b/tests/src/com/android/providers/telephony/SmsProviderTest.java
new file mode 100644
index 0000000..8fba85b
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/SmsProviderTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 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.app.AppOpsManager;
+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.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Telephony;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+
+/**
+ * Tests for testing CRUD operations of SmsProvider.
+ * Uses a MockContentResolver to test insert
+ * Uses SmsProviderTestable 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/SmsProviderTest.java \
+ * --test-method testInsertUri
+ */
+public class SmsProviderTest extends TestCase {
+ private static final String TAG = "TelephonyProviderTest";
+
+ private MockContextWithProvider mContext;
+ private MockContentResolver mContentResolver;
+ private SmsProviderTestable mSmsProviderTestable;
+
+ private int notifyChangeCount;
+
+
+ /**
+ * This is used to give the SmsProviderTest a mocked context which takes a
+ * SmsProvider 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;
+
+ public MockContextWithProvider(SmsProvider smsProvider) {
+ mResolver = new MockContentResolver() {
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
+ int userHandle) {
+ notifyChangeCount++;
+ }
+ };
+
+ // Add authority="sms" to given smsProvider
+ ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = "sms";
+
+ // Add context to given smsProvider
+ smsProvider.attachInfoForTesting(this, providerInfo);
+ Log.d(TAG, "MockContextWithProvider: smsProvider.getContext(): "
+ + smsProvider.getContext());
+
+ // Add given SmsProvider to mResolver with authority="sms" so that
+ // mResolver can send queries to mSmsProvider
+ mResolver.addProvider("sms", smsProvider);
+ Log.d(TAG, "MockContextWithProvider: Add SmsProvider to mResolver");
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ Log.d(TAG, "getSystemService: returning null");
+ switch (name) {
+ case Context.APP_OPS_SERVICE:
+ return Mockito.mock(AppOpsManager.class);
+ case Context.TELEPHONY_SERVICE:
+ return Mockito.mock(TelephonyManager.class);
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public Resources getResources() {
+ Log.d(TAG, "getResources: returning null");
+ return null;
+ }
+
+ @Override
+ public int getUserId() {
+ return 0;
+ }
+
+ @Override
+ public MockContentResolver getContentResolver() {
+ return mResolver;
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mSmsProviderTestable = new SmsProviderTestable();
+ mContext = new MockContextWithProvider(mSmsProviderTestable);
+ mContentResolver = mContext.getContentResolver();
+ notifyChangeCount = 0;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mSmsProviderTestable.closeDatabase();
+ }
+
+ @Test
+ @SmallTest
+ public void testInsertUri() {
+ // insert test contentValues
+ final ContentValues values = new ContentValues();
+ values.put(Telephony.Sms.SUBSCRIPTION_ID, 1);
+ values.put(Telephony.Sms.ADDRESS, "12345");
+ values.put(Telephony.Sms.BODY, "test");
+ values.put(Telephony.Sms.DATE, System.currentTimeMillis()); // milliseconds
+ values.put(Telephony.Sms.SEEN, 1);
+ values.put(Telephony.Sms.READ, 1);
+ values.put(Telephony.Sms.THREAD_ID, 1);
+
+ // test for sms table
+ Log.d(TAG, "testInsertSmsTable Inserting contentValues: " + values);
+ assertEquals(Uri.parse("content://sms/1"),
+ mContentResolver.insert(Uri.parse("content://sms"), values));
+ assertEquals(Uri.parse("content://sms/2"),
+ mContentResolver.insert(Uri.parse("content://sms"), values));
+
+ // test for attachments table
+ values.clear();
+ values.put("sms_id", 1);
+ values.put("content_url", "test");
+ values.put("offset", 0);
+ Log.d(TAG, "testInsertAttachmentTable Inserting contentValues: " + values);
+ assertEquals(Uri.parse("content://sms/attachments/1"),
+ mContentResolver.insert(Uri.parse("content://sms/attachments"), values));
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/providers/telephony/SmsProviderTestable.java b/tests/src/com/android/providers/telephony/SmsProviderTestable.java
new file mode 100644
index 0000000..9c3372e
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/SmsProviderTestable.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 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.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * A subclass of SmsProvider used for testing on an in-memory database
+ */
+public class SmsProviderTestable extends SmsProvider {
+ private static final String TAG = "SmsProviderTestable";
+
+ private InMemorySmsProviderDbHelper mDbHelper;
+
+ @Override
+ public boolean onCreate() {
+ Log.d(TAG, "onCreate called: mDbHelper = new InMemorySmsProviderDbHelper()");
+ mDbHelper = new InMemorySmsProviderDbHelper();
+ return true;
+ }
+
+ @Override
+ SQLiteDatabase getReadableDatabase(int match) {
+ Log.d(TAG, "getReadableDatabase called");
+ return mDbHelper.getReadableDatabase();
+ }
+
+ @Override
+ SQLiteDatabase getWritableDatabase(int match) {
+ Log.d(TAG, "getWritableDatabase called");
+ return mDbHelper.getWritableDatabase();
+ }
+
+ // close mDbHelper database object
+ protected void closeDatabase() {
+ mDbHelper.close();
+ }
+
+ /**
+ * An in memory DB for SmsProviderTestable to use
+ */
+ public static class InMemorySmsProviderDbHelper extends SQLiteOpenHelper {
+
+
+ public InMemorySmsProviderDbHelper() {
+ super(null, // no context is needed for in-memory db
+ null, // db file name is null for in-memory db
+ null, // CursorFactory is null by default
+ 1); // db version is no-op for tests
+ Log.d(TAG, "InMemorySmsProviderDbHelper creating in-memory database");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ // Set up the sms tables
+ Log.d(TAG, "InMemorySmsProviderDbHelper onCreate creating the sms tables");
+ db.execSQL(MmsSmsDatabaseHelper.CREATE_SMS_TABLE_STRING);
+ db.execSQL(MmsSmsDatabaseHelper.CREATE_ATTACHMENTS_TABLE_STRING);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.d(TAG, "InMemorySmsProviderDbHelper onUpgrade doing nothing");
+ return;
+ }
+ }
+}
+