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

Commit 8a405909 authored by Calvin Pan's avatar Calvin Pan Committed by Gerrit Code Review
Browse files

Merge "Support bulk capability exchange in UCE"

parents ae8d8edc 8013fa42
Loading
Loading
Loading
Loading
+483 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.ims.rcs.uce.eab;

import static android.telephony.ims.RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS;
import static android.telephony.ims.RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE;

import static com.android.ims.rcs.uce.eab.EabControllerImpl.getCapabilityCacheExpiration;

import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.Telephony;
import android.telephony.CarrierConfigManager;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsRcsManager;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.util.Log;

import com.android.ims.rcs.uce.UceController;

import java.util.ArrayList;
import java.util.List;

public final class EabBulkCapabilityUpdater {
    private final String TAG = this.getClass().getSimpleName();

    private static final Uri USER_EAB_SETTING = Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
            Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED);
    private static final int NUM_SECS_IN_DAY = 86400;

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

    private final AlarmManager.OnAlarmListener mCapabilityExpiredListener;
    private final ContactChangedListener mContactProviderListener;
    private final EabSettingsListener mEabSettingListener;
    private final BroadcastReceiver mCarrierConfigChangedListener;
    private final EabControllerImpl mEabControllerImpl;
    private final EabContactSyncController mEabContactSyncController;

    private UceController.UceControllerCallback mUceControllerCallback;
    private List<Uri> mRefreshContactList;

    private boolean mIsContactProviderListenerRegistered = false;
    private boolean mIsEabSettingListenerRegistered = false;
    private boolean mIsCarrierConfigListenerRegistered = false;
    private boolean mIsCarrierConfigEnabled = false;

    /**
     * Listen capability expired intent. Only registered when
     * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} has enabled bulk
     * capability exchange.
     */
    private class CapabilityExpiredListener implements AlarmManager.OnAlarmListener {
        @Override
        public void onAlarm() {
            Log.d(TAG, "Capability expired.");
            try {
                List<Uri> expiredContactList = getExpiredContactList();
                if (expiredContactList.size() > 0) {
                    mUceControllerCallback.refreshCapabilities(
                            getExpiredContactList(),
                            mRcsUceControllerCallback);
                } else {
                    Log.d(TAG, "expiredContactList is empty.");
                }
            } catch (RemoteException e) {
                Log.e(TAG, "CapabilityExpiredListener RemoteException", e);
            }
        }
    }

    /**
     * Listen contact provider change. Only registered when
     * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} has enabled bulk
     * capability exchange.
     */
    private class ContactChangedListener extends ContentObserver {
        public ContactChangedListener(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            Log.d(TAG, "Contact changed");
            syncContactAndRefreshCapabilities();
        }
    }

    /**
     * Listen EAB settings change. Only registered when
     * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} has enabled bulk
     * capability exchange.
     */
    private class EabSettingsListener extends ContentObserver {
        public EabSettingsListener(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            boolean isUserEnableUce = isUserEnableUce();
            Log.d(TAG, "EAB user setting changed: " + isUserEnableUce);
            if (isUserEnableUce) {
                mHandler.post(new SyncContactRunnable());
            } else {
                unRegisterContactProviderListener();
                cancelTimeAlert(mContext);
            }
        }
    }

    /**
     * Listen carrier config changed to prevent this instance created before carrier config loaded.
     */
    private class CarrierConfigChangedListener extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            boolean isSupportBulkCapabilityExchange = getBooleanCarrierConfig(
                CarrierConfigManager.Ims.KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, mSubId);

            Log.d(TAG, "Carrier config changed. "
                    + "isCarrierConfigEnabled: " + mIsCarrierConfigEnabled
                    + ", isSupportBulkCapabilityExchange: " + isSupportBulkCapabilityExchange);
            if (!mIsCarrierConfigEnabled && isSupportBulkCapabilityExchange) {
                enableBulkCapability();
                updateExpiredTimeAlert();
                mIsCarrierConfigEnabled = true;
            } else if (mIsCarrierConfigEnabled && !isSupportBulkCapabilityExchange) {
                onDestroy();
            }
        }
    }

    private IRcsUceControllerCallback mRcsUceControllerCallback = new IRcsUceControllerCallback() {
        @Override
        public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
            Log.d(TAG, "onCapabilitiesReceived");
            mEabControllerImpl.saveCapabilities(contactCapabilities);
        }

        @Override
        public void onComplete() {
            Log.d(TAG, "onComplete");
        }

        @Override
        public void onError(int errorCode, long retryAfterMilliseconds) {
            Log.d(TAG, "Refresh capabilities failed. Error code: " + errorCode
                    + ", retryAfterMilliseconds: " + retryAfterMilliseconds);
            if (retryAfterMilliseconds != 0) {
                mHandler.postDelayed(new retryRunnable(), retryAfterMilliseconds);
            }
        }

        @Override
        public IBinder asBinder() {
            return null;
        }
    };

    private class SyncContactRunnable implements Runnable {
        @Override
        public void run() {
            Log.d(TAG, "Sync contact from contact provider");
            syncContactAndRefreshCapabilities();
            registerContactProviderListener();
            registerEabUserSettingsListener();
        }
    }

    /**
     * Re-refresh capability if error happened.
     */
    private class retryRunnable implements Runnable {
        @Override
        public void run() {
            Log.d(TAG, "Retry refreshCapabilities()");

            try {
                mUceControllerCallback.refreshCapabilities(
                        mRefreshContactList, mRcsUceControllerCallback);
            } catch (RemoteException e) {
                Log.e(TAG, "refreshCapabilities RemoteException" , e);
            }
        }
    }

    public EabBulkCapabilityUpdater(Context context,
            int subId,
            EabControllerImpl eabControllerImpl,
            EabContactSyncController eabContactSyncController,
            UceController.UceControllerCallback uceControllerCallback,
            Looper looper) {
        mContext = context;
        mSubId = subId;
        mEabControllerImpl = eabControllerImpl;
        mEabContactSyncController = eabContactSyncController;
        mUceControllerCallback = uceControllerCallback;

        mHandler = new Handler(looper);
        mContactProviderListener = new ContactChangedListener(mHandler);
        mEabSettingListener = new EabSettingsListener(mHandler);
        mCapabilityExpiredListener = new CapabilityExpiredListener();
        mCarrierConfigChangedListener = new CarrierConfigChangedListener();

        Log.d(TAG, "create EabBulkCapabilityUpdater() subId: " + mSubId);

        enableBulkCapability();
        updateExpiredTimeAlert();
    }

    private void enableBulkCapability() {
        boolean isUserEnableUce = isUserEnableUce();
        boolean isSupportBulkCapabilityExchange = getBooleanCarrierConfig(
                CarrierConfigManager.Ims.KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, mSubId);

        Log.d(TAG, "isUserEnableUce: " + isUserEnableUce
                + ", isSupportBulkCapabilityExchange: " + isSupportBulkCapabilityExchange);

        if (isUserEnableUce && isSupportBulkCapabilityExchange) {
            mHandler.post(new SyncContactRunnable());
            mIsCarrierConfigEnabled = true;
        } else if (!isUserEnableUce && isSupportBulkCapabilityExchange) {
            registerEabUserSettingsListener();
            mIsCarrierConfigEnabled = false;
        } else {
            registerCarrierConfigChanged();
            Log.d(TAG, "Not support bulk capability exchange.");
        }
    }

    private void syncContactAndRefreshCapabilities() {
        mRefreshContactList = mEabContactSyncController.syncContactToEabProvider(mContext);
        Log.d(TAG, "refresh contacts number: " + mRefreshContactList.size());

        if (mUceControllerCallback == null) {
            Log.d(TAG, "mUceControllerCallback is null.");
            return;
        }

        try {
            if (mRefreshContactList.size() > 0) {
                mUceControllerCallback.refreshCapabilities(
                        mRefreshContactList, mRcsUceControllerCallback);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "mUceControllerCallback RemoteException.", e);
        }
    }

    protected void updateExpiredTimeAlert() {
        boolean isUserEnableUce = isUserEnableUce();
        boolean isSupportBulkCapabilityExchange = getBooleanCarrierConfig(
                CarrierConfigManager.Ims.KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, mSubId);

        Log.d(TAG, " updateExpiredTimeAlert(), isUserEnableUce: " + isUserEnableUce
                + ", isSupportBulkCapabilityExchange: " + isSupportBulkCapabilityExchange);

        if (isUserEnableUce && isSupportBulkCapabilityExchange) {
            long expiredTimestamp = getLeastExpiredTimestamp();
            if (expiredTimestamp == Long.MAX_VALUE) {
                Log.d(TAG, "Can't find min timestamp in eab provider");
                return;
            }
            expiredTimestamp += getCapabilityCacheExpiration(mSubId);
            Log.d(TAG, "set time alert at " + expiredTimestamp);
            cancelTimeAlert(mContext);
            setTimeAlert(mContext, expiredTimestamp);
        }
    }

    private long getLeastExpiredTimestamp() {
        String selection = "("
                // Query presence timestamp
                + EabProvider.EabCommonColumns.MECHANISM + "=" + CAPABILITY_MECHANISM_PRESENCE
                + " AND "
                + EabProvider.PresenceTupleColumns.REQUEST_TIMESTAMP + " IS NOT NULL) "

                // Query options timestamp
                + " OR " + "(" + EabProvider.EabCommonColumns.MECHANISM + "="
                + CAPABILITY_MECHANISM_OPTIONS + " AND "
                + EabProvider.OptionsColumns.REQUEST_TIMESTAMP + " IS NOT NULL) "

                // filter by sub id
                + " AND " + EabProvider.EabCommonColumns.SUBSCRIPTION_ID + "=" + mSubId

                // filter the contact that not come from contact provider
                + " AND " + EabProvider.ContactColumns.RAW_CONTACT_ID + " IS NOT NULL "
                + " AND " + EabProvider.ContactColumns.DATA_ID + " IS NOT NULL ";

        long minTimestamp = Long.MAX_VALUE;
        Cursor result = mContext.getContentResolver().query(EabProvider.ALL_DATA_URI, null,
                selection,
                null, null);

        if (result != null) {
            while (result.moveToNext()) {
                int mechanism = result.getInt(
                        result.getColumnIndex(EabProvider.EabCommonColumns.MECHANISM));
                long timestamp;
                if (mechanism == CAPABILITY_MECHANISM_PRESENCE) {
                    timestamp = result.getLong(result.getColumnIndex(
                            EabProvider.PresenceTupleColumns.REQUEST_TIMESTAMP));
                } else {
                    timestamp = result.getLong(result.getColumnIndex(
                            EabProvider.OptionsColumns.REQUEST_TIMESTAMP));
                }

                if (timestamp < minTimestamp) {
                    minTimestamp = timestamp;
                }
            }
            result.close();
        } else {
            Log.d(TAG, "getLeastExpiredTimestamp() cursor is null");
        }
        return minTimestamp;
    }

    private void setTimeAlert(Context context, long wakeupTimeMs) {
        AlarmManager am = context.getSystemService(AlarmManager.class);

        // To prevent all devices from sending requests to the server at the same time, add a jitter
        // time (0 sec ~ 2 days) randomly.
        int jitterTimeSec = (int) (Math.random() * (NUM_SECS_IN_DAY * 2));
        Log.d(TAG, " setTimeAlert: " + wakeupTimeMs + ", jitterTimeSec: " + jitterTimeSec);
        am.set(AlarmManager.RTC_WAKEUP,
                (wakeupTimeMs * 1000) + jitterTimeSec,
                TAG,
                mCapabilityExpiredListener,
                mHandler);
    }

    private void cancelTimeAlert(Context context) {
        Log.d(TAG, "cancelTimeAlert.");
        AlarmManager am = context.getSystemService(AlarmManager.class);
        am.cancel(mCapabilityExpiredListener);
    }

    private boolean getBooleanCarrierConfig(String key, int subId) {
        CarrierConfigManager mConfigManager = mContext.getSystemService(CarrierConfigManager.class);
        PersistableBundle b = null;
        if (mConfigManager != null) {
            b = mConfigManager.getConfigForSubId(subId);
        }
        if (b != null) {
            return b.getBoolean(key);
        } else {
            Log.w(TAG, "getConfigForSubId(subId) is null. Return the default value of " + key);
            return CarrierConfigManager.getDefaultConfig().getBoolean(key);
        }
    }

    private boolean isUserEnableUce() {
        ImsManager manager = mContext.getSystemService(ImsManager.class);
        if (manager == null) {
            Log.e(TAG, "ImsManager is null");
            return false;
        }
        try {
            ImsRcsManager rcsManager = manager.getImsRcsManager(mSubId);
            return (rcsManager != null) && rcsManager.getUceAdapter().isUceSettingEnabled();
        } catch (Exception e) {
            Log.e(TAG, "hasUserEnabledUce: exception = " + e.getMessage());
        }
        return false;
    }

    private List<Uri> getExpiredContactList() {
        List<Uri> refreshList = new ArrayList<>();
        long expiredTime = (System.currentTimeMillis() / 1000)
                + getCapabilityCacheExpiration(mSubId);
        String selection = "("
                + EabProvider.EabCommonColumns.MECHANISM + "=" + CAPABILITY_MECHANISM_PRESENCE
                + " AND " + EabProvider.PresenceTupleColumns.REQUEST_TIMESTAMP + "<"
                + expiredTime + ")";
        selection += " OR " + "(" + EabProvider.EabCommonColumns.MECHANISM + "="
                + CAPABILITY_MECHANISM_OPTIONS + " AND "
                + EabProvider.OptionsColumns.REQUEST_TIMESTAMP + "<" + expiredTime + ")";

        Cursor result = mContext.getContentResolver().query(EabProvider.ALL_DATA_URI, null,
                selection,
                null, null);
        while (result.moveToNext()) {
            String phoneNumber = result.getString(
                    result.getColumnIndex(EabProvider.ContactColumns.PHONE_NUMBER));
            refreshList.add(Uri.parse(phoneNumber));
        }
        result.close();
        return refreshList;
    }

    protected void onDestroy() {
        Log.d(TAG, "onDestroy");
        cancelTimeAlert(mContext);
        unRegisterContactProviderListener();
        unRegisterEabUserSettings();
        unRegisterCarrierConfigChanged();
    }

    private void registerContactProviderListener() {
        Log.d(TAG, "registerContactProviderListener");
        mIsContactProviderListenerRegistered = true;
        mContext.getContentResolver().registerContentObserver(
                ContactsContract.Contacts.CONTENT_URI,
                true,
                mContactProviderListener);
    }

    private void registerEabUserSettingsListener() {
        Log.d(TAG, "registerEabUserSettingsListener");
        mIsEabSettingListenerRegistered = true;
        mContext.getContentResolver().registerContentObserver(
                USER_EAB_SETTING,
                true,
                mEabSettingListener);
    }

    private void registerCarrierConfigChanged() {
        Log.d(TAG, "registerCarrierConfigChanged");
        mIsCarrierConfigListenerRegistered = true;
        IntentFilter FILTER_CARRIER_CONFIG_CHANGED =
                new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
        mContext.registerReceiver(mCarrierConfigChangedListener, FILTER_CARRIER_CONFIG_CHANGED);
    }

    private void unRegisterContactProviderListener() {
        Log.d(TAG, "unRegisterContactProviderListener");
        if (mIsContactProviderListenerRegistered) {
            mIsContactProviderListenerRegistered = false;
            mContext.getContentResolver().unregisterContentObserver(mContactProviderListener);
        }
    }

    private void unRegisterEabUserSettings() {
        Log.d(TAG, "unRegisterEabUserSettings");
        if (mIsEabSettingListenerRegistered) {
            mIsEabSettingListenerRegistered = false;
            mContext.getContentResolver().unregisterContentObserver(mEabSettingListener);
        }
    }

    private void unRegisterCarrierConfigChanged() {
        Log.d(TAG, "unregisterCarrierConfigChanged");
        if (mIsCarrierConfigListenerRegistered) {
            mIsCarrierConfigListenerRegistered = false;
            mContext.unregisterReceiver(mCarrierConfigChangedListener);
        }
    }

    public void setUceRequestCallback(UceController.UceControllerCallback uceControllerCallback) {
        mUceControllerCallback = uceControllerCallback;
    }
}
+332 −0

File added.

Preview size limit exceeded, changes collapsed.

+33 −17
Original line number Diff line number Diff line
@@ -57,15 +57,20 @@ public class EabControllerImpl implements EabController {

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

    private UceControllerCallback mUceControllerCallback;
    private final Looper mLooper;
    private volatile boolean mIsSetDestroyedFlag = false;

    public EabControllerImpl(Context context, int subId, UceControllerCallback c, Looper looper) {
        mContext = context;
        mSubId = subId;
        mUceControllerCallback = c;
        mLooper = looper;
        mEabBulkCapabilityUpdater = new EabBulkCapabilityUpdater(mContext, mSubId,
                this,
                new EabContactSyncController(),
                mUceControllerCallback,
                looper);
    }

    @Override
@@ -80,6 +85,7 @@ public class EabControllerImpl implements EabController {
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        mIsSetDestroyedFlag = true;
        mEabBulkCapabilityUpdater.onDestroy();
    }

    /**
@@ -93,6 +99,7 @@ public class EabControllerImpl implements EabController {
            return;
        }
        mUceControllerCallback = c;
        mEabBulkCapabilityUpdater.setUceRequestCallback(c);
    }

    /**
@@ -148,7 +155,8 @@ public class EabControllerImpl implements EabController {
        for (RcsContactUceCapability capability : contactCapabilities) {
            String phoneNumber = getNumberFromUri(capability.getContactUri());
            Cursor c = mContext.getContentResolver().query(
                    EabProvider.CONTACT_URI, null, EabProvider.ContactColumns.PHONE_NUMBER + "=?",
                    EabProvider.CONTACT_URI, null,
                    EabProvider.ContactColumns.PHONE_NUMBER + "=?",
                    new String[]{phoneNumber}, null);

            if (c != null && c.moveToNext()) {
@@ -176,6 +184,8 @@ public class EabControllerImpl implements EabController {
                c.close();
            }
        }

        mEabBulkCapabilityUpdater.updateExpiredTimeAlert();
    }

    private List<EabCapabilityResult> generateDestroyedResult(List<Uri> contactUri) {
@@ -335,7 +345,7 @@ public class EabControllerImpl implements EabController {
        if (requestTimeStamp != null) {
            Instant expiredTimestamp = Instant
                    .ofEpochSecond(Long.parseLong(requestTimeStamp))
                    .plus(getCapabilityCacheExpiration(), ChronoUnit.SECONDS);
                    .plus(getCapabilityCacheExpiration(mSubId), ChronoUnit.SECONDS);
            expired = expiredTimestamp.isBefore(Instant.now());
            Log.d(TAG, "Capability expiredTimestamp: "
                    + expiredTimestamp.getEpochSecond() + ", expired:" + expired);
@@ -352,7 +362,7 @@ public class EabControllerImpl implements EabController {
        if (requestTimeStamp != null) {
            Instant expiredTimestamp = Instant
                    .ofEpochSecond(Long.parseLong(requestTimeStamp))
                    .plus(getAvailabilityCacheExpiration(), ChronoUnit.SECONDS);
                    .plus(getAvailabilityCacheExpiration(mSubId), ChronoUnit.SECONDS);
            expired = expiredTimestamp.isBefore(Instant.now());
            Log.d(TAG, "Availability insertedTimestamp: "
                    + expiredTimestamp.getEpochSecond() + ", expired:" + expired);
@@ -375,10 +385,10 @@ public class EabControllerImpl implements EabController {
        return expiredTimestamp;
    }

    private long getCapabilityCacheExpiration() {
    protected static long getCapabilityCacheExpiration(int subId) {
        long value = -1;
        try {
            ProvisioningManager pm = ProvisioningManager.createForSubscriptionId(mSubId);
            ProvisioningManager pm = ProvisioningManager.createForSubscriptionId(subId);
            value = pm.getProvisioningIntValue(
                    ProvisioningManager.KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC);
        } catch (Exception ex) {
@@ -392,10 +402,10 @@ public class EabControllerImpl implements EabController {
        return value;
    }

    private long getAvailabilityCacheExpiration() {
    protected static long getAvailabilityCacheExpiration(int subId) {
        long value = -1;
        try {
            ProvisioningManager pm = ProvisioningManager.createForSubscriptionId(mSubId);
            ProvisioningManager pm = ProvisioningManager.createForSubscriptionId(subId);
            value = pm.getProvisioningIntValue(
                    ProvisioningManager.KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC);
        } catch (Exception ex) {
@@ -424,11 +434,14 @@ public class EabControllerImpl implements EabController {
                new String[]{String.valueOf(id)}, null);

        if (c != null && c.getCount() > 0) {
            while(c.moveToNext()) {
                int commonId = c.getInt(c.getColumnIndex(EabProvider.EabCommonColumns._ID));
                mContext.getContentResolver().delete(
                    EabProvider.PRESENCE_URI, EabProvider.PresenceTupleColumns.EAB_COMMON_ID + "=?",
                        EabProvider.PRESENCE_URI,
                        EabProvider.PresenceTupleColumns.EAB_COMMON_ID + "=?",
                        new String[]{String.valueOf(commonId)});
            }
        }

        if (c != null) {
            c.close();
@@ -508,11 +521,14 @@ public class EabControllerImpl implements EabController {
                new String[]{String.valueOf(contactId)}, null);

        if (c != null && c.getCount() > 0) {
            while(c.moveToNext()) {
                int commonId = c.getInt(c.getColumnIndex(EabProvider.EabCommonColumns._ID));
                mContext.getContentResolver().delete(
                    EabProvider.OPTIONS_URI, EabProvider.OptionsColumns.EAB_COMMON_ID + "=?",
                        EabProvider.OPTIONS_URI,
                        EabProvider.OptionsColumns.EAB_COMMON_ID + "=?",
                        new String[]{String.valueOf(commonId)});
            }
        }

        if (c != null) {
            c.close();
+37 −4

File changed.

Preview size limit exceeded, changes collapsed.

+5 −0
Original line number Diff line number Diff line
@@ -18,6 +18,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.ims.tests">

    <!--  For EabBulkCapabilityUpdaterTest, EabBulkCapabilityUpdater will register content
     observer to contact provider but currently there is no better way to mock contact provider
     (registerContentObserver() is final), so require the read_contacts permission to test APK.-->
    <uses-permission android:name="android.permission.READ_CONTACTS"/>

    <application android:label="@string/app_name">
        <uses-library android:name="android.test.runner" />
    </application>
Loading