Loading src/java/com/android/internal/telephony/InboundSmsHandler.java +65 −39 Original line number Diff line number Diff line Loading @@ -1127,40 +1127,42 @@ public abstract class InboundSmsHandler extends StateMachine { } /** * Insert a message PDU into the raw table so we can acknowledge it immediately. * If the device crashes before the broadcast to listeners completes, it will be delivered * from the raw table on the next device boot. For single-part messages, the deleteWhere * and deleteWhereArgs fields of the tracker will be set to delete the correct row after * the ordered broadcast completes. * * @param tracker the tracker to add to the raw table * @return true on success; false on failure to write to database * Function to check if message should be dropped because same message has already been * received. In certain cases it checks for similar messages instead of exact same (cases where * keeping both messages in db can cause ambiguity) * @return true if duplicate exists, false otherwise */ private int addTrackerToRawTable(InboundSmsTracker tracker, boolean deDup) { private boolean duplicateExists(InboundSmsTracker tracker) throws SQLException { String address = tracker.getAddress(); // convert to strings for query String refNumber = Integer.toString(tracker.getReferenceNumber()); String count = Integer.toString(tracker.getMessageCount()); if (deDup) { // check for duplicate message segments Cursor cursor = null; try { // sequence numbers are 1-based except for CDMA WAP, which is 0-based int sequence = tracker.getSequenceNumber(); // convert to strings for query String seqNumber = Integer.toString(sequence); String date = Long.toString(tracker.getTimestamp()); String messageBody = tracker.getMessageBody(); String where; if (tracker.getMessageCount() == 1) { where = "address=? AND reference_number=? AND count=? AND sequence=? AND " + "date=? AND message_body=?"; } else { // for multi-part messages, deduping should also be done against undeleted // segments that can cause ambiguity when contacenating the segments, that is, // segments with same address, reference_number, count and sequence where = "address=? AND reference_number=? AND count=? AND sequence=? AND " + "((date=? AND message_body=?) OR deleted=0)"; } Cursor cursor = null; try { // Check for duplicate message segments cursor = mResolver.query(sRawUri, PDU_PROJECTION, "address=? AND reference_number=? AND count=? AND sequence=? AND date=? " + "AND message_body=?", cursor = mResolver.query(sRawUri, PDU_PROJECTION, where, new String[]{address, refNumber, count, seqNumber, date, messageBody}, null); // moveToNext() returns false if no duplicates were found if (cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) { loge("Discarding duplicate message segment, refNumber=" + refNumber + " seqNumber=" + seqNumber + " count=" + count); if (VDBG) { Loading @@ -1174,20 +1176,44 @@ public abstract class InboundSmsHandler extends StateMachine { loge("Warning: dup message segment PDU of length " + pdu.length + " is different from existing PDU of length " + oldPdu.length); } return Intents.RESULT_SMS_DUPLICATED; // reject message return true; // reject message } } catch (SQLException e) { loge("Can't access SMS database", e); return Intents.RESULT_SMS_GENERIC_ERROR; // reject message } finally { if (cursor != null) { cursor.close(); } } return false; } /** * Insert a message PDU into the raw table so we can acknowledge it immediately. * If the device crashes before the broadcast to listeners completes, it will be delivered * from the raw table on the next device boot. For single-part messages, the deleteWhere * and deleteWhereArgs fields of the tracker will be set to delete the correct row after * the ordered broadcast completes. * * @param tracker the tracker to add to the raw table * @return true on success; false on failure to write to database */ private int addTrackerToRawTable(InboundSmsTracker tracker, boolean deDup) { if (deDup) { try { if (duplicateExists(tracker)) { return Intents.RESULT_SMS_DUPLICATED; // reject message } } catch (SQLException e) { loge("Can't access SMS database", e); return Intents.RESULT_SMS_GENERIC_ERROR; // reject message } } else { logd("Skipped message de-duping logic"); } String address = tracker.getAddress(); String refNumber = Integer.toString(tracker.getReferenceNumber()); String count = Integer.toString(tracker.getMessageCount()); ContentValues values = tracker.getContentValues(); if (VDBG) log("adding content values to raw table: " + values.toString()); Loading src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java +2 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,8 @@ public class VisualVoicemailSmsFilter { */ public static boolean filter(Context context, byte[][] pdus, String format, int destPort, int subId) { TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); // TODO: select client package. String vvmClientPackage = SYSTEM_VVM_CLIENT_PACKAGE; Loading tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java +0 −12 Original line number Diff line number Diff line Loading @@ -404,18 +404,6 @@ public class ContextFixture implements TestFixture<Context> { public String getPackageName() { return "com.android.internal.telephony"; } public boolean testMethod() { return true; } public boolean testMethod1() { return true; } public boolean testMethod2() { return true; } } private final Multimap<String, ComponentName> mComponentNamesByAction = Loading tests/telephonytests/src/com/android/internal/telephony/FakeSmsContentProvider.java 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * 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.internal.telephony; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.telephony.SubscriptionManager; import android.test.mock.MockContentProvider; public class FakeSmsContentProvider extends MockContentProvider { private static final String RAW_TABLE_NAME = "raw"; public SQLiteOpenHelper mDbHelper = new InMemorySmsDbHelper(); private static final UriMatcher sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH); private static final int SMS_RAW_MESSAGE = 1; private static final int SMS_RAW_MESSAGE_PERMANENT_DELETE = 2; static { sURLMatcher.addURI("sms", "raw", SMS_RAW_MESSAGE); sURLMatcher.addURI("sms", "raw/permanentDelete", SMS_RAW_MESSAGE_PERMANENT_DELETE); } private class InMemorySmsDbHelper extends SQLiteOpenHelper { public InMemorySmsDbHelper() { super(getContext(), null, //db file name - null for in-memory db null, //CursorFactory - null for default 1); //db version - no-op for tests } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE raw (" + "_id INTEGER PRIMARY KEY," + "date INTEGER," + "reference_number INTEGER," + // one per full message "count INTEGER," + // the number of parts "sequence INTEGER," + // the part number of this message "destination_port INTEGER," + "address TEXT," + "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " + "pdu TEXT," + // the raw PDU for this part "deleted INTEGER DEFAULT 0," + // bool to indicate if row is deleted "message_body TEXT);"); // message body } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); return db.query(RAW_TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder); } @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); long rowId = db.insert(RAW_TABLE_NAME, null, values); return Uri.parse("content://raw/" + rowId); } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); int match = sURLMatcher.match(uri); int count = 0; switch (match) { case SMS_RAW_MESSAGE: ContentValues cv = new ContentValues(); cv.put("deleted", 1); count = db.update(RAW_TABLE_NAME, cv, selection, selectionArgs); break; case SMS_RAW_MESSAGE_PERMANENT_DELETE: count = db.delete(RAW_TABLE_NAME, selection, selectionArgs); break; } return count; } @Override public void shutdown() { mDbHelper.close(); } public int getNumRows() { int numRows = 0; Cursor c = query(null, null, null, null, null); if (c != null) { numRows = c.getCount(); c.close(); } return numRows; } } No newline at end of file tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java +7 −4 Original line number Diff line number Diff line Loading @@ -28,11 +28,11 @@ import android.provider.Telephony; import android.test.mock.MockContentResolver; import android.test.suitebuilder.annotation.MediumTest; import com.android.internal.telephony.FakeSmsContentProvider; import com.android.internal.telephony.InboundSmsHandler; import com.android.internal.telephony.SmsStorageMonitor; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.cdma.sms.SmsEnvelope; import com.android.internal.telephony.gsm.GsmInboundSmsHandlerTest; import com.android.internal.util.IState; import com.android.internal.util.StateMachine; Loading Loading @@ -68,6 +68,7 @@ public class CdmaInboundSmsHandlerTest extends TelephonyTest { private CdmaInboundSmsHandler mCdmaInboundSmsHandler; private SmsEnvelope mSmsEnvelope = new SmsEnvelope(); private ContentValues mInboundSmsTrackerCV = new ContentValues(); private FakeSmsContentProvider mContentProvider; private class CdmaInboundSmsHandlerTestHandler extends HandlerThread { Loading Loading @@ -122,11 +123,12 @@ public class CdmaInboundSmsHandlerTest extends TelephonyTest { doReturn(-1).when(mInboundSmsTracker).getDestPort(); doReturn(mInboundSmsTrackerCV).when(mInboundSmsTracker).getContentValues(); doReturn(smsPdu).when(mInboundSmsTracker).getPdu(); doReturn("This is the message body").when(mInboundSmsTracker).getMessageBody(); doReturn("1234567890").when(mInboundSmsTracker).getAddress(); GsmInboundSmsHandlerTest.FakeSmsContentProvider contentProvider = new GsmInboundSmsHandlerTest.FakeSmsContentProvider(); mContentProvider = new FakeSmsContentProvider(); ((MockContentResolver)mContext.getContentResolver()).addProvider( Telephony.Sms.CONTENT_URI.getAuthority(), contentProvider); Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider); new CdmaInboundSmsHandlerTestHandler(TAG).start(); waitUntilReady(); Loading @@ -142,6 +144,7 @@ public class CdmaInboundSmsHandlerTest extends TelephonyTest { } assertFalse(mCdmaInboundSmsHandler.getWakeLock().isHeld()); mCdmaInboundSmsHandler = null; mContentProvider.shutdown(); super.tearDown(); } Loading Loading
src/java/com/android/internal/telephony/InboundSmsHandler.java +65 −39 Original line number Diff line number Diff line Loading @@ -1127,40 +1127,42 @@ public abstract class InboundSmsHandler extends StateMachine { } /** * Insert a message PDU into the raw table so we can acknowledge it immediately. * If the device crashes before the broadcast to listeners completes, it will be delivered * from the raw table on the next device boot. For single-part messages, the deleteWhere * and deleteWhereArgs fields of the tracker will be set to delete the correct row after * the ordered broadcast completes. * * @param tracker the tracker to add to the raw table * @return true on success; false on failure to write to database * Function to check if message should be dropped because same message has already been * received. In certain cases it checks for similar messages instead of exact same (cases where * keeping both messages in db can cause ambiguity) * @return true if duplicate exists, false otherwise */ private int addTrackerToRawTable(InboundSmsTracker tracker, boolean deDup) { private boolean duplicateExists(InboundSmsTracker tracker) throws SQLException { String address = tracker.getAddress(); // convert to strings for query String refNumber = Integer.toString(tracker.getReferenceNumber()); String count = Integer.toString(tracker.getMessageCount()); if (deDup) { // check for duplicate message segments Cursor cursor = null; try { // sequence numbers are 1-based except for CDMA WAP, which is 0-based int sequence = tracker.getSequenceNumber(); // convert to strings for query String seqNumber = Integer.toString(sequence); String date = Long.toString(tracker.getTimestamp()); String messageBody = tracker.getMessageBody(); String where; if (tracker.getMessageCount() == 1) { where = "address=? AND reference_number=? AND count=? AND sequence=? AND " + "date=? AND message_body=?"; } else { // for multi-part messages, deduping should also be done against undeleted // segments that can cause ambiguity when contacenating the segments, that is, // segments with same address, reference_number, count and sequence where = "address=? AND reference_number=? AND count=? AND sequence=? AND " + "((date=? AND message_body=?) OR deleted=0)"; } Cursor cursor = null; try { // Check for duplicate message segments cursor = mResolver.query(sRawUri, PDU_PROJECTION, "address=? AND reference_number=? AND count=? AND sequence=? AND date=? " + "AND message_body=?", cursor = mResolver.query(sRawUri, PDU_PROJECTION, where, new String[]{address, refNumber, count, seqNumber, date, messageBody}, null); // moveToNext() returns false if no duplicates were found if (cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) { loge("Discarding duplicate message segment, refNumber=" + refNumber + " seqNumber=" + seqNumber + " count=" + count); if (VDBG) { Loading @@ -1174,20 +1176,44 @@ public abstract class InboundSmsHandler extends StateMachine { loge("Warning: dup message segment PDU of length " + pdu.length + " is different from existing PDU of length " + oldPdu.length); } return Intents.RESULT_SMS_DUPLICATED; // reject message return true; // reject message } } catch (SQLException e) { loge("Can't access SMS database", e); return Intents.RESULT_SMS_GENERIC_ERROR; // reject message } finally { if (cursor != null) { cursor.close(); } } return false; } /** * Insert a message PDU into the raw table so we can acknowledge it immediately. * If the device crashes before the broadcast to listeners completes, it will be delivered * from the raw table on the next device boot. For single-part messages, the deleteWhere * and deleteWhereArgs fields of the tracker will be set to delete the correct row after * the ordered broadcast completes. * * @param tracker the tracker to add to the raw table * @return true on success; false on failure to write to database */ private int addTrackerToRawTable(InboundSmsTracker tracker, boolean deDup) { if (deDup) { try { if (duplicateExists(tracker)) { return Intents.RESULT_SMS_DUPLICATED; // reject message } } catch (SQLException e) { loge("Can't access SMS database", e); return Intents.RESULT_SMS_GENERIC_ERROR; // reject message } } else { logd("Skipped message de-duping logic"); } String address = tracker.getAddress(); String refNumber = Integer.toString(tracker.getReferenceNumber()); String count = Integer.toString(tracker.getMessageCount()); ContentValues values = tracker.getContentValues(); if (VDBG) log("adding content values to raw table: " + values.toString()); Loading
src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java +2 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,8 @@ public class VisualVoicemailSmsFilter { */ public static boolean filter(Context context, byte[][] pdus, String format, int destPort, int subId) { TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); // TODO: select client package. String vvmClientPackage = SYSTEM_VVM_CLIENT_PACKAGE; Loading
tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java +0 −12 Original line number Diff line number Diff line Loading @@ -404,18 +404,6 @@ public class ContextFixture implements TestFixture<Context> { public String getPackageName() { return "com.android.internal.telephony"; } public boolean testMethod() { return true; } public boolean testMethod1() { return true; } public boolean testMethod2() { return true; } } private final Multimap<String, ComponentName> mComponentNamesByAction = Loading
tests/telephonytests/src/com/android/internal/telephony/FakeSmsContentProvider.java 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * 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.internal.telephony; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.telephony.SubscriptionManager; import android.test.mock.MockContentProvider; public class FakeSmsContentProvider extends MockContentProvider { private static final String RAW_TABLE_NAME = "raw"; public SQLiteOpenHelper mDbHelper = new InMemorySmsDbHelper(); private static final UriMatcher sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH); private static final int SMS_RAW_MESSAGE = 1; private static final int SMS_RAW_MESSAGE_PERMANENT_DELETE = 2; static { sURLMatcher.addURI("sms", "raw", SMS_RAW_MESSAGE); sURLMatcher.addURI("sms", "raw/permanentDelete", SMS_RAW_MESSAGE_PERMANENT_DELETE); } private class InMemorySmsDbHelper extends SQLiteOpenHelper { public InMemorySmsDbHelper() { super(getContext(), null, //db file name - null for in-memory db null, //CursorFactory - null for default 1); //db version - no-op for tests } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE raw (" + "_id INTEGER PRIMARY KEY," + "date INTEGER," + "reference_number INTEGER," + // one per full message "count INTEGER," + // the number of parts "sequence INTEGER," + // the part number of this message "destination_port INTEGER," + "address TEXT," + "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " + "pdu TEXT," + // the raw PDU for this part "deleted INTEGER DEFAULT 0," + // bool to indicate if row is deleted "message_body TEXT);"); // message body } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); return db.query(RAW_TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder); } @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); long rowId = db.insert(RAW_TABLE_NAME, null, values); return Uri.parse("content://raw/" + rowId); } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { SQLiteDatabase db = mDbHelper.getWritableDatabase(); int match = sURLMatcher.match(uri); int count = 0; switch (match) { case SMS_RAW_MESSAGE: ContentValues cv = new ContentValues(); cv.put("deleted", 1); count = db.update(RAW_TABLE_NAME, cv, selection, selectionArgs); break; case SMS_RAW_MESSAGE_PERMANENT_DELETE: count = db.delete(RAW_TABLE_NAME, selection, selectionArgs); break; } return count; } @Override public void shutdown() { mDbHelper.close(); } public int getNumRows() { int numRows = 0; Cursor c = query(null, null, null, null, null); if (c != null) { numRows = c.getCount(); c.close(); } return numRows; } } No newline at end of file
tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java +7 −4 Original line number Diff line number Diff line Loading @@ -28,11 +28,11 @@ import android.provider.Telephony; import android.test.mock.MockContentResolver; import android.test.suitebuilder.annotation.MediumTest; import com.android.internal.telephony.FakeSmsContentProvider; import com.android.internal.telephony.InboundSmsHandler; import com.android.internal.telephony.SmsStorageMonitor; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.cdma.sms.SmsEnvelope; import com.android.internal.telephony.gsm.GsmInboundSmsHandlerTest; import com.android.internal.util.IState; import com.android.internal.util.StateMachine; Loading Loading @@ -68,6 +68,7 @@ public class CdmaInboundSmsHandlerTest extends TelephonyTest { private CdmaInboundSmsHandler mCdmaInboundSmsHandler; private SmsEnvelope mSmsEnvelope = new SmsEnvelope(); private ContentValues mInboundSmsTrackerCV = new ContentValues(); private FakeSmsContentProvider mContentProvider; private class CdmaInboundSmsHandlerTestHandler extends HandlerThread { Loading Loading @@ -122,11 +123,12 @@ public class CdmaInboundSmsHandlerTest extends TelephonyTest { doReturn(-1).when(mInboundSmsTracker).getDestPort(); doReturn(mInboundSmsTrackerCV).when(mInboundSmsTracker).getContentValues(); doReturn(smsPdu).when(mInboundSmsTracker).getPdu(); doReturn("This is the message body").when(mInboundSmsTracker).getMessageBody(); doReturn("1234567890").when(mInboundSmsTracker).getAddress(); GsmInboundSmsHandlerTest.FakeSmsContentProvider contentProvider = new GsmInboundSmsHandlerTest.FakeSmsContentProvider(); mContentProvider = new FakeSmsContentProvider(); ((MockContentResolver)mContext.getContentResolver()).addProvider( Telephony.Sms.CONTENT_URI.getAuthority(), contentProvider); Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider); new CdmaInboundSmsHandlerTestHandler(TAG).start(); waitUntilReady(); Loading @@ -142,6 +144,7 @@ public class CdmaInboundSmsHandlerTest extends TelephonyTest { } assertFalse(mCdmaInboundSmsHandler.getWakeLock().isHeld()); mCdmaInboundSmsHandler = null; mContentProvider.shutdown(); super.tearDown(); } Loading