Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54543184 authored by Amit Mahajan's avatar Amit Mahajan
Browse files

Additional sms segment de-duping for multi-part SMS.

Adding de-duping logic for segments with same parameters that can
cause ambiguity when concatenating them to get the complete
message.

Bug: 28932719
Change-Id: I187942d8384dc18d4311265441d076f085651c67
parent ad6a054e
Loading
Loading
Loading
Loading
+65 −39
Original line number Diff line number Diff line
@@ -1128,40 +1128,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) {
@@ -1175,20 +1177,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());
+2 −1
Original line number Diff line number Diff line
@@ -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;
+0 −12
Original line number Diff line number Diff line
@@ -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 =
+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
+7 −4
Original line number Diff line number Diff line
@@ -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;

@@ -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 {

@@ -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();
@@ -142,6 +144,7 @@ public class CdmaInboundSmsHandlerTest extends TelephonyTest {
        }
        assertFalse(mCdmaInboundSmsHandler.getWakeLock().isHeld());
        mCdmaInboundSmsHandler = null;
        mContentProvider.shutdown();
        super.tearDown();
    }

Loading