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

Commit 93f0a375 authored by Calvin Pan's avatar Calvin Pan Committed by Automerger Merge Worker
Browse files

Merge changes from topic "non_rcs_capabilities_expiration" am: d9d88b54 am:...

Merge changes from topic "non_rcs_capabilities_expiration" am: d9d88b54 am: cca32e32 am: 09b18aa4

Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/net/ims/+/1566054

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ic45a73b99117c989fe017d55bbb86c9f51bba01b
parents 9a235082 09b18aa4
Loading
Loading
Loading
Loading
+122 −0
Original line number Diff line number Diff line
@@ -21,11 +21,15 @@ import static android.telephony.ims.RcsContactUceCapability.CAPABILITY_MECHANISM
import static android.telephony.ims.RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND;
import static android.telephony.ims.RcsContactUceCapability.SOURCE_TYPE_CACHED;

import static com.android.ims.rcs.uce.eab.EabProvider.EAB_OPTIONS_TABLE_NAME;
import static com.android.ims.rcs.uce.eab.EabProvider.EAB_PRESENCE_TUPLE_TABLE_NAME;

import android.annotation.NonNull;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
@@ -42,6 +46,7 @@ import android.util.TimeFormatException;

import com.android.ims.RcsFeatureManager;
import com.android.ims.rcs.uce.UceController.UceControllerCallback;
import com.android.internal.annotations.VisibleForTesting;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -62,17 +67,29 @@ public class EabControllerImpl implements EabController {
    private static final int DEFAULT_CAPABILITY_CACHE_EXPIRATION_SEC = 90 * 24 * 60 * 60;
    private static final int DEFAULT_AVAILABILITY_CACHE_EXPIRATION_SEC = 60;

    // 1 week
    private static final int CLEAN_UP_LEGACY_CAPABILITY_SEC = 7 * 24 * 60 * 60;
    private static final int CLEAN_UP_LEGACY_CAPABILITY_DELAY_MILLI_SEC = 30 * 1000;

    private final Context mContext;
    private final int mSubId;
    private final EabBulkCapabilityUpdater mEabBulkCapabilityUpdater;
    private final Handler mHandler;

    private UceControllerCallback mUceControllerCallback;
    private volatile boolean mIsSetDestroyedFlag = false;

    @VisibleForTesting
    public final Runnable mCapabilityCleanupRunnable = () -> {
        Log.d(TAG, "Cleanup Capabilities");
        cleanupExpiredCapabilities();
    };

    public EabControllerImpl(Context context, int subId, UceControllerCallback c, Looper looper) {
        mContext = context;
        mSubId = subId;
        mUceControllerCallback = c;
        mHandler = new Handler(looper);
        mEabBulkCapabilityUpdater = new EabBulkCapabilityUpdater(mContext, mSubId,
                this,
                new EabContactSyncController(),
@@ -194,6 +211,12 @@ public class EabControllerImpl implements EabController {
        }

        mEabBulkCapabilityUpdater.updateExpiredTimeAlert();

        if (mHandler.hasCallbacks(mCapabilityCleanupRunnable)) {
            mHandler.removeCallbacks(mCapabilityCleanupRunnable);
        }
        mHandler.postDelayed(mCapabilityCleanupRunnable,
                CLEAN_UP_LEGACY_CAPABILITY_DELAY_MILLI_SEC);
    }

    private List<EabCapabilityResult> generateDestroyedResult(List<Uri> contactUri) {
@@ -627,6 +650,105 @@ public class EabControllerImpl implements EabController {
        mContext.getContentResolver().bulkInsert(EabProvider.OPTIONS_URI, optionContent);
    }

    private void cleanupExpiredCapabilities() {
        // Cleanup the capabilities that expired more than 1 week
        long rcsCapabilitiesExpiredTime = Instant.now().getEpochSecond() -
                getCapabilityCacheExpiration(mSubId) -
                CLEAN_UP_LEGACY_CAPABILITY_SEC;

        // Cleanup the capabilities that expired more than 1 week
        long nonRcsCapabilitiesExpiredTime = Instant.now().getEpochSecond() -
                getNonRcsCapabilityCacheExpiration(mSubId) -
                CLEAN_UP_LEGACY_CAPABILITY_SEC;

        cleanupCapabilities(rcsCapabilitiesExpiredTime, getRcsCommonIdList());
        cleanupCapabilities(nonRcsCapabilitiesExpiredTime, getNonRcsCommonIdList());
        cleanupOrphanedRows();
    }

    private void cleanupCapabilities(long rcsCapabilitiesExpiredTime, List<Integer> commonIdList) {
        if (commonIdList.size() > 0) {
            String presenceClause =
                    EabProvider.PresenceTupleColumns.EAB_COMMON_ID +
                            " IN (" + TextUtils.join(",", commonIdList) + ") " + " AND " +
                            EabProvider.PresenceTupleColumns.REQUEST_TIMESTAMP + "<?";

            String optionClause =
                    EabProvider.PresenceTupleColumns.EAB_COMMON_ID +
                            " IN (" + TextUtils.join(",", commonIdList) + ") " + " AND " +
                            EabProvider.OptionsColumns.REQUEST_TIMESTAMP + "<?";

            int deletePresenceCount = mContext.getContentResolver().delete(
                    EabProvider.PRESENCE_URI,
                    presenceClause,
                    new String[]{String.valueOf(rcsCapabilitiesExpiredTime)});

            int deleteOptionsCount = mContext.getContentResolver().delete(
                    EabProvider.OPTIONS_URI,
                    optionClause,
                    new String[]{String.valueOf(rcsCapabilitiesExpiredTime)});

            Log.d(TAG, "Cleanup capabilities. deletePresenceCount: " + deletePresenceCount +
                ",deleteOptionsCount: " + deleteOptionsCount);
        }
    }

    private List<Integer> getRcsCommonIdList() {
        ArrayList<Integer> list = new ArrayList<>();
        Cursor cursor = mContext.getContentResolver().query(
                EabProvider.COMMON_URI,
                null,
                EabProvider.EabCommonColumns.REQUEST_RESULT + "<>?",
                new String[]{String.valueOf(REQUEST_RESULT_NOT_FOUND)},
                null);

        if (cursor == null) return list;

        while (cursor.moveToNext()) {
            list.add(cursor.getInt(cursor.getColumnIndex(EabProvider.EabCommonColumns._ID)));
        }
        cursor.close();

        return list;
    }

    private List<Integer> getNonRcsCommonIdList() {
        ArrayList<Integer> list = new ArrayList<>();
        Cursor cursor = mContext.getContentResolver().query(
                EabProvider.COMMON_URI,
                null,
                EabProvider.EabCommonColumns.REQUEST_RESULT + "=?",
                new String[]{String.valueOf(REQUEST_RESULT_NOT_FOUND)},
                null);

        if (cursor == null) return list;

        while (cursor.moveToNext()) {
            list.add(cursor.getInt(cursor.getColumnIndex(EabProvider.EabCommonColumns._ID)));
        }
        cursor.close();

        return list;
    }

    /**
     * Cleanup the entry of common table that can't map to presence or option table
     */
    private void cleanupOrphanedRows() {
        String presenceSelection =
                " (SELECT " + EabProvider.PresenceTupleColumns.EAB_COMMON_ID +
                        " FROM " + EAB_PRESENCE_TUPLE_TABLE_NAME + ") ";
        String optionSelection =
                " (SELECT " + EabProvider.OptionsColumns.EAB_COMMON_ID +
                        " FROM " + EAB_OPTIONS_TABLE_NAME + ") ";

        mContext.getContentResolver().delete(
                EabProvider.COMMON_URI,
                EabProvider.EabCommonColumns._ID + " NOT IN " + presenceSelection +
                        " AND " + EabProvider.EabCommonColumns._ID+ " NOT IN " + optionSelection,
                null);
    }

    private String getStringValue(Cursor cursor, String column) {
        return cursor.getString(cursor.getColumnIndex(column));
    }
+4 −4
Original line number Diff line number Diff line
@@ -83,10 +83,10 @@ public class EabProvider extends ContentProvider {
    private static final String TAG = "EabProvider";
    private static final int DATABASE_VERSION = 2;

    private static final String EAB_CONTACT_TABLE_NAME = "eab_contact";
    private static final String EAB_COMMON_TABLE_NAME = "eab_common";
    private static final String EAB_PRESENCE_TUPLE_TABLE_NAME = "eab_presence";
    private static final String EAB_OPTIONS_TABLE_NAME = "eab_options";
    public static final String EAB_CONTACT_TABLE_NAME = "eab_contact";
    public static final String EAB_COMMON_TABLE_NAME = "eab_common";
    public static final String EAB_PRESENCE_TUPLE_TABLE_NAME = "eab_presence";
    public static final String EAB_OPTIONS_TABLE_NAME = "eab_options";

    private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int URL_CONTACT = 1;
+103 −0
Original line number Diff line number Diff line
@@ -17,13 +17,20 @@
package com.android.ims.rcs.uce.eab;

import static android.telephony.CarrierConfigManager.Ims.KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT;
import static android.telephony.ims.RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE;
import static android.telephony.ims.RcsContactUceCapability.REQUEST_RESULT_FOUND;
import static android.telephony.ims.RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND;
import static android.telephony.ims.RcsContactUceCapability.SOURCE_TYPE_NETWORK;

import static com.android.ims.rcs.uce.eab.EabProvider.COMMON_URI;
import static com.android.ims.rcs.uce.eab.EabProvider.CONTACT_URI;
import static com.android.ims.rcs.uce.eab.EabProvider.OPTIONS_URI;
import static com.android.ims.rcs.uce.eab.EabProvider.PRESENCE_URI;

import static org.junit.Assert.fail;

import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Looper;
import android.os.PersistableBundle;
@@ -48,12 +55,16 @@ import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@RunWith(AndroidJUnit4.class)
public class EabControllerTest extends ImsTestBase {
    EabProviderTestable mEabProviderTestable = new EabProviderTestable();
    EabControllerImpl mEabController;
    PersistableBundle mBundle;
    ExecutorService mExecutor = Executors.newSingleThreadExecutor();

    private static final int TEST_SUB_ID = 1;
    private static final String TEST_PHONE_NUMBER = "16661234567";
@@ -64,6 +75,7 @@ public class EabControllerTest extends ImsTestBase {
    private static final boolean TEST_AUDIO_CAPABLE = true;
    private static final boolean TEST_VIDEO_CAPABLE = false;

    private static final int TIME_OUT_IN_SEC = 5;
    private static final Uri TEST_CONTACT_URI = Uri.parse(TEST_PHONE_NUMBER + "@android.test");

    @Before
@@ -184,6 +196,97 @@ public class EabControllerTest extends ImsTestBase {
                mEabController.getCapabilities(contactUriList).get(0).getStatus());
    }

    @Test
    @SmallTest
    public void testCleanupInvalidDataInCommonTable() throws InterruptedException {
        // Insert invalid data in common table
        ContentValues data = new ContentValues();
        data.put(EabProvider.EabCommonColumns.EAB_CONTACT_ID, -1);
        data.put(EabProvider.EabCommonColumns.MECHANISM, CAPABILITY_MECHANISM_PRESENCE);
        data.put(EabProvider.EabCommonColumns.REQUEST_RESULT, REQUEST_RESULT_FOUND);
        data.put(EabProvider.EabCommonColumns.SUBSCRIPTION_ID, -1);
        mContext.getContentResolver().insert(COMMON_URI, data);

        mExecutor.execute(mEabController.mCapabilityCleanupRunnable);
        mExecutor.awaitTermination(TIME_OUT_IN_SEC, TimeUnit.SECONDS);

        // Verify the entry that cannot map to presence/option table has been removed
        Cursor cursor = mContext.getContentResolver().query(COMMON_URI, null, null, null, null);
        while(cursor.moveToNext()) {
            int contactId = cursor.getInt(
                    cursor.getColumnIndex(EabProvider.EabCommonColumns.EAB_CONTACT_ID));
            if (contactId == -1) {
                fail("Invalid data didn't been cleared");
            }
        }
    }

    @Test
    @SmallTest
    public void testCleanupInvalidDataInPresenceTable() throws InterruptedException {
        String expiredContact = "expiredContact";
        GregorianCalendar expiredDate = new GregorianCalendar();
        expiredDate.setTimeZone(TimeZone.getTimeZone("UTC"));
        expiredDate.add(Calendar.DATE, -120);
        // Insert invalid data in presence table
        ContentValues data = new ContentValues();
        data.put(EabProvider.EabCommonColumns.REQUEST_RESULT, REQUEST_RESULT_FOUND);
        Uri commonUri = mContext.getContentResolver().insert(COMMON_URI, data);

        data = new ContentValues();
        data.put(EabProvider.PresenceTupleColumns.EAB_COMMON_ID, commonUri.getLastPathSegment());
        data.put(EabProvider.PresenceTupleColumns.CONTACT_URI, expiredContact);
        data.put(EabProvider.PresenceTupleColumns.REQUEST_TIMESTAMP,
                expiredDate.getTime().getTime() / 1000);
        mContext.getContentResolver().insert(PRESENCE_URI, data);

        mExecutor.execute(mEabController.mCapabilityCleanupRunnable);
        mExecutor.awaitTermination(TIME_OUT_IN_SEC, TimeUnit.SECONDS);

        // Verify the invalid data has been removed after save capabilities
        Cursor cursor = mContext.getContentResolver().query(PRESENCE_URI, null, null, null, null);
        while(cursor.moveToNext()) {
            String contactUri = cursor.getString(
                    cursor.getColumnIndex(EabProvider.PresenceTupleColumns.CONTACT_URI));
            if (contactUri.equals(expiredContact)) {
                fail("Invalid data didn't been cleared");
            }
        }
    }

    @Test
    @SmallTest
    public void testCleanupInvalidDataInOptionTable() throws InterruptedException {
        String expiredFeatureTag = "expiredFeatureTag";
        GregorianCalendar expiredDate = new GregorianCalendar();
        expiredDate.setTimeZone(TimeZone.getTimeZone("UTC"));
        expiredDate.add(Calendar.DATE, -120);
        // Insert invalid data in presence table
        ContentValues data = new ContentValues();
        data.put(EabProvider.EabCommonColumns.REQUEST_RESULT, REQUEST_RESULT_NOT_FOUND);
        Uri commonUri = mContext.getContentResolver().insert(COMMON_URI, data);

        data = new ContentValues();
        data.put(EabProvider.PresenceTupleColumns.EAB_COMMON_ID, commonUri.getLastPathSegment());
        data.put(EabProvider.OptionsColumns.FEATURE_TAG, expiredFeatureTag);
        data.put(EabProvider.OptionsColumns.REQUEST_TIMESTAMP,
                expiredDate.getTime().getTime() / 1000);
        mContext.getContentResolver().insert(OPTIONS_URI, data);

        mExecutor.execute(mEabController.mCapabilityCleanupRunnable);
        mExecutor.awaitTermination(TIME_OUT_IN_SEC, TimeUnit.SECONDS);

        // Verify the invalid data has been removed after save capabilities
        Cursor cursor = mContext.getContentResolver().query(OPTIONS_URI, null, null, null, null);
        while(cursor.moveToNext()) {
            String featureTag = cursor.getString(
                    cursor.getColumnIndex(EabProvider.OptionsColumns.FEATURE_TAG));
            if (featureTag.equals(expiredFeatureTag)) {
                fail("Invalid data didn't been cleared");
            }
        }
    }

    private RcsContactUceCapability createPresenceCapability(boolean isExpired) {
        String timestamp;
        GregorianCalendar date = new GregorianCalendar();